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