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