Try to fix incorrect non-Latin handling on Win32 filenames.
[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   // check for sample/frame rate sanity
236   if ( ASDCP_SUCCESS(result)
237        && m_ADesc.EditRate != EditRate_24
238        && m_ADesc.EditRate != EditRate_25
239        && m_ADesc.EditRate != EditRate_30
240        && m_ADesc.EditRate != EditRate_48
241        && m_ADesc.EditRate != EditRate_50
242        && m_ADesc.EditRate != EditRate_60
243        && m_ADesc.EditRate != EditRate_96
244        && m_ADesc.EditRate != EditRate_100
245        && m_ADesc.EditRate != EditRate_120
246        && m_ADesc.EditRate != EditRate_23_98 )
247     {
248       DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
249                              m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
250
251       // oh, they gave us the audio sampling rate instead, assume 24/1
252       if ( m_ADesc.EditRate == SampleRate_48k )
253         {
254           DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
255           m_ADesc.EditRate = EditRate_24;
256         }
257       else
258         {
259           // or we just drop the hammer
260           return RESULT_FORMAT;
261         }
262     }
263
264   if( ASDCP_SUCCESS(result) )
265     result = InitMXFIndex();
266
267   if( ASDCP_SUCCESS(result) )
268     result = InitInfo();
269
270   // TODO: test file for sane CBR index BytesPerEditUnit
271
272   return result;
273 }
274
275
276 //
277 //
278 ASDCP::Result_t
279 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
280                                             AESDecContext* Ctx, HMACContext* HMAC)
281 {
282   if ( ! m_File.IsOpen() )
283     return RESULT_INIT;
284
285   assert(m_Dict);
286   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
287 }
288
289 //------------------------------------------------------------------------------------------
290
291
292 //
293 void
294 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
295 {
296   if ( stream == 0 )
297     stream = stderr;
298
299   fprintf(stream, "Frame: %06u, %7u bytes\n",
300           m_FrameNumber, m_Size);
301
302   if ( dump_len )
303     Kumu::hexdump(m_Data, dump_len, stream);
304 }
305
306 //------------------------------------------------------------------------------------------
307
308 ASDCP::PCM::MXFReader::MXFReader()
309 {
310   m_Reader = new h__Reader(DefaultCompositeDict());
311 }
312
313
314 ASDCP::PCM::MXFReader::~MXFReader()
315 {
316   if ( m_Reader && m_Reader->m_File.IsOpen() )
317     m_Reader->Close();
318 }
319
320 // Warning: direct manipulation of MXF structures can interfere
321 // with the normal operation of the wrapper.  Caveat emptor!
322 //
323 ASDCP::MXF::OPAtomHeader&
324 ASDCP::PCM::MXFReader::OPAtomHeader()
325 {
326   if ( m_Reader.empty() )
327     {
328       assert(g_OPAtomHeader);
329       return *g_OPAtomHeader;
330     }
331
332   return m_Reader->m_HeaderPart;
333 }
334
335 // Warning: direct manipulation of MXF structures can interfere
336 // with the normal operation of the wrapper.  Caveat emptor!
337 //
338 ASDCP::MXF::OPAtomIndexFooter&
339 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
340 {
341   if ( m_Reader.empty() )
342     {
343       assert(g_OPAtomIndexFooter);
344       return *g_OPAtomIndexFooter;
345     }
346
347   return m_Reader->m_FooterPart;
348 }
349
350 // Open the file for reading. The file must exist. Returns error if the
351 // operation cannot be completed.
352 ASDCP::Result_t
353 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
354 {
355   return m_Reader->OpenRead(filename);
356 }
357
358 // Reads a frame of essence from the MXF file. If the optional AESEncContext
359 // argument is present, the essence is decrypted after reading. If the MXF
360 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
361 // will contain the ciphertext frame data.
362 ASDCP::Result_t
363 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
364                                  AESDecContext* Ctx, HMACContext* HMAC) const
365 {
366   if ( m_Reader && m_Reader->m_File.IsOpen() )
367     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
368
369   return RESULT_INIT;
370 }
371
372
373 // Fill the struct with the values from the file's header.
374 // Returns RESULT_INIT if the file is not open.
375 ASDCP::Result_t
376 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
377 {
378   if ( m_Reader && m_Reader->m_File.IsOpen() )
379     {
380       ADesc = m_Reader->m_ADesc;
381       return RESULT_OK;
382     }
383
384   return RESULT_INIT;
385 }
386
387 // Fill the struct with the values from the file's header.
388 // Returns RESULT_INIT if the file is not open.
389 ASDCP::Result_t
390 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
391 {
392   if ( m_Reader && m_Reader->m_File.IsOpen() )
393     {
394       Info = m_Reader->m_Info;
395       return RESULT_OK;
396     }
397
398   return RESULT_INIT;
399 }
400
401 //
402 void
403 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
404 {
405   if ( m_Reader && m_Reader->m_File.IsOpen() )
406     m_Reader->m_HeaderPart.Dump(stream);
407 }
408
409
410 //
411 void
412 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
413 {
414   if ( m_Reader->m_File.IsOpen() )
415     m_Reader->m_FooterPart.Dump(stream);
416 }
417
418 //
419 ASDCP::Result_t
420 ASDCP::PCM::MXFReader::Close() const
421 {
422   if ( m_Reader && m_Reader->m_File.IsOpen() )
423     {
424       m_Reader->Close();
425       return RESULT_OK;
426     }
427
428   return RESULT_INIT;
429 }
430
431
432 //------------------------------------------------------------------------------------------
433
434 //
435 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
436 {
437   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
438   h__Writer();
439
440 public:
441   AudioDescriptor m_ADesc;
442   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
443   
444   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
445     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
446   }
447
448   ~h__Writer(){}
449
450   Result_t OpenWrite(const char*, ui32_t HeaderSize);
451   Result_t SetSourceStream(const AudioDescriptor&);
452   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
453   Result_t Finalize();
454 };
455
456
457
458 // Open the file for writing. The file must not exist. Returns error if
459 // the operation cannot be completed.
460 ASDCP::Result_t
461 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
462 {
463   if ( ! m_State.Test_BEGIN() )
464     return RESULT_STATE;
465
466   Result_t result = m_File.OpenWrite(filename);
467
468   if ( ASDCP_SUCCESS(result) )
469     {
470       m_HeaderSize = HeaderSize;
471       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
472       result = m_State.Goto_INIT();
473     }
474
475   return result;
476 }
477
478
479 // Automatically sets the MXF file's metadata from the WAV parser info.
480 ASDCP::Result_t
481 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
482 {
483   if ( ! m_State.Test_INIT() )
484     return RESULT_STATE;
485
486   if ( ADesc.EditRate != EditRate_24
487        && ADesc.EditRate != EditRate_25
488        && ADesc.EditRate != EditRate_30
489        && ADesc.EditRate != EditRate_48
490        && ADesc.EditRate != EditRate_50
491        && ADesc.EditRate != EditRate_60
492        && ADesc.EditRate != EditRate_96
493        && ADesc.EditRate != EditRate_100
494        && ADesc.EditRate != EditRate_120
495        && ADesc.EditRate != EditRate_23_98 )
496     {
497       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
498                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
499       return RESULT_RAW_FORMAT;
500     }
501
502   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
503     {
504       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
505                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
506       return RESULT_RAW_FORMAT;
507     }
508
509   assert(m_Dict);
510   m_ADesc = ADesc;
511
512   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
513   
514   if ( ASDCP_SUCCESS(result) )
515     {
516       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
517       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
518       result = m_State.Goto_READY();
519     }
520
521   if ( ASDCP_SUCCESS(result) )
522     {
523       ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
524       
525       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
526                               SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
527                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
528     }
529
530   return result;
531 }
532
533
534 //
535 //
536 ASDCP::Result_t
537 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
538                                              HMACContext* HMAC)
539 {
540   Result_t result = RESULT_OK;
541
542   if ( m_State.Test_READY() )
543     result = m_State.Goto_RUNNING(); // first time through
544
545   if ( ASDCP_SUCCESS(result) )
546     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
547
548   if ( ASDCP_SUCCESS(result) )
549     m_FramesWritten++;
550
551   return result;
552 }
553
554 // Closes the MXF file, writing the index and other closing information.
555 //
556 ASDCP::Result_t
557 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
558 {
559   if ( ! m_State.Test_RUNNING() )
560     return RESULT_STATE;
561
562   m_State.Goto_FINAL();
563
564   return WriteMXFFooter();
565 }
566
567
568 //------------------------------------------------------------------------------------------
569 //
570
571
572
573 ASDCP::PCM::MXFWriter::MXFWriter()
574 {
575 }
576
577 ASDCP::PCM::MXFWriter::~MXFWriter()
578 {
579 }
580
581 // Warning: direct manipulation of MXF structures can interfere
582 // with the normal operation of the wrapper.  Caveat emptor!
583 //
584 ASDCP::MXF::OPAtomHeader&
585 ASDCP::PCM::MXFWriter::OPAtomHeader()
586 {
587   if ( m_Writer.empty() )
588     {
589       assert(g_OPAtomHeader);
590       return *g_OPAtomHeader;
591     }
592
593   return m_Writer->m_HeaderPart;
594 }
595
596 // Warning: direct manipulation of MXF structures can interfere
597 // with the normal operation of the wrapper.  Caveat emptor!
598 //
599 ASDCP::MXF::OPAtomIndexFooter&
600 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
601 {
602   if ( m_Writer.empty() )
603     {
604       assert(g_OPAtomIndexFooter);
605       return *g_OPAtomIndexFooter;
606     }
607
608   return m_Writer->m_FooterPart;
609 }
610
611 // Open the file for writing. The file must not exist. Returns error if
612 // the operation cannot be completed.
613 ASDCP::Result_t
614 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
615                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
616 {
617   if ( Info.LabelSetType == LS_MXF_SMPTE )
618     m_Writer = new h__Writer(DefaultSMPTEDict());
619   else
620     m_Writer = new h__Writer(DefaultInteropDict());
621
622   m_Writer->m_Info = Info;
623   
624   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
625
626   if ( ASDCP_SUCCESS(result) )
627     result = m_Writer->SetSourceStream(ADesc);
628
629   if ( ASDCP_FAILURE(result) )
630     m_Writer.release();
631
632   return result;
633 }
634
635 // Writes a frame of essence to the MXF file. If the optional AESEncContext
636 // argument is present, the essence is encrypted prior to writing.
637 // Fails if the file is not open, is finalized, or an operating system
638 // error occurs.
639 ASDCP::Result_t
640 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
641 {
642   if ( m_Writer.empty() )
643     return RESULT_INIT;
644
645   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
646 }
647
648 // Closes the MXF file, writing the index and other closing information.
649 ASDCP::Result_t
650 ASDCP::PCM::MXFWriter::Finalize()
651 {
652   if ( m_Writer.empty() )
653     return RESULT_INIT;
654
655   return m_Writer->Finalize();
656 }
657
658 //
659 // end AS_DCP_PCM.cpp
660 //
661