make way for MCA
[asdcplib.git] / src / AS_DCP_PCM.cpp
1 /*
2 Copyright (c) 2004-2012, 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 const ASDCP::Dictionary *sg_dict = &DefaultSMPTEDict();
40 static MXF::OPAtomHeader sg_OPAtomHeader(sg_dict);
41 static MXF::OPAtomIndexFooter sg_OPAtomIndexFooter(sg_dict);
42
43 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of wave audio";
44 static std::string SOUND_DEF_LABEL = "Sound Track";
45
46 //
47 Result_t
48 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
49 {
50   ASDCP_TEST_NULL(ADescObj);
51   ADescObj->SampleRate = ADesc.EditRate;
52   ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
53   ADescObj->Locked = ADesc.Locked;
54   ADescObj->ChannelCount = ADesc.ChannelCount;
55   ADescObj->QuantizationBits = ADesc.QuantizationBits;
56   ADescObj->BlockAlign = ADesc.BlockAlign;
57   ADescObj->AvgBps = ADesc.AvgBps;
58   ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
59   ADescObj->ContainerDuration = ADesc.ContainerDuration;
60
61   ADescObj->ChannelAssignment.Reset();
62
63   switch ( ADesc.ChannelFormat )
64     {
65       case PCM::CF_CFG_1:
66         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul;
67         break;
68
69       case PCM::CF_CFG_2:
70         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul;
71         break;
72
73       case PCM::CF_CFG_3:
74         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul;
75         break;
76
77       case PCM::CF_CFG_4:
78         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul;
79         break;
80
81       case PCM::CF_CFG_5:
82         ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul;
83         break;
84     }
85
86   return RESULT_OK;
87 }
88
89 //
90 ASDCP::Result_t
91 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.HasValue() )
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
125   return RESULT_OK;
126 }
127
128 //
129 std::ostream&
130 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
131 {
132   strm << "        SampleRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
133   strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
134   strm << "            Locked: " << (unsigned) ADesc.Locked << std::endl;
135   strm << "      ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
136   strm << "  QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
137   strm << "        BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
138   strm << "            AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
139   strm << "     LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
140   strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
141
142   return strm;
143 }
144
145 //
146 void
147 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
148 {
149   if ( stream == 0 )
150     stream = stderr;
151
152   fprintf(stream, "\
153         EditRate: %d/%d\n\
154  AudioSamplingRate: %d/%d\n\
155             Locked: %u\n\
156       ChannelCount: %u\n\
157   QuantizationBits: %u\n\
158         BlockAlign: %u\n\
159             AvgBps: %u\n\
160      LinkedTrackID: %u\n\
161  ContainerDuration: %u\n",
162           ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
163           ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
164           ADesc.Locked,
165           ADesc.ChannelCount,
166           ADesc.QuantizationBits,
167           ADesc.BlockAlign,
168           ADesc.AvgBps,
169           ADesc.LinkedTrackID,
170           ADesc.ContainerDuration
171           );
172 }
173
174
175 //
176 //
177 static ui32_t
178 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
179 {
180   ui32_t CBR_frame_size = 0;
181
182   if ( Info.EncryptedEssence )
183     {
184       CBR_frame_size =
185         SMPTE_UL_LENGTH
186         + MXF_BER_LENGTH
187         + klv_cryptinfo_size
188         + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
189         + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
190     }
191   else
192     {
193       CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
194     }
195
196   return CBR_frame_size;
197 }
198
199
200 //------------------------------------------------------------------------------------------
201
202
203 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
204 {
205   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
206   h__Reader();
207
208 public:
209   AudioDescriptor m_ADesc;
210
211   h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
212   ~h__Reader() {}
213   Result_t    OpenRead(const char*);
214   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
215 };
216
217
218 //
219 //
220 ASDCP::Result_t
221 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
222 {
223   Result_t result = OpenMXFRead(filename);
224
225   if( ASDCP_SUCCESS(result) )
226     {
227       InterchangeObject* Object;
228       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
229         {
230           assert(Object);
231           result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
232         }
233     }
234
235   // check for sample/frame rate sanity
236   if ( ASDCP_SUCCESS(result)
237        && m_ADesc.EditRate != EditRate_24
238        && m_ADesc.EditRate != EditRate_25
239        && m_ADesc.EditRate != EditRate_30
240        && m_ADesc.EditRate != EditRate_48
241        && m_ADesc.EditRate != EditRate_50
242        && m_ADesc.EditRate != EditRate_60
243        && m_ADesc.EditRate != EditRate_96
244        && m_ADesc.EditRate != EditRate_100
245        && m_ADesc.EditRate != EditRate_120
246        && m_ADesc.EditRate != EditRate_23_98 )
247     {
248       DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
249                              m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
250
251       // oh, they gave us the audio sampling rate instead, assume 24/1
252       if ( m_ADesc.EditRate == SampleRate_48k )
253         {
254           DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
255           m_ADesc.EditRate = EditRate_24;
256         }
257       else
258         {
259           // or we just drop the hammer
260           return RESULT_FORMAT;
261         }
262     }
263
264   if( ASDCP_SUCCESS(result) )
265     result = InitMXFIndex();
266
267   if( ASDCP_SUCCESS(result) )
268     result = InitInfo();
269
270   // TODO: test file for sane CBR index BytesPerEditUnit
271
272   return result;
273 }
274
275
276 //
277 //
278 ASDCP::Result_t
279 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
280                                             AESDecContext* Ctx, HMACContext* HMAC)
281 {
282   if ( ! m_File.IsOpen() )
283     return RESULT_INIT;
284
285   assert(m_Dict);
286   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
287 }
288
289 //------------------------------------------------------------------------------------------
290
291
292 //
293 void
294 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
295 {
296   if ( stream == 0 )
297     stream = stderr;
298
299   fprintf(stream, "Frame: %06u, %7u bytes\n",
300           m_FrameNumber, m_Size);
301
302   if ( dump_len )
303     Kumu::hexdump(m_Data, dump_len, stream);
304 }
305
306 //------------------------------------------------------------------------------------------
307
308 ASDCP::PCM::MXFReader::MXFReader()
309 {
310   m_Reader = new h__Reader(DefaultCompositeDict());
311 }
312
313
314 ASDCP::PCM::MXFReader::~MXFReader()
315 {
316   if ( m_Reader && m_Reader->m_File.IsOpen() )
317     m_Reader->Close();
318 }
319
320 // Warning: direct manipulation of MXF structures can interfere
321 // with the normal operation of the wrapper.  Caveat emptor!
322 //
323 ASDCP::MXF::OPAtomHeader&
324 ASDCP::PCM::MXFReader::OPAtomHeader()
325 {
326   if ( m_Reader.empty() )
327     return sg_OPAtomHeader;
328
329   return m_Reader->m_HeaderPart;
330 }
331
332 // Warning: direct manipulation of MXF structures can interfere
333 // with the normal operation of the wrapper.  Caveat emptor!
334 //
335 ASDCP::MXF::OPAtomIndexFooter&
336 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
337 {
338   if ( m_Reader.empty() )
339     return sg_OPAtomIndexFooter;
340
341   return m_Reader->m_FooterPart;
342 }
343
344 // Open the file for reading. The file must exist. Returns error if the
345 // operation cannot be completed.
346 ASDCP::Result_t
347 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
348 {
349   return m_Reader->OpenRead(filename);
350 }
351
352 // Reads a frame of essence from the MXF file. If the optional AESEncContext
353 // argument is present, the essence is decrypted after reading. If the MXF
354 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
355 // will contain the ciphertext frame data.
356 ASDCP::Result_t
357 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
358                                  AESDecContext* Ctx, HMACContext* HMAC) const
359 {
360   if ( m_Reader && m_Reader->m_File.IsOpen() )
361     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
362
363   return RESULT_INIT;
364 }
365
366
367 // Fill the struct with the values from the file's header.
368 // Returns RESULT_INIT if the file is not open.
369 ASDCP::Result_t
370 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
371 {
372   if ( m_Reader && m_Reader->m_File.IsOpen() )
373     {
374       ADesc = m_Reader->m_ADesc;
375       return RESULT_OK;
376     }
377
378   return RESULT_INIT;
379 }
380
381 // Fill the struct with the values from the file's header.
382 // Returns RESULT_INIT if the file is not open.
383 ASDCP::Result_t
384 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
385 {
386   if ( m_Reader && m_Reader->m_File.IsOpen() )
387     {
388       Info = m_Reader->m_Info;
389       return RESULT_OK;
390     }
391
392   return RESULT_INIT;
393 }
394
395 //
396 void
397 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
398 {
399   if ( m_Reader && m_Reader->m_File.IsOpen() )
400     m_Reader->m_HeaderPart.Dump(stream);
401 }
402
403
404 //
405 void
406 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
407 {
408   if ( m_Reader->m_File.IsOpen() )
409     m_Reader->m_FooterPart.Dump(stream);
410 }
411
412 //
413 ASDCP::Result_t
414 ASDCP::PCM::MXFReader::Close() const
415 {
416   if ( m_Reader && m_Reader->m_File.IsOpen() )
417     {
418       m_Reader->Close();
419       return RESULT_OK;
420     }
421
422   return RESULT_INIT;
423 }
424
425
426 //------------------------------------------------------------------------------------------
427
428 //
429 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
430 {
431   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
432   h__Writer();
433
434 public:
435   AudioDescriptor m_ADesc;
436   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
437   
438   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
439     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
440   }
441
442   ~h__Writer(){}
443
444   Result_t OpenWrite(const char*, ui32_t HeaderSize);
445   Result_t SetSourceStream(const AudioDescriptor&);
446   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
447   Result_t Finalize();
448 };
449
450
451
452 // Open the file for writing. The file must not exist. Returns error if
453 // the operation cannot be completed.
454 ASDCP::Result_t
455 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
456 {
457   if ( ! m_State.Test_BEGIN() )
458     return RESULT_STATE;
459
460   Result_t result = m_File.OpenWrite(filename);
461
462   if ( ASDCP_SUCCESS(result) )
463     {
464       m_HeaderSize = HeaderSize;
465       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
466       result = m_State.Goto_INIT();
467     }
468
469   return result;
470 }
471
472
473 // Automatically sets the MXF file's metadata from the WAV parser info.
474 ASDCP::Result_t
475 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
476 {
477   if ( ! m_State.Test_INIT() )
478     return RESULT_STATE;
479
480   if ( ADesc.EditRate != EditRate_24
481        && ADesc.EditRate != EditRate_25
482        && ADesc.EditRate != EditRate_30
483        && ADesc.EditRate != EditRate_48
484        && ADesc.EditRate != EditRate_50
485        && ADesc.EditRate != EditRate_60
486        && ADesc.EditRate != EditRate_96
487        && ADesc.EditRate != EditRate_100
488        && ADesc.EditRate != EditRate_120
489        && ADesc.EditRate != EditRate_23_98 )
490     {
491       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
492                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
493       return RESULT_RAW_FORMAT;
494     }
495
496   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
497     {
498       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
499                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
500       return RESULT_RAW_FORMAT;
501     }
502
503   assert(m_Dict);
504   m_ADesc = ADesc;
505
506   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
507   
508   if ( ASDCP_SUCCESS(result) )
509     {
510       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
511       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
512       result = m_State.Goto_READY();
513     }
514
515   if ( ASDCP_SUCCESS(result) )
516     {
517       ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
518       
519       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
520                               SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
521                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
522     }
523
524   return result;
525 }
526
527
528 //
529 //
530 ASDCP::Result_t
531 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
532                                              HMACContext* HMAC)
533 {
534   Result_t result = RESULT_OK;
535
536   if ( m_State.Test_READY() )
537     result = m_State.Goto_RUNNING(); // first time through
538
539   if ( ASDCP_SUCCESS(result) )
540     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
541
542   if ( ASDCP_SUCCESS(result) )
543     m_FramesWritten++;
544
545   return result;
546 }
547
548 // Closes the MXF file, writing the index and other closing information.
549 //
550 ASDCP::Result_t
551 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
552 {
553   if ( ! m_State.Test_RUNNING() )
554     return RESULT_STATE;
555
556   m_State.Goto_FINAL();
557
558   return WriteMXFFooter();
559 }
560
561
562 //------------------------------------------------------------------------------------------
563 //
564
565
566
567 ASDCP::PCM::MXFWriter::MXFWriter()
568 {
569 }
570
571 ASDCP::PCM::MXFWriter::~MXFWriter()
572 {
573 }
574
575 // Warning: direct manipulation of MXF structures can interfere
576 // with the normal operation of the wrapper.  Caveat emptor!
577 //
578 ASDCP::MXF::OPAtomHeader&
579 ASDCP::PCM::MXFWriter::OPAtomHeader()
580 {
581   if ( m_Writer.empty() )
582     return sg_OPAtomHeader;
583
584   return m_Writer->m_HeaderPart;
585 }
586
587 // Warning: direct manipulation of MXF structures can interfere
588 // with the normal operation of the wrapper.  Caveat emptor!
589 //
590 ASDCP::MXF::OPAtomIndexFooter&
591 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
592 {
593   if ( m_Writer.empty() )
594     return sg_OPAtomIndexFooter;
595
596   return m_Writer->m_FooterPart;
597 }
598
599 // Open the file for writing. The file must not exist. Returns error if
600 // the operation cannot be completed.
601 ASDCP::Result_t
602 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
603                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
604 {
605   if ( Info.LabelSetType == LS_MXF_SMPTE )
606     m_Writer = new h__Writer(DefaultSMPTEDict());
607   else
608     m_Writer = new h__Writer(DefaultInteropDict());
609
610   m_Writer->m_Info = Info;
611   
612   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
613
614   if ( ASDCP_SUCCESS(result) )
615     result = m_Writer->SetSourceStream(ADesc);
616
617   if ( ASDCP_FAILURE(result) )
618     m_Writer.release();
619
620   return result;
621 }
622
623 // Writes a frame of essence to the MXF file. If the optional AESEncContext
624 // argument is present, the essence is encrypted prior to writing.
625 // Fails if the file is not open, is finalized, or an operating system
626 // error occurs.
627 ASDCP::Result_t
628 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
629 {
630   if ( m_Writer.empty() )
631     return RESULT_INIT;
632
633   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
634 }
635
636 // Closes the MXF file, writing the index and other closing information.
637 ASDCP::Result_t
638 ASDCP::PCM::MXFWriter::Finalize()
639 {
640   if ( m_Writer.empty() )
641     return RESULT_INIT;
642
643   return m_Writer->Finalize();
644 }
645
646 //
647 // end AS_DCP_PCM.cpp
648 //
649