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