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