imf bugs
[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.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-1 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_16
297        && m_ADesc.EditRate != EditRate_18
298        && m_ADesc.EditRate != EditRate_20
299        && m_ADesc.EditRate != EditRate_22
300        && m_ADesc.EditRate != EditRate_23_98 )
301     {
302       DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
303                              m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
304
305       // oh, they gave us the audio sampling rate instead, assume 24/1
306       if ( m_ADesc.EditRate == SampleRate_48k || m_ADesc.EditRate == SampleRate_96k )
307         {
308           DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
309           m_ADesc.EditRate = EditRate_24;
310         }
311       else
312         {
313           DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
314           // or we just drop the hammer
315           return RESULT_FORMAT;
316         }
317     }
318
319   // TODO: test file for sane CBR index BytesPerEditUnit
320
321   return result;
322 }
323
324
325 //
326 //
327 ASDCP::Result_t
328 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
329                                             AESDecContext* Ctx, HMACContext* HMAC)
330 {
331   if ( ! m_File.IsOpen() )
332     return RESULT_INIT;
333
334   if ( (FrameNum+1) > m_ADesc.ContainerDuration )
335     {
336       return RESULT_RANGE;
337     }
338
339   assert(m_Dict);
340   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
341 }
342
343 //------------------------------------------------------------------------------------------
344
345
346 //
347 void
348 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
349 {
350   if ( stream == 0 )
351     stream = stderr;
352
353   fprintf(stream, "Frame: %06u, %7u bytes\n",
354           m_FrameNumber, m_Size);
355
356   if ( dump_len )
357     Kumu::hexdump(m_Data, dump_len, stream);
358 }
359
360 //------------------------------------------------------------------------------------------
361
362 ASDCP::PCM::MXFReader::MXFReader()
363 {
364   m_Reader = new h__Reader(DefaultCompositeDict());
365 }
366
367
368 ASDCP::PCM::MXFReader::~MXFReader()
369 {
370   if ( m_Reader && m_Reader->m_File.IsOpen() )
371     m_Reader->Close();
372 }
373
374 // Warning: direct manipulation of MXF structures can interfere
375 // with the normal operation of the wrapper.  Caveat emptor!
376 //
377 ASDCP::MXF::OP1aHeader&
378 ASDCP::PCM::MXFReader::OP1aHeader()
379 {
380   if ( m_Reader.empty() )
381     {
382       assert(g_OP1aHeader);
383       return *g_OP1aHeader;
384     }
385
386   return m_Reader->m_HeaderPart;
387 }
388
389 // Warning: direct manipulation of MXF structures can interfere
390 // with the normal operation of the wrapper.  Caveat emptor!
391 //
392 ASDCP::MXF::OPAtomIndexFooter&
393 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
394 {
395   if ( m_Reader.empty() )
396     {
397       assert(g_OPAtomIndexFooter);
398       return *g_OPAtomIndexFooter;
399     }
400
401   return m_Reader->m_IndexAccess;
402 }
403
404 // Warning: direct manipulation of MXF structures can interfere
405 // with the normal operation of the wrapper.  Caveat emptor!
406 //
407 ASDCP::MXF::RIP&
408 ASDCP::PCM::MXFReader::RIP()
409 {
410   if ( m_Reader.empty() )
411     {
412       assert(g_RIP);
413       return *g_RIP;
414     }
415
416   return m_Reader->m_RIP;
417 }
418
419 // Open the file for reading. The file must exist. Returns error if the
420 // operation cannot be completed.
421 ASDCP::Result_t
422 ASDCP::PCM::MXFReader::OpenRead(const std::string& filename) const
423 {
424   return m_Reader->OpenRead(filename);
425 }
426
427 // Reads a frame of essence from the MXF file. If the optional AESEncContext
428 // argument is present, the essence is decrypted after reading. If the MXF
429 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
430 // will contain the ciphertext frame data.
431 ASDCP::Result_t
432 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
433                                  AESDecContext* Ctx, HMACContext* HMAC) const
434 {
435   if ( m_Reader && m_Reader->m_File.IsOpen() )
436     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
437
438   return RESULT_INIT;
439 }
440
441
442 ASDCP::Result_t
443 ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
444 {
445     return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
446 }
447
448 // Fill the struct with the values from the file's header.
449 // Returns RESULT_INIT if the file is not open.
450 ASDCP::Result_t
451 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
452 {
453   if ( m_Reader && m_Reader->m_File.IsOpen() )
454     {
455       ADesc = m_Reader->m_ADesc;
456       return RESULT_OK;
457     }
458
459   return RESULT_INIT;
460 }
461
462 // Fill the struct with the values from the file's header.
463 // Returns RESULT_INIT if the file is not open.
464 ASDCP::Result_t
465 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
466 {
467   if ( m_Reader && m_Reader->m_File.IsOpen() )
468     {
469       Info = m_Reader->m_Info;
470       return RESULT_OK;
471     }
472
473   return RESULT_INIT;
474 }
475
476 //
477 void
478 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
479 {
480   if ( m_Reader && m_Reader->m_File.IsOpen() )
481     m_Reader->m_HeaderPart.Dump(stream);
482 }
483
484
485 //
486 void
487 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
488 {
489   if ( m_Reader->m_File.IsOpen() )
490     m_Reader->m_IndexAccess.Dump(stream);
491 }
492
493 //
494 ASDCP::Result_t
495 ASDCP::PCM::MXFReader::Close() const
496 {
497   if ( m_Reader && m_Reader->m_File.IsOpen() )
498     {
499       m_Reader->Close();
500       return RESULT_OK;
501     }
502
503   return RESULT_INIT;
504 }
505
506
507 //------------------------------------------------------------------------------------------
508
509 //
510 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
511 {
512   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
513   h__Writer();
514
515 public:
516   AudioDescriptor m_ADesc;
517   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
518   
519   h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d) {
520     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
521   }
522
523   virtual ~h__Writer(){}
524
525   Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
526   Result_t SetSourceStream(const AudioDescriptor&);
527   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
528   Result_t Finalize();
529 };
530
531
532
533 // Open the file for writing. The file must not exist. Returns error if
534 // the operation cannot be completed.
535 ASDCP::Result_t
536 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
537 {
538   if ( ! m_State.Test_BEGIN() )
539     return RESULT_STATE;
540
541   Result_t result = m_File.OpenWrite(filename);
542
543   if ( ASDCP_SUCCESS(result) )
544     {
545       m_HeaderSize = HeaderSize;
546       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
547       result = m_State.Goto_INIT();
548     }
549
550   return result;
551 }
552
553
554 // Automatically sets the MXF file's metadata from the WAV parser info.
555 ASDCP::Result_t
556 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
557 {
558   if ( ! m_State.Test_INIT() )
559     return RESULT_STATE;
560
561   if ( ADesc.EditRate != EditRate_24
562        && ADesc.EditRate != EditRate_25
563        && ADesc.EditRate != EditRate_30
564        && ADesc.EditRate != EditRate_48
565        && ADesc.EditRate != EditRate_50
566        && ADesc.EditRate != EditRate_60
567        && ADesc.EditRate != EditRate_96
568        && ADesc.EditRate != EditRate_100
569        && ADesc.EditRate != EditRate_120
570        && ADesc.EditRate != EditRate_16
571        && ADesc.EditRate != EditRate_18
572        && ADesc.EditRate != EditRate_20
573        && ADesc.EditRate != EditRate_22
574        && ADesc.EditRate != EditRate_23_98 )
575     {
576       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
577                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
578       return RESULT_RAW_FORMAT;
579     }
580
581   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
582     {
583       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
584                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
585       return RESULT_RAW_FORMAT;
586     }
587
588   assert(m_Dict);
589   m_ADesc = ADesc;
590
591   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
592   
593   if ( ASDCP_SUCCESS(result) )
594     {
595       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
596       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
597       result = m_State.Goto_READY();
598     }
599
600   if ( ASDCP_SUCCESS(result) )
601     {
602       result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingFrame)),
603                                 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
604                                 m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
605                                 calc_CBR_frame_size(m_Info, m_ADesc));
606     }
607
608   return result;
609 }
610
611
612 //
613 //
614 ASDCP::Result_t
615 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
616                                              HMACContext* HMAC)
617 {
618   Result_t result = RESULT_OK;
619
620   if ( m_State.Test_READY() )
621     result = m_State.Goto_RUNNING(); // first time through
622
623   if ( ASDCP_SUCCESS(result) )
624     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
625
626   if ( ASDCP_SUCCESS(result) )
627     m_FramesWritten++;
628
629   return result;
630 }
631
632 // Closes the MXF file, writing the index and other closing information.
633 //
634 ASDCP::Result_t
635 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
636 {
637   if ( ! m_State.Test_RUNNING() )
638     return RESULT_STATE;
639
640   m_State.Goto_FINAL();
641
642   return WriteASDCPFooter();
643 }
644
645
646 //------------------------------------------------------------------------------------------
647 //
648
649
650
651 ASDCP::PCM::MXFWriter::MXFWriter()
652 {
653 }
654
655 ASDCP::PCM::MXFWriter::~MXFWriter()
656 {
657 }
658
659 // Warning: direct manipulation of MXF structures can interfere
660 // with the normal operation of the wrapper.  Caveat emptor!
661 //
662 ASDCP::MXF::OP1aHeader&
663 ASDCP::PCM::MXFWriter::OP1aHeader()
664 {
665   if ( m_Writer.empty() )
666     {
667       assert(g_OP1aHeader);
668       return *g_OP1aHeader;
669     }
670
671   return m_Writer->m_HeaderPart;
672 }
673
674 // Warning: direct manipulation of MXF structures can interfere
675 // with the normal operation of the wrapper.  Caveat emptor!
676 //
677 ASDCP::MXF::OPAtomIndexFooter&
678 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
679 {
680   if ( m_Writer.empty() )
681     {
682       assert(g_OPAtomIndexFooter);
683       return *g_OPAtomIndexFooter;
684     }
685
686   return m_Writer->m_FooterPart;
687 }
688
689 // Warning: direct manipulation of MXF structures can interfere
690 // with the normal operation of the wrapper.  Caveat emptor!
691 //
692 ASDCP::MXF::RIP&
693 ASDCP::PCM::MXFWriter::RIP()
694 {
695   if ( m_Writer.empty() )
696     {
697       assert(g_RIP);
698       return *g_RIP;
699     }
700
701   return m_Writer->m_RIP;
702 }
703
704 // Open the file for writing. The file must not exist. Returns error if
705 // the operation cannot be completed.
706 ASDCP::Result_t
707 ASDCP::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
708                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
709 {
710   if ( Info.LabelSetType == LS_MXF_SMPTE )
711     m_Writer = new h__Writer(DefaultSMPTEDict());
712   else
713     m_Writer = new h__Writer(DefaultInteropDict());
714
715   m_Writer->m_Info = Info;
716   
717   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
718
719   if ( ASDCP_SUCCESS(result) )
720     result = m_Writer->SetSourceStream(ADesc);
721
722   if ( ASDCP_FAILURE(result) )
723     m_Writer.release();
724
725   return result;
726 }
727
728 // Writes a frame of essence to the MXF file. If the optional AESEncContext
729 // argument is present, the essence is encrypted prior to writing.
730 // Fails if the file is not open, is finalized, or an operating system
731 // error occurs.
732 ASDCP::Result_t
733 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
734 {
735   if ( m_Writer.empty() )
736     return RESULT_INIT;
737
738   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
739 }
740
741 // Closes the MXF file, writing the index and other closing information.
742 ASDCP::Result_t
743 ASDCP::PCM::MXFWriter::Finalize()
744 {
745   if ( m_Writer.empty() )
746     return RESULT_INIT;
747
748   return m_Writer->Finalize();
749 }
750
751 //
752 // end AS_DCP_PCM.cpp
753 //
754