136a3edcbd5d2a7d026edd436d7bd6cf74a607b3
[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
82   return RESULT_OK;
83 }
84
85 //
86 ASDCP::Result_t
87 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
88 {
89   ASDCP_TEST_NULL(ADescObj);
90   ADesc.EditRate = ADescObj->SampleRate;
91   ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
92   ADesc.Locked = ADescObj->Locked;
93   ADesc.ChannelCount = ADescObj->ChannelCount;
94   ADesc.QuantizationBits = ADescObj->QuantizationBits;
95   ADesc.BlockAlign = ADescObj->BlockAlign;
96   ADesc.AvgBps = ADescObj->AvgBps;
97   ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
98   assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
99   ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
100
101   ADesc.ChannelFormat = PCM::CF_NONE;
102
103   if ( ADescObj->ChannelAssignment.HasValue() )
104     {
105       if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul )
106         ADesc.ChannelFormat = PCM::CF_CFG_1;
107
108       else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul )
109         ADesc.ChannelFormat = PCM::CF_CFG_2;
110
111       else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul )
112         ADesc.ChannelFormat = PCM::CF_CFG_3;
113
114       else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul )
115         ADesc.ChannelFormat = PCM::CF_CFG_4;
116
117       else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul )
118         ADesc.ChannelFormat = PCM::CF_CFG_5;
119     }
120
121   return RESULT_OK;
122 }
123
124 //
125 std::ostream&
126 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
127 {
128   strm << "        SampleRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
129   strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
130   strm << "            Locked: " << (unsigned) ADesc.Locked << std::endl;
131   strm << "      ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
132   strm << "  QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
133   strm << "        BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
134   strm << "            AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
135   strm << "     LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
136   strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
137
138   return strm;
139 }
140
141 //
142 void
143 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
144 {
145   if ( stream == 0 )
146     stream = stderr;
147
148   fprintf(stream, "\
149         EditRate: %d/%d\n\
150  AudioSamplingRate: %d/%d\n\
151             Locked: %u\n\
152       ChannelCount: %u\n\
153   QuantizationBits: %u\n\
154         BlockAlign: %u\n\
155             AvgBps: %u\n\
156      LinkedTrackID: %u\n\
157  ContainerDuration: %u\n",
158           ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
159           ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
160           ADesc.Locked,
161           ADesc.ChannelCount,
162           ADesc.QuantizationBits,
163           ADesc.BlockAlign,
164           ADesc.AvgBps,
165           ADesc.LinkedTrackID,
166           ADesc.ContainerDuration
167           );
168 }
169
170
171 //
172 //
173 static ui32_t
174 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
175 {
176   ui32_t CBR_frame_size = 0;
177
178   if ( Info.EncryptedEssence )
179     {
180       CBR_frame_size =
181         SMPTE_UL_LENGTH
182         + MXF_BER_LENGTH
183         + klv_cryptinfo_size
184         + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
185         + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
186     }
187   else
188     {
189       CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
190     }
191
192   return CBR_frame_size;
193 }
194
195
196 //------------------------------------------------------------------------------------------
197
198
199 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
200 {
201   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
202   h__Reader();
203
204 public:
205   AudioDescriptor m_ADesc;
206
207   h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
208   ~h__Reader() {}
209   Result_t    OpenRead(const char*);
210   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
211 };
212
213
214 //
215 //
216 ASDCP::Result_t
217 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
218 {
219   Result_t result = OpenMXFRead(filename);
220
221   if( ASDCP_SUCCESS(result) )
222     {
223       InterchangeObject* Object;
224       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
225         {
226           assert(Object);
227           result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
228         }
229     }
230
231   // check for sample/frame rate sanity
232   if ( ASDCP_SUCCESS(result)
233        && m_ADesc.EditRate != EditRate_24
234        && m_ADesc.EditRate != EditRate_25
235        && m_ADesc.EditRate != EditRate_30
236        && m_ADesc.EditRate != EditRate_48
237        && m_ADesc.EditRate != EditRate_50
238        && m_ADesc.EditRate != EditRate_60
239        && m_ADesc.EditRate != EditRate_96
240        && m_ADesc.EditRate != EditRate_100
241        && m_ADesc.EditRate != EditRate_120
242        && m_ADesc.EditRate != EditRate_23_98 )
243     {
244       DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
245                              m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
246
247       // oh, they gave us the audio sampling rate instead, assume 24/1
248       if ( m_ADesc.EditRate == SampleRate_48k )
249         {
250           DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
251           m_ADesc.EditRate = EditRate_24;
252         }
253       else
254         {
255           // or we just drop the hammer
256           return RESULT_FORMAT;
257         }
258     }
259
260   if( ASDCP_SUCCESS(result) )
261     result = InitMXFIndex();
262
263   if( ASDCP_SUCCESS(result) )
264     result = InitInfo();
265
266   // TODO: test file for sane CBR index BytesPerEditUnit
267
268   return result;
269 }
270
271
272 //
273 //
274 ASDCP::Result_t
275 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
276                                             AESDecContext* Ctx, HMACContext* HMAC)
277 {
278   if ( ! m_File.IsOpen() )
279     return RESULT_INIT;
280
281   assert(m_Dict);
282   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
283 }
284
285 //------------------------------------------------------------------------------------------
286
287
288 //
289 void
290 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
291 {
292   if ( stream == 0 )
293     stream = stderr;
294
295   fprintf(stream, "Frame: %06u, %7u bytes\n",
296           m_FrameNumber, m_Size);
297
298   if ( dump_len )
299     Kumu::hexdump(m_Data, dump_len, stream);
300 }
301
302 //------------------------------------------------------------------------------------------
303
304 ASDCP::PCM::MXFReader::MXFReader()
305 {
306   m_Reader = new h__Reader(DefaultCompositeDict());
307 }
308
309
310 ASDCP::PCM::MXFReader::~MXFReader()
311 {
312   if ( m_Reader && m_Reader->m_File.IsOpen() )
313     m_Reader->Close();
314 }
315
316 // Warning: direct manipulation of MXF structures can interfere
317 // with the normal operation of the wrapper.  Caveat emptor!
318 //
319 ASDCP::MXF::OPAtomHeader&
320 ASDCP::PCM::MXFReader::OPAtomHeader()
321 {
322   if ( m_Reader.empty() )
323     {
324       assert(g_OPAtomHeader);
325       return *g_OPAtomHeader;
326     }
327
328   return m_Reader->m_HeaderPart;
329 }
330
331 // Warning: direct manipulation of MXF structures can interfere
332 // with the normal operation of the wrapper.  Caveat emptor!
333 //
334 ASDCP::MXF::OPAtomIndexFooter&
335 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
336 {
337   if ( m_Reader.empty() )
338     {
339       assert(g_OPAtomIndexFooter);
340       return *g_OPAtomIndexFooter;
341     }
342
343   return m_Reader->m_FooterPart;
344 }
345
346 // Open the file for reading. The file must exist. Returns error if the
347 // operation cannot be completed.
348 ASDCP::Result_t
349 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
350 {
351   return m_Reader->OpenRead(filename);
352 }
353
354 // Reads a frame of essence from the MXF file. If the optional AESEncContext
355 // argument is present, the essence is decrypted after reading. If the MXF
356 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
357 // will contain the ciphertext frame data.
358 ASDCP::Result_t
359 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
360                                  AESDecContext* Ctx, HMACContext* HMAC) const
361 {
362   if ( m_Reader && m_Reader->m_File.IsOpen() )
363     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
364
365   return RESULT_INIT;
366 }
367
368
369 // Fill the struct with the values from the file's header.
370 // Returns RESULT_INIT if the file is not open.
371 ASDCP::Result_t
372 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
373 {
374   if ( m_Reader && m_Reader->m_File.IsOpen() )
375     {
376       ADesc = m_Reader->m_ADesc;
377       return RESULT_OK;
378     }
379
380   return RESULT_INIT;
381 }
382
383 // Fill the struct with the values from the file's header.
384 // Returns RESULT_INIT if the file is not open.
385 ASDCP::Result_t
386 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
387 {
388   if ( m_Reader && m_Reader->m_File.IsOpen() )
389     {
390       Info = m_Reader->m_Info;
391       return RESULT_OK;
392     }
393
394   return RESULT_INIT;
395 }
396
397 //
398 void
399 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
400 {
401   if ( m_Reader && m_Reader->m_File.IsOpen() )
402     m_Reader->m_HeaderPart.Dump(stream);
403 }
404
405
406 //
407 void
408 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
409 {
410   if ( m_Reader->m_File.IsOpen() )
411     m_Reader->m_FooterPart.Dump(stream);
412 }
413
414 //
415 ASDCP::Result_t
416 ASDCP::PCM::MXFReader::Close() const
417 {
418   if ( m_Reader && m_Reader->m_File.IsOpen() )
419     {
420       m_Reader->Close();
421       return RESULT_OK;
422     }
423
424   return RESULT_INIT;
425 }
426
427
428 //------------------------------------------------------------------------------------------
429
430 //
431 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
432 {
433   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
434   h__Writer();
435
436 public:
437   AudioDescriptor m_ADesc;
438   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
439   
440   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
441     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
442   }
443
444   ~h__Writer(){}
445
446   Result_t OpenWrite(const char*, ui32_t HeaderSize);
447   Result_t SetSourceStream(const AudioDescriptor&);
448   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
449   Result_t Finalize();
450 };
451
452
453
454 // Open the file for writing. The file must not exist. Returns error if
455 // the operation cannot be completed.
456 ASDCP::Result_t
457 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
458 {
459   if ( ! m_State.Test_BEGIN() )
460     return RESULT_STATE;
461
462   Result_t result = m_File.OpenWrite(filename);
463
464   if ( ASDCP_SUCCESS(result) )
465     {
466       m_HeaderSize = HeaderSize;
467       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
468       result = m_State.Goto_INIT();
469     }
470
471   return result;
472 }
473
474
475 // Automatically sets the MXF file's metadata from the WAV parser info.
476 ASDCP::Result_t
477 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
478 {
479   if ( ! m_State.Test_INIT() )
480     return RESULT_STATE;
481
482   if ( ADesc.EditRate != EditRate_24
483        && ADesc.EditRate != EditRate_25
484        && ADesc.EditRate != EditRate_30
485        && ADesc.EditRate != EditRate_48
486        && ADesc.EditRate != EditRate_50
487        && ADesc.EditRate != EditRate_60
488        && ADesc.EditRate != EditRate_96
489        && ADesc.EditRate != EditRate_100
490        && ADesc.EditRate != EditRate_120
491        && ADesc.EditRate != EditRate_23_98 )
492     {
493       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
494                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
495       return RESULT_RAW_FORMAT;
496     }
497
498   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
499     {
500       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
501                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
502       return RESULT_RAW_FORMAT;
503     }
504
505   assert(m_Dict);
506   m_ADesc = ADesc;
507
508   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
509   
510   if ( ASDCP_SUCCESS(result) )
511     {
512       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
513       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
514       result = m_State.Goto_READY();
515     }
516
517   if ( ASDCP_SUCCESS(result) )
518     {
519       ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
520       
521       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
522                               SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
523                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
524     }
525
526   return result;
527 }
528
529
530 //
531 //
532 ASDCP::Result_t
533 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
534                                              HMACContext* HMAC)
535 {
536   Result_t result = RESULT_OK;
537
538   if ( m_State.Test_READY() )
539     result = m_State.Goto_RUNNING(); // first time through
540
541   if ( ASDCP_SUCCESS(result) )
542     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
543
544   if ( ASDCP_SUCCESS(result) )
545     m_FramesWritten++;
546
547   return result;
548 }
549
550 // Closes the MXF file, writing the index and other closing information.
551 //
552 ASDCP::Result_t
553 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
554 {
555   if ( ! m_State.Test_RUNNING() )
556     return RESULT_STATE;
557
558   m_State.Goto_FINAL();
559
560   return WriteMXFFooter();
561 }
562
563
564 //------------------------------------------------------------------------------------------
565 //
566
567
568
569 ASDCP::PCM::MXFWriter::MXFWriter()
570 {
571 }
572
573 ASDCP::PCM::MXFWriter::~MXFWriter()
574 {
575 }
576
577 // Warning: direct manipulation of MXF structures can interfere
578 // with the normal operation of the wrapper.  Caveat emptor!
579 //
580 ASDCP::MXF::OPAtomHeader&
581 ASDCP::PCM::MXFWriter::OPAtomHeader()
582 {
583   if ( m_Writer.empty() )
584     {
585       assert(g_OPAtomHeader);
586       return *g_OPAtomHeader;
587     }
588
589   return m_Writer->m_HeaderPart;
590 }
591
592 // Warning: direct manipulation of MXF structures can interfere
593 // with the normal operation of the wrapper.  Caveat emptor!
594 //
595 ASDCP::MXF::OPAtomIndexFooter&
596 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
597 {
598   if ( m_Writer.empty() )
599     {
600       assert(g_OPAtomIndexFooter);
601       return *g_OPAtomIndexFooter;
602     }
603
604   return m_Writer->m_FooterPart;
605 }
606
607 // Open the file for writing. The file must not exist. Returns error if
608 // the operation cannot be completed.
609 ASDCP::Result_t
610 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
611                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
612 {
613   if ( Info.LabelSetType == LS_MXF_SMPTE )
614     m_Writer = new h__Writer(DefaultSMPTEDict());
615   else
616     m_Writer = new h__Writer(DefaultInteropDict());
617
618   m_Writer->m_Info = Info;
619   
620   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
621
622   if ( ASDCP_SUCCESS(result) )
623     result = m_Writer->SetSourceStream(ADesc);
624
625   if ( ASDCP_FAILURE(result) )
626     m_Writer.release();
627
628   return result;
629 }
630
631 // Writes a frame of essence to the MXF file. If the optional AESEncContext
632 // argument is present, the essence is encrypted prior to writing.
633 // Fails if the file is not open, is finalized, or an operating system
634 // error occurs.
635 ASDCP::Result_t
636 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
637 {
638   if ( m_Writer.empty() )
639     return RESULT_INIT;
640
641   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
642 }
643
644 // Closes the MXF file, writing the index and other closing information.
645 ASDCP::Result_t
646 ASDCP::PCM::MXFWriter::Finalize()
647 {
648   if ( m_Writer.empty() )
649     return RESULT_INIT;
650
651   return m_Writer->Finalize();
652 }
653
654 //
655 // end AS_DCP_PCM.cpp
656 //
657