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