606a6f5d4356fe7a04a90a3762680744c54b7849
[asdcplib.git] / 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$       
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 ASDCP::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 ASDCP::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__ASDCPReader
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__ASDCPReader(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_16
243        && m_ADesc.EditRate != EditRate_18
244        && m_ADesc.EditRate != EditRate_20
245        && m_ADesc.EditRate != EditRate_22
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_16
496        && ADesc.EditRate != EditRate_18
497        && ADesc.EditRate != EditRate_20
498        && ADesc.EditRate != EditRate_22
499        && ADesc.EditRate != EditRate_23_98 )
500     {
501       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
502                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
503       return RESULT_RAW_FORMAT;
504     }
505
506   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
507     {
508       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
509                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
510       return RESULT_RAW_FORMAT;
511     }
512
513   assert(m_Dict);
514   m_ADesc = ADesc;
515
516   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
517   
518   if ( ASDCP_SUCCESS(result) )
519     {
520       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
521       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
522       result = m_State.Goto_READY();
523     }
524
525   if ( ASDCP_SUCCESS(result) )
526     {
527       ui32_t TCFrameRate = m_ADesc.EditRate.Numerator;
528
529       if ( m_ADesc.EditRate == EditRate_23_98  )
530         TCFrameRate = 24;
531       else if ( m_ADesc.EditRate == EditRate_18  )
532         TCFrameRate = 18;
533       else if ( m_ADesc.EditRate == EditRate_22  )
534         TCFrameRate = 22;
535       
536       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
537                               SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
538                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
539     }
540
541   return result;
542 }
543
544
545 //
546 //
547 ASDCP::Result_t
548 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
549                                              HMACContext* HMAC)
550 {
551   Result_t result = RESULT_OK;
552
553   if ( m_State.Test_READY() )
554     result = m_State.Goto_RUNNING(); // first time through
555
556   if ( ASDCP_SUCCESS(result) )
557     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
558
559   if ( ASDCP_SUCCESS(result) )
560     m_FramesWritten++;
561
562   return result;
563 }
564
565 // Closes the MXF file, writing the index and other closing information.
566 //
567 ASDCP::Result_t
568 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
569 {
570   if ( ! m_State.Test_RUNNING() )
571     return RESULT_STATE;
572
573   m_State.Goto_FINAL();
574
575   return WriteMXFFooter();
576 }
577
578
579 //------------------------------------------------------------------------------------------
580 //
581
582
583
584 ASDCP::PCM::MXFWriter::MXFWriter()
585 {
586 }
587
588 ASDCP::PCM::MXFWriter::~MXFWriter()
589 {
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::OPAtomHeader&
596 ASDCP::PCM::MXFWriter::OPAtomHeader()
597 {
598   if ( m_Writer.empty() )
599     {
600       assert(g_OPAtomHeader);
601       return *g_OPAtomHeader;
602     }
603
604   return m_Writer->m_HeaderPart;
605 }
606
607 // Warning: direct manipulation of MXF structures can interfere
608 // with the normal operation of the wrapper.  Caveat emptor!
609 //
610 ASDCP::MXF::OPAtomIndexFooter&
611 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
612 {
613   if ( m_Writer.empty() )
614     {
615       assert(g_OPAtomIndexFooter);
616       return *g_OPAtomIndexFooter;
617     }
618
619   return m_Writer->m_FooterPart;
620 }
621
622 // Open the file for writing. The file must not exist. Returns error if
623 // the operation cannot be completed.
624 ASDCP::Result_t
625 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
626                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
627 {
628   if ( Info.LabelSetType == LS_MXF_SMPTE )
629     m_Writer = new h__Writer(DefaultSMPTEDict());
630   else
631     m_Writer = new h__Writer(DefaultInteropDict());
632
633   m_Writer->m_Info = Info;
634   
635   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
636
637   if ( ASDCP_SUCCESS(result) )
638     result = m_Writer->SetSourceStream(ADesc);
639
640   if ( ASDCP_FAILURE(result) )
641     m_Writer.release();
642
643   return result;
644 }
645
646 // Writes a frame of essence to the MXF file. If the optional AESEncContext
647 // argument is present, the essence is encrypted prior to writing.
648 // Fails if the file is not open, is finalized, or an operating system
649 // error occurs.
650 ASDCP::Result_t
651 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
652 {
653   if ( m_Writer.empty() )
654     return RESULT_INIT;
655
656   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
657 }
658
659 // Closes the MXF file, writing the index and other closing information.
660 ASDCP::Result_t
661 ASDCP::PCM::MXFWriter::Finalize()
662 {
663   if ( m_Writer.empty() )
664     return RESULT_INIT;
665
666   return m_Writer->Finalize();
667 }
668
669 //
670 // end AS_DCP_PCM.cpp
671 //
672