2 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
27 /*! \file AS_02_PCM.cpp
29 \brief AS-02 library, PCM essence reader and writer implementation
32 #include "AS_02_internal.h"
38 //------------------------------------------------------------------------------------------
40 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
41 static std::string SOUND_DEF_LABEL = "Sound Track";
43 //this must be changed because the CBR_frame_size is only
46 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
48 ui32_t CBR_frame_size = 0;
50 if ( Info.EncryptedEssence )
56 + */klv_cryptinfo_size
57 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
58 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
62 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc);
65 return CBR_frame_size;
69 //------------------------------------------------------------------------------------------
72 class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
74 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
78 ASDCP::PCM::AudioDescriptor m_ADesc;
80 h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d) {}
81 virtual ~h__Reader() {}
83 ASDCP::Result_t OpenRead(const char*);
84 ASDCP::Result_t ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
87 Result_t OpenMXFRead(const char* filename);
88 // positions file before reading
89 Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
90 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
92 // reads from current position
93 Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
94 const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
103 AS_02::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
105 Result_t result = OpenMXFRead(filename);
107 if( ASDCP_SUCCESS(result) )
109 InterchangeObject* Object;
110 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
113 result = MD_to_PCM_ADesc((ASDCP::MXF::WaveAudioDescriptor*)Object, m_ADesc);
117 // check for sample/frame rate sanity
118 if ( ASDCP_SUCCESS(result)
119 && m_ADesc.EditRate != EditRate_24
120 && m_ADesc.EditRate != EditRate_25
121 && m_ADesc.EditRate != EditRate_30
122 && m_ADesc.EditRate != EditRate_48
123 && m_ADesc.EditRate != EditRate_50
124 && m_ADesc.EditRate != EditRate_60
125 && m_ADesc.EditRate != EditRate_23_98 )
127 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
128 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
130 // oh, they gave us the audio sampling rate instead, assume 24/1
131 if ( m_ADesc.EditRate == SampleRate_48k )
133 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
134 m_ADesc.EditRate = EditRate_24;
138 // or we just drop the hammer
139 return RESULT_FORMAT;
143 // TODO: test file for sane CBR index BytesPerEditUnit
152 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
153 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
155 if ( ! m_File.IsOpen() )
159 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
163 AS_02::PCM::MXFReader::MXFReader()
165 m_Reader = new h__Reader(DefaultCompositeDict());
169 AS_02::PCM::MXFReader::~MXFReader()
171 if ( m_Reader && m_Reader->m_File.IsOpen() )
175 // Warning: direct manipulation of MXF structures can interfere
176 // with the normal operation of the wrapper. Caveat emptor!
178 ASDCP::MXF::OP1aHeader&
179 AS_02::PCM::MXFReader::OP1aHeader()
181 if ( m_Reader.empty() )
183 assert(g_OP1aHeader);
184 return *g_OP1aHeader;
187 return m_Reader->m_HeaderPart;
190 // Warning: direct manipulation of MXF structures can interfere
191 // with the normal operation of the wrapper. Caveat emptor!
193 AS_02::MXF::AS02IndexReader&
194 AS_02::PCM::MXFReader::AS02IndexReader()
196 if ( m_Reader.empty() )
198 assert(g_AS02IndexReader);
199 return *g_AS02IndexReader;
202 return m_Reader->m_IndexAccess;
205 // Warning: direct manipulation of MXF structures can interfere
206 // with the normal operation of the wrapper. Caveat emptor!
209 AS_02::PCM::MXFReader::RIP()
211 if ( m_Reader.empty() )
217 return m_Reader->m_RIP;
220 // Open the file for reading. The file must exist. Returns error if the
221 // operation cannot be completed.
223 AS_02::PCM::MXFReader::OpenRead(const char* filename) const
225 return m_Reader->OpenRead(filename);
228 // Reads a frame of essence from the MXF file. If the optional AESEncContext
229 // argument is present, the essence is decrypted after reading. If the MXF
230 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
231 // will contain the ciphertext frame data.
233 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
234 ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
236 if ( m_Reader && m_Reader->m_File.IsOpen() )
237 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
243 // Fill the struct with the values from the file's header.
244 // Returns RESULT_INIT if the file is not open.
246 AS_02::PCM::MXFReader::FillAudioDescriptor(ASDCP::PCM::AudioDescriptor& ADesc) const
248 if ( m_Reader && m_Reader->m_File.IsOpen() )
250 ADesc = m_Reader->m_ADesc;
257 // Fill the struct with the values from the file's header.
258 // Returns RESULT_INIT if the file is not open.
260 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
262 if ( m_Reader && m_Reader->m_File.IsOpen() )
264 Info = m_Reader->m_Info;
273 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
275 if ( m_Reader && m_Reader->m_File.IsOpen() )
276 m_Reader->m_HeaderPart.Dump(stream);
282 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
284 if ( m_Reader->m_File.IsOpen() )
285 m_Reader->m_IndexAccess.Dump(stream);
289 //------------------------------------------------------------------------------------------
292 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02Writer
294 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
298 ASDCP::PCM::AudioDescriptor m_ADesc;
299 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
302 h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_KLV_start(0){
303 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
307 virtual ~h__Writer(){}
309 Result_t OpenWrite(const char*, ui32_t HeaderSize);
310 Result_t SetSourceStream(const ASDCP::PCM::AudioDescriptor&);
311 Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
315 //void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
316 // const std::string& TrackName, const UL& EssenceUL,
317 // const UL& DataDefinition, const std::string& PackageLabel);
318 //void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
319 // const std::string& TrackName, const UL& DataDefinition,
320 // const std::string& PackageLabel);
321 //void AddEssenceDescriptor(const UL& WrappingUL);
322 //Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
324 ////new method to create BodyPartition for essence and index
325 //Result_t CreateBodyPartPair();
326 ////new method to finalize BodyPartion(index)
327 //Result_t CompleteIndexBodyPart();
329 // reimplement these functions in AS_02_PCM to support modifications for AS-02
330 Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
331 const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
332 Result_t WriteAS02Footer();
336 // Open the file for writing. The file must not exist. Returns error if
337 // the operation cannot be completed.
339 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
341 if ( ! m_State.Test_BEGIN() )
344 Result_t result = m_File.OpenWrite(filename);
346 if ( ASDCP_SUCCESS(result) )
348 m_HeaderSize = HeaderSize;
349 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
350 result = m_State.Goto_INIT();
357 // Automatically sets the MXF file's metadata from the WAV parser info.
359 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::PCM::AudioDescriptor& ADesc)
361 if ( ! m_State.Test_INIT() )
364 if ( ADesc.EditRate != EditRate_24
365 && ADesc.EditRate != EditRate_25
366 && ADesc.EditRate != EditRate_30
367 && ADesc.EditRate != EditRate_48
368 && ADesc.EditRate != EditRate_50
369 && ADesc.EditRate != EditRate_60
370 && ADesc.EditRate != EditRate_23_98 )
372 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
373 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
374 return RESULT_RAW_FORMAT;
377 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
379 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
380 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
381 return RESULT_RAW_FORMAT;
387 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
389 if ( ASDCP_SUCCESS(result) )
391 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
392 //SMPTE 382M-2007: ByteNo. 15 - 02h - Wave Clip-Wrapped Element
393 m_EssenceUL[SMPTE_UL_LENGTH-2] = 2; // 02h - Wave Clip-Wrapped Element
394 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
395 result = m_State.Goto_READY();
398 if ( ASDCP_SUCCESS(result) )
400 ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98 ) ? 24 : m_ADesc.EditRate.Numerator;
402 result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
403 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
404 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
414 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
417 Result_t result = RESULT_OK;
419 if ( m_State.Test_READY() )
420 result = m_State.Goto_RUNNING(); // first time through
422 if ( ASDCP_SUCCESS(result) )
423 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
425 if ( ASDCP_SUCCESS(result) )
431 // Closes the MXF file, writing the index and other closing information.
434 AS_02::PCM::MXFWriter::h__Writer::Finalize()
436 if ( ! m_State.Test_RUNNING() )
439 m_State.Goto_FINAL();
441 return WriteAS02Footer();
445 //------------------------------------------------------------------------------------------
450 AS_02::PCM::MXFWriter::MXFWriter()
454 AS_02::PCM::MXFWriter::~MXFWriter()
458 // Warning: direct manipulation of MXF structures can interfere
459 // with the normal operation of the wrapper. Caveat emptor!
461 ASDCP::MXF::OP1aHeader&
462 AS_02::PCM::MXFWriter::OP1aHeader()
464 if ( m_Writer.empty() )
466 assert(g_OP1aHeader);
467 return *g_OP1aHeader;
470 return m_Writer->m_HeaderPart;
473 // Warning: direct manipulation of MXF structures can interfere
474 // with the normal operation of the wrapper. Caveat emptor!
477 AS_02::PCM::MXFWriter::RIP()
479 if ( m_Writer.empty() )
485 return m_Writer->m_RIP;
489 // Open the file for writing. The file must not exist. Returns error if
490 // the operation cannot be completed.
492 AS_02::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
493 const ASDCP::PCM::AudioDescriptor& ADesc, ui32_t HeaderSize)
495 m_Writer = new h__Writer(DefaultSMPTEDict());
496 m_Writer->m_Info = Info;
498 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
500 if ( ASDCP_SUCCESS(result) )
501 result = m_Writer->SetSourceStream(ADesc);
503 if ( ASDCP_FAILURE(result) )
509 // Writes a frame of essence to the MXF file. If the optional AESEncContext
510 // argument is present, the essence is encrypted prior to writing.
511 // Fails if the file is not open, is finalized, or an operating system
514 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
516 if ( m_Writer.empty() )
519 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
522 // Closes the MXF file, writing the index and other closing information.
524 AS_02::PCM::MXFWriter::Finalize()
526 if ( m_Writer.empty() )
529 return m_Writer->Finalize();