385d9035739ae658295d288d885a2c7a9ff00e29
[libdcp.git] / asdcplib / src / AS_DCP_PCM.cpp
1 /*
2 Copyright (c) 2004-2012, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    AS_DCP_PCM.cpp
28     \version $Id: AS_DCP_PCM.cpp,v 1.36 2012/02/07 18:54:25 jhurst Exp $       
29     \brief   AS-DCP library, PCM essence reader and writer implementation
30 */
31
32 #include "AS_DCP_internal.h"
33 #include <map>
34 #include <iostream>
35 #include <iomanip>
36
37 //------------------------------------------------------------------------------------------
38
39 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of wave audio";
40 static std::string SOUND_DEF_LABEL = "Sound Track";
41
42 //
43 Result_t
44 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
45 {
46   ASDCP_TEST_NULL(ADescObj);
47   ADescObj->SampleRate = ADesc.EditRate;
48   ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
49   ADescObj->Locked = ADesc.Locked;
50   ADescObj->ChannelCount = ADesc.ChannelCount;
51   ADescObj->QuantizationBits = ADesc.QuantizationBits;
52   ADescObj->BlockAlign = ADesc.BlockAlign;
53   ADescObj->AvgBps = ADesc.AvgBps;
54   ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
55   ADescObj->ContainerDuration = ADesc.ContainerDuration;
56
57   ADescObj->ChannelAssignment.Reset();
58
59   switch ( ADesc.ChannelFormat )
60     {
61       case PCM::CF_CFG_1:
62         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul;
63         break;
64
65       case PCM::CF_CFG_2:
66         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul;
67         break;
68
69       case PCM::CF_CFG_3:
70         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul;
71         break;
72
73       case PCM::CF_CFG_4:
74         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul;
75         break;
76
77       case PCM::CF_CFG_5:
78         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul;
79         break;
80
81       case PCM::CF_NONE:
82         /* Keep gcc quiet */
83         break;
84     }
85
86   return RESULT_OK;
87 }
88
89 //
90 ASDCP::Result_t
91 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
92 {
93   ASDCP_TEST_NULL(ADescObj);
94   ADesc.EditRate = ADescObj->SampleRate;
95   ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
96   ADesc.Locked = ADescObj->Locked;
97   ADesc.ChannelCount = ADescObj->ChannelCount;
98   ADesc.QuantizationBits = ADescObj->QuantizationBits;
99   ADesc.BlockAlign = ADescObj->BlockAlign;
100   ADesc.AvgBps = ADescObj->AvgBps;
101   ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
102   assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
103   ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
104
105   ADesc.ChannelFormat = PCM::CF_NONE;
106
107   if ( ADescObj->ChannelAssignment.HasValue() )
108     {
109       if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul )
110         ADesc.ChannelFormat = PCM::CF_CFG_1;
111
112       else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul )
113         ADesc.ChannelFormat = PCM::CF_CFG_2;
114
115       else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul )
116         ADesc.ChannelFormat = PCM::CF_CFG_3;
117
118       else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul )
119         ADesc.ChannelFormat = PCM::CF_CFG_4;
120
121       else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul )
122         ADesc.ChannelFormat = PCM::CF_CFG_5;
123     }
124
125   return RESULT_OK;
126 }
127
128 //
129 std::ostream&
130 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
131 {
132   strm << "        SampleRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
133   strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
134   strm << "            Locked: " << (unsigned) ADesc.Locked << std::endl;
135   strm << "      ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
136   strm << "  QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
137   strm << "        BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
138   strm << "            AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
139   strm << "     LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
140   strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
141
142   return strm;
143 }
144
145 //
146 void
147 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
148 {
149   if ( stream == 0 )
150     stream = stderr;
151
152   fprintf(stream, "\
153         EditRate: %d/%d\n\
154  AudioSamplingRate: %d/%d\n\
155             Locked: %u\n\
156       ChannelCount: %u\n\
157   QuantizationBits: %u\n\
158         BlockAlign: %u\n\
159             AvgBps: %u\n\
160      LinkedTrackID: %u\n\
161  ContainerDuration: %u\n",
162           ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
163           ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
164           ADesc.Locked,
165           ADesc.ChannelCount,
166           ADesc.QuantizationBits,
167           ADesc.BlockAlign,
168           ADesc.AvgBps,
169           ADesc.LinkedTrackID,
170           ADesc.ContainerDuration
171           );
172 }
173
174
175 //
176 //
177 static ui32_t
178 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
179 {
180   ui32_t CBR_frame_size = 0;
181
182   if ( Info.EncryptedEssence )
183     {
184       CBR_frame_size =
185         SMPTE_UL_LENGTH
186         + MXF_BER_LENGTH
187         + klv_cryptinfo_size
188         + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
189         + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
190     }
191   else
192     {
193       CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
194     }
195
196   return CBR_frame_size;
197 }
198
199
200 //------------------------------------------------------------------------------------------
201
202
203 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
204 {
205   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
206   h__Reader();
207
208 public:
209   AudioDescriptor m_ADesc;
210
211   h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
212   ~h__Reader() {}
213   Result_t    OpenRead(const char*);
214   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
215 };
216
217
218 //
219 //
220 ASDCP::Result_t
221 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
222 {
223   Result_t result = OpenMXFRead(filename);
224
225   if( ASDCP_SUCCESS(result) )
226     {
227       InterchangeObject* Object;
228       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
229         {
230           assert(Object);
231           result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
232         }
233     }
234
235   /* This check has been removed so that DCP-o-matic can use any edit rate
236      it wants.
237   */
238 #if 0  
239   // check for sample/frame rate sanity
240   if ( ASDCP_SUCCESS(result)
241        && m_ADesc.EditRate != EditRate_24
242        && m_ADesc.EditRate != EditRate_25
243        && m_ADesc.EditRate != EditRate_30
244        && m_ADesc.EditRate != EditRate_48
245        && m_ADesc.EditRate != EditRate_50
246        && m_ADesc.EditRate != EditRate_60
247        && m_ADesc.EditRate != EditRate_96
248        && m_ADesc.EditRate != EditRate_100
249        && m_ADesc.EditRate != EditRate_120
250        && m_ADesc.EditRate != EditRate_23_98 )
251     {
252       DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
253                              m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
254
255       // oh, they gave us the audio sampling rate instead, assume 24/1
256       if ( m_ADesc.EditRate == SampleRate_48k )
257         {
258           DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
259           m_ADesc.EditRate = EditRate_24;
260         }
261       else
262         {
263           // or we just drop the hammer
264           return RESULT_FORMAT;
265         }
266     }
267 #endif  
268
269   if( ASDCP_SUCCESS(result) )
270     result = InitMXFIndex();
271
272   if( ASDCP_SUCCESS(result) )
273     result = InitInfo();
274
275   // TODO: test file for sane CBR index BytesPerEditUnit
276
277   return result;
278 }
279
280
281 //
282 //
283 ASDCP::Result_t
284 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
285                                             AESDecContext* Ctx, HMACContext* HMAC)
286 {
287   if ( ! m_File.IsOpen() )
288     return RESULT_INIT;
289
290   assert(m_Dict);
291   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
292 }
293
294 //------------------------------------------------------------------------------------------
295
296
297 //
298 void
299 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
300 {
301   if ( stream == 0 )
302     stream = stderr;
303
304   fprintf(stream, "Frame: %06u, %7u bytes\n",
305           m_FrameNumber, m_Size);
306
307   if ( dump_len )
308     Kumu::hexdump(m_Data, dump_len, stream);
309 }
310
311 //------------------------------------------------------------------------------------------
312
313 ASDCP::PCM::MXFReader::MXFReader()
314 {
315   m_Reader = new h__Reader(DefaultCompositeDict());
316 }
317
318
319 ASDCP::PCM::MXFReader::~MXFReader()
320 {
321   if ( m_Reader && m_Reader->m_File.IsOpen() )
322     m_Reader->Close();
323 }
324
325 // Warning: direct manipulation of MXF structures can interfere
326 // with the normal operation of the wrapper.  Caveat emptor!
327 //
328 ASDCP::MXF::OPAtomHeader&
329 ASDCP::PCM::MXFReader::OPAtomHeader()
330 {
331   if ( m_Reader.empty() )
332     {
333       assert(g_OPAtomHeader);
334       return *g_OPAtomHeader;
335     }
336
337   return m_Reader->m_HeaderPart;
338 }
339
340 // Warning: direct manipulation of MXF structures can interfere
341 // with the normal operation of the wrapper.  Caveat emptor!
342 //
343 ASDCP::MXF::OPAtomIndexFooter&
344 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
345 {
346   if ( m_Reader.empty() )
347     {
348       assert(g_OPAtomIndexFooter);
349       return *g_OPAtomIndexFooter;
350     }
351
352   return m_Reader->m_FooterPart;
353 }
354
355 // Open the file for reading. The file must exist. Returns error if the
356 // operation cannot be completed.
357 ASDCP::Result_t
358 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
359 {
360   return m_Reader->OpenRead(filename);
361 }
362
363 // Reads a frame of essence from the MXF file. If the optional AESEncContext
364 // argument is present, the essence is decrypted after reading. If the MXF
365 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
366 // will contain the ciphertext frame data.
367 ASDCP::Result_t
368 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
369                                  AESDecContext* Ctx, HMACContext* HMAC) const
370 {
371   if ( m_Reader && m_Reader->m_File.IsOpen() )
372     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
373
374   return RESULT_INIT;
375 }
376
377
378 // Fill the struct with the values from the file's header.
379 // Returns RESULT_INIT if the file is not open.
380 ASDCP::Result_t
381 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
382 {
383   if ( m_Reader && m_Reader->m_File.IsOpen() )
384     {
385       ADesc = m_Reader->m_ADesc;
386       return RESULT_OK;
387     }
388
389   return RESULT_INIT;
390 }
391
392 // Fill the struct with the values from the file's header.
393 // Returns RESULT_INIT if the file is not open.
394 ASDCP::Result_t
395 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
396 {
397   if ( m_Reader && m_Reader->m_File.IsOpen() )
398     {
399       Info = m_Reader->m_Info;
400       return RESULT_OK;
401     }
402
403   return RESULT_INIT;
404 }
405
406 //
407 void
408 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
409 {
410   if ( m_Reader && m_Reader->m_File.IsOpen() )
411     m_Reader->m_HeaderPart.Dump(stream);
412 }
413
414
415 //
416 void
417 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
418 {
419   if ( m_Reader->m_File.IsOpen() )
420     m_Reader->m_FooterPart.Dump(stream);
421 }
422
423 //
424 ASDCP::Result_t
425 ASDCP::PCM::MXFReader::Close() const
426 {
427   if ( m_Reader && m_Reader->m_File.IsOpen() )
428     {
429       m_Reader->Close();
430       return RESULT_OK;
431     }
432
433   return RESULT_INIT;
434 }
435
436
437 //------------------------------------------------------------------------------------------
438
439 //
440 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
441 {
442   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
443   h__Writer();
444
445 public:
446   AudioDescriptor m_ADesc;
447   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
448   
449   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
450     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
451   }
452
453   ~h__Writer(){}
454
455   Result_t OpenWrite(const char*, ui32_t HeaderSize);
456   Result_t SetSourceStream(const AudioDescriptor&);
457   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
458   Result_t Finalize();
459 };
460
461
462
463 // Open the file for writing. The file must not exist. Returns error if
464 // the operation cannot be completed.
465 ASDCP::Result_t
466 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
467 {
468   if ( ! m_State.Test_BEGIN() )
469     return RESULT_STATE;
470
471   Result_t result = m_File.OpenWrite(filename);
472
473   if ( ASDCP_SUCCESS(result) )
474     {
475       m_HeaderSize = HeaderSize;
476       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
477       result = m_State.Goto_INIT();
478     }
479
480   return result;
481 }
482
483
484 // Automatically sets the MXF file's metadata from the WAV parser info.
485 ASDCP::Result_t
486 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
487 {
488   if ( ! m_State.Test_INIT() )
489     return RESULT_STATE;
490
491 #if 0
492   /* This check has been removed so that DCP-o-matic can use anye dit rate
493      it wants.
494   */
495   if ( ADesc.EditRate != EditRate_24
496        && ADesc.EditRate != EditRate_25
497        && ADesc.EditRate != EditRate_30
498        && ADesc.EditRate != EditRate_48
499        && ADesc.EditRate != EditRate_50
500        && ADesc.EditRate != EditRate_60
501        && ADesc.EditRate != EditRate_96
502        && ADesc.EditRate != EditRate_100
503        && ADesc.EditRate != EditRate_120
504        && ADesc.EditRate != EditRate_23_98 )
505     {
506       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
507                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
508       return RESULT_RAW_FORMAT;
509     }
510 #endif  
511
512   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
513     {
514       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
515                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
516       return RESULT_RAW_FORMAT;
517     }
518
519   assert(m_Dict);
520   m_ADesc = ADesc;
521
522   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
523   
524   if ( ASDCP_SUCCESS(result) )
525     {
526       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
527       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
528       result = m_State.Goto_READY();
529     }
530
531   if ( ASDCP_SUCCESS(result) )
532     {
533       ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
534       
535       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
536                               SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
537                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
538     }
539
540   return result;
541 }
542
543
544 //
545 //
546 ASDCP::Result_t
547 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
548                                              HMACContext* HMAC)
549 {
550   Result_t result = RESULT_OK;
551
552   if ( m_State.Test_READY() )
553     result = m_State.Goto_RUNNING(); // first time through
554
555   if ( ASDCP_SUCCESS(result) )
556     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
557
558   if ( ASDCP_SUCCESS(result) )
559     m_FramesWritten++;
560
561   return result;
562 }
563
564 // Closes the MXF file, writing the index and other closing information.
565 //
566 ASDCP::Result_t
567 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
568 {
569   if ( ! m_State.Test_RUNNING() )
570     return RESULT_STATE;
571
572   m_State.Goto_FINAL();
573
574   return WriteMXFFooter();
575 }
576
577
578 //------------------------------------------------------------------------------------------
579 //
580
581
582
583 ASDCP::PCM::MXFWriter::MXFWriter()
584 {
585 }
586
587 ASDCP::PCM::MXFWriter::~MXFWriter()
588 {
589 }
590
591 // Warning: direct manipulation of MXF structures can interfere
592 // with the normal operation of the wrapper.  Caveat emptor!
593 //
594 ASDCP::MXF::OPAtomHeader&
595 ASDCP::PCM::MXFWriter::OPAtomHeader()
596 {
597   if ( m_Writer.empty() )
598     {
599       assert(g_OPAtomHeader);
600       return *g_OPAtomHeader;
601     }
602
603   return m_Writer->m_HeaderPart;
604 }
605
606 // Warning: direct manipulation of MXF structures can interfere
607 // with the normal operation of the wrapper.  Caveat emptor!
608 //
609 ASDCP::MXF::OPAtomIndexFooter&
610 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
611 {
612   if ( m_Writer.empty() )
613     {
614       assert(g_OPAtomIndexFooter);
615       return *g_OPAtomIndexFooter;
616     }
617
618   return m_Writer->m_FooterPart;
619 }
620
621 // Open the file for writing. The file must not exist. Returns error if
622 // the operation cannot be completed.
623 ASDCP::Result_t
624 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
625                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
626 {
627   if ( Info.LabelSetType == LS_MXF_SMPTE )
628     m_Writer = new h__Writer(DefaultSMPTEDict());
629   else
630     m_Writer = new h__Writer(DefaultInteropDict());
631
632   m_Writer->m_Info = Info;
633   
634   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
635
636   if ( ASDCP_SUCCESS(result) )
637     result = m_Writer->SetSourceStream(ADesc);
638
639   if ( ASDCP_FAILURE(result) )
640     m_Writer.release();
641
642   return result;
643 }
644
645 // Writes a frame of essence to the MXF file. If the optional AESEncContext
646 // argument is present, the essence is encrypted prior to writing.
647 // Fails if the file is not open, is finalized, or an operating system
648 // error occurs.
649 ASDCP::Result_t
650 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
651 {
652   if ( m_Writer.empty() )
653     return RESULT_INIT;
654
655   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
656 }
657
658 // Closes the MXF file, writing the index and other closing information.
659 ASDCP::Result_t
660 ASDCP::PCM::MXFWriter::Finalize()
661 {
662   if ( m_Writer.empty() )
663     return RESULT_INIT;
664
665   return m_Writer->Finalize();
666 }
667
668 //
669 // end AS_DCP_PCM.cpp
670 //
671