Add another OpenReadFrame method for JP2K::CodestreamParser.
[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   // check for sample/frame rate sanity
289   if ( ASDCP_SUCCESS(result)
290        && m_ADesc.EditRate != EditRate_24
291        && m_ADesc.EditRate != EditRate_25
292        && m_ADesc.EditRate != EditRate_30
293        && m_ADesc.EditRate != EditRate_48
294        && m_ADesc.EditRate != EditRate_50
295        && m_ADesc.EditRate != EditRate_60
296        && m_ADesc.EditRate != EditRate_96
297        && m_ADesc.EditRate != EditRate_100
298        && m_ADesc.EditRate != EditRate_120
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_16
574        && ADesc.EditRate != EditRate_18
575        && ADesc.EditRate != EditRate_20
576        && ADesc.EditRate != EditRate_22
577        && ADesc.EditRate != EditRate_23_98 )
578     {
579       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
580                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
581       return RESULT_RAW_FORMAT;
582     }
583
584   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
585     {
586       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
587                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
588       return RESULT_RAW_FORMAT;
589     }
590
591   assert(m_Dict);
592   m_ADesc = ADesc;
593
594   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
595   
596   if ( ASDCP_SUCCESS(result) )
597     {
598       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
599       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
600       result = m_State.Goto_READY();
601     }
602
603   if ( ASDCP_SUCCESS(result) )
604     {
605       result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingFrame)),
606                                 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
607                                 m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
608                                 calc_CBR_frame_size(m_Info, m_ADesc));
609     }
610
611   return result;
612 }
613
614
615 //
616 //
617 ASDCP::Result_t
618 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
619                                              HMACContext* HMAC)
620 {
621   Result_t result = RESULT_OK;
622
623   if ( m_State.Test_READY() )
624     result = m_State.Goto_RUNNING(); // first time through
625
626   if ( ASDCP_SUCCESS(result) )
627     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
628
629   if ( ASDCP_SUCCESS(result) )
630     m_FramesWritten++;
631
632   return result;
633 }
634
635 // Closes the MXF file, writing the index and other closing information.
636 //
637 ASDCP::Result_t
638 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
639 {
640   if ( ! m_State.Test_RUNNING() )
641     return RESULT_STATE;
642
643   m_State.Goto_FINAL();
644
645   return WriteASDCPFooter();
646 }
647
648
649 //------------------------------------------------------------------------------------------
650 //
651
652
653
654 ASDCP::PCM::MXFWriter::MXFWriter()
655 {
656 }
657
658 ASDCP::PCM::MXFWriter::~MXFWriter()
659 {
660 }
661
662 // Warning: direct manipulation of MXF structures can interfere
663 // with the normal operation of the wrapper.  Caveat emptor!
664 //
665 ASDCP::MXF::OP1aHeader&
666 ASDCP::PCM::MXFWriter::OP1aHeader()
667 {
668   if ( m_Writer.empty() )
669     {
670       assert(g_OP1aHeader);
671       return *g_OP1aHeader;
672     }
673
674   return m_Writer->m_HeaderPart;
675 }
676
677 // Warning: direct manipulation of MXF structures can interfere
678 // with the normal operation of the wrapper.  Caveat emptor!
679 //
680 ASDCP::MXF::OPAtomIndexFooter&
681 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
682 {
683   if ( m_Writer.empty() )
684     {
685       assert(g_OPAtomIndexFooter);
686       return *g_OPAtomIndexFooter;
687     }
688
689   return m_Writer->m_FooterPart;
690 }
691
692 // Warning: direct manipulation of MXF structures can interfere
693 // with the normal operation of the wrapper.  Caveat emptor!
694 //
695 ASDCP::MXF::RIP&
696 ASDCP::PCM::MXFWriter::RIP()
697 {
698   if ( m_Writer.empty() )
699     {
700       assert(g_RIP);
701       return *g_RIP;
702     }
703
704   return m_Writer->m_RIP;
705 }
706
707 // Open the file for writing. The file must not exist. Returns error if
708 // the operation cannot be completed.
709 ASDCP::Result_t
710 ASDCP::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
711                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
712 {
713   if ( Info.LabelSetType == LS_MXF_SMPTE )
714     m_Writer = new h__Writer(DefaultSMPTEDict());
715   else
716     m_Writer = new h__Writer(DefaultInteropDict());
717
718   m_Writer->m_Info = Info;
719   
720   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
721
722   if ( ASDCP_SUCCESS(result) )
723     result = m_Writer->SetSourceStream(ADesc);
724
725   if ( ASDCP_FAILURE(result) )
726     m_Writer.release();
727
728   return result;
729 }
730
731 // Writes a frame of essence to the MXF file. If the optional AESEncContext
732 // argument is present, the essence is encrypted prior to writing.
733 // Fails if the file is not open, is finalized, or an operating system
734 // error occurs.
735 ASDCP::Result_t
736 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
737 {
738   if ( m_Writer.empty() )
739     return RESULT_INIT;
740
741   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
742 }
743
744 // Closes the MXF file, writing the index and other closing information.
745 ASDCP::Result_t
746 ASDCP::PCM::MXFWriter::Finalize()
747 {
748   if ( m_Writer.empty() )
749     return RESULT_INIT;
750
751   return m_Writer->Finalize();
752 }
753
754 //
755 // end AS_DCP_PCM.cpp
756 //
757