2 Copyright (c) 2004-2011, 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_DCP_PCM.cpp
29 \brief AS-DCP library, PCM essence reader and writer implementation
32 #include "AS_DCP_internal.h"
37 //------------------------------------------------------------------------------------------
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";
42 static byte_t SNDFMT_CFG_1_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
43 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x01, 0x00 };
45 static byte_t SNDFMT_CFG_2_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
46 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x02, 0x00 };
48 static byte_t SNDFMT_CFG_3_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
49 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x03, 0x00 };
53 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
55 ASDCP_TEST_NULL(ADescObj);
56 ADescObj->SampleRate = ADesc.EditRate;
57 ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
58 ADescObj->Locked = ADesc.Locked;
59 ADescObj->ChannelCount = ADesc.ChannelCount;
60 ADescObj->QuantizationBits = ADesc.QuantizationBits;
61 ADescObj->BlockAlign = ADesc.BlockAlign;
62 ADescObj->AvgBps = ADesc.AvgBps;
63 ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
64 ADescObj->ContainerDuration = ADesc.ContainerDuration;
66 ADescObj->ChannelAssignment.Reset();
68 switch ( ADesc.ChannelFormat )
71 ADescObj->ChannelAssignment = UL(SNDFMT_CFG_1_UL);
75 ADescObj->ChannelAssignment = UL(SNDFMT_CFG_2_UL);
79 ADescObj->ChannelAssignment = UL(SNDFMT_CFG_3_UL);
88 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
90 ASDCP_TEST_NULL(ADescObj);
91 ADesc.EditRate = ADescObj->SampleRate;
92 ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
93 ADesc.Locked = ADescObj->Locked;
94 ADesc.ChannelCount = ADescObj->ChannelCount;
95 ADesc.QuantizationBits = ADescObj->QuantizationBits;
96 ADesc.BlockAlign = ADescObj->BlockAlign;
97 ADesc.AvgBps = ADescObj->AvgBps;
98 ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
99 assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
100 ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
102 ADesc.ChannelFormat = PCM::CF_NONE;
104 if ( ADescObj->ChannelAssignment.HasValue() )
106 if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_1_UL) )
107 ADesc.ChannelFormat = PCM::CF_CFG_1;
109 else if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_2_UL) )
110 ADesc.ChannelFormat = PCM::CF_CFG_2;
112 else if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_3_UL) )
113 ADesc.ChannelFormat = PCM::CF_CFG_3;
121 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
123 strm << " SampleRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
124 strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
125 strm << " Locked: " << (unsigned) ADesc.Locked << std::endl;
126 strm << " ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
127 strm << " QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
128 strm << " BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
129 strm << " AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
130 strm << " LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
131 strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
138 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
145 AudioSamplingRate: %d/%d\n\
148 QuantizationBits: %u\n\
152 ContainerDuration: %u\n",
153 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
154 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
157 ADesc.QuantizationBits,
161 ADesc.ContainerDuration
169 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
171 ui32_t CBR_frame_size = 0;
173 if ( Info.EncryptedEssence )
179 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
180 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
184 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
187 return CBR_frame_size;
191 //------------------------------------------------------------------------------------------
194 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
196 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
200 AudioDescriptor m_ADesc;
202 h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
204 Result_t OpenRead(const char*);
205 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
212 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
214 Result_t result = OpenMXFRead(filename);
216 if( ASDCP_SUCCESS(result) )
218 InterchangeObject* Object;
219 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
222 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
226 // check for sample/frame rate sanity
227 if ( ASDCP_SUCCESS(result)
228 && m_ADesc.EditRate != EditRate_24
229 && m_ADesc.EditRate != EditRate_25
230 && m_ADesc.EditRate != EditRate_30
231 && m_ADesc.EditRate != EditRate_48
232 && m_ADesc.EditRate != EditRate_50
233 && m_ADesc.EditRate != EditRate_60
234 && m_ADesc.EditRate != EditRate_23_98 )
236 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
237 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
239 // oh, they gave us the audio sampling rate instead, assume 24/1
240 if ( m_ADesc.EditRate == SampleRate_48k )
242 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
243 m_ADesc.EditRate = EditRate_24;
247 // or we just drop the hammer
248 return RESULT_FORMAT;
252 if( ASDCP_SUCCESS(result) )
253 result = InitMXFIndex();
255 if( ASDCP_SUCCESS(result) )
258 // TODO: test file for sane CBR index BytesPerEditUnit
267 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
268 AESDecContext* Ctx, HMACContext* HMAC)
270 if ( ! m_File.IsOpen() )
274 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
277 //------------------------------------------------------------------------------------------
282 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
287 fprintf(stream, "Frame: %06u, %7u bytes\n",
288 m_FrameNumber, m_Size);
291 Kumu::hexdump(m_Data, dump_len, stream);
294 //------------------------------------------------------------------------------------------
296 ASDCP::PCM::MXFReader::MXFReader()
298 m_Reader = new h__Reader(DefaultCompositeDict());
302 ASDCP::PCM::MXFReader::~MXFReader()
304 if ( m_Reader && m_Reader->m_File.IsOpen() )
308 // Open the file for reading. The file must exist. Returns error if the
309 // operation cannot be completed.
311 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
313 return m_Reader->OpenRead(filename);
316 // Reads a frame of essence from the MXF file. If the optional AESEncContext
317 // argument is present, the essence is decrypted after reading. If the MXF
318 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
319 // will contain the ciphertext frame data.
321 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
322 AESDecContext* Ctx, HMACContext* HMAC) const
324 if ( m_Reader && m_Reader->m_File.IsOpen() )
325 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
331 // Fill the struct with the values from the file's header.
332 // Returns RESULT_INIT if the file is not open.
334 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
336 if ( m_Reader && m_Reader->m_File.IsOpen() )
338 ADesc = m_Reader->m_ADesc;
345 // Fill the struct with the values from the file's header.
346 // Returns RESULT_INIT if the file is not open.
348 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
350 if ( m_Reader && m_Reader->m_File.IsOpen() )
352 Info = m_Reader->m_Info;
361 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
363 if ( m_Reader && m_Reader->m_File.IsOpen() )
364 m_Reader->m_HeaderPart.Dump(stream);
370 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
372 if ( m_Reader->m_File.IsOpen() )
373 m_Reader->m_FooterPart.Dump(stream);
378 ASDCP::PCM::MXFReader::Close() const
380 if ( m_Reader && m_Reader->m_File.IsOpen() )
390 //------------------------------------------------------------------------------------------
393 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
395 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
399 AudioDescriptor m_ADesc;
400 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
402 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
403 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
408 Result_t OpenWrite(const char*, ui32_t HeaderSize);
409 Result_t SetSourceStream(const AudioDescriptor&);
410 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
416 // Open the file for writing. The file must not exist. Returns error if
417 // the operation cannot be completed.
419 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
421 if ( ! m_State.Test_BEGIN() )
424 Result_t result = m_File.OpenWrite(filename);
426 if ( ASDCP_SUCCESS(result) )
428 m_HeaderSize = HeaderSize;
429 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
430 result = m_State.Goto_INIT();
437 // Automatically sets the MXF file's metadata from the WAV parser info.
439 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
441 if ( ! m_State.Test_INIT() )
444 if ( ADesc.EditRate != EditRate_24
445 && ADesc.EditRate != EditRate_25
446 && ADesc.EditRate != EditRate_30
447 && ADesc.EditRate != EditRate_48
448 && ADesc.EditRate != EditRate_50
449 && ADesc.EditRate != EditRate_60
450 && ADesc.EditRate != EditRate_23_98 )
452 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
453 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
454 return RESULT_RAW_FORMAT;
457 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
459 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
460 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
461 return RESULT_RAW_FORMAT;
467 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
469 if ( ASDCP_SUCCESS(result) )
471 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
472 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
473 result = m_State.Goto_READY();
476 if ( ASDCP_SUCCESS(result) )
478 ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98 ) ? 24 : m_ADesc.EditRate.Numerator;
480 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
481 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
482 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
492 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
495 Result_t result = RESULT_OK;
497 if ( m_State.Test_READY() )
498 result = m_State.Goto_RUNNING(); // first time through
500 if ( ASDCP_SUCCESS(result) )
501 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
503 if ( ASDCP_SUCCESS(result) )
509 // Closes the MXF file, writing the index and other closing information.
512 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
514 if ( ! m_State.Test_RUNNING() )
517 m_State.Goto_FINAL();
519 return WriteMXFFooter();
523 //------------------------------------------------------------------------------------------
528 ASDCP::PCM::MXFWriter::MXFWriter()
532 ASDCP::PCM::MXFWriter::~MXFWriter()
537 // Open the file for writing. The file must not exist. Returns error if
538 // the operation cannot be completed.
540 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
541 const AudioDescriptor& ADesc, ui32_t HeaderSize)
543 if ( Info.LabelSetType == LS_MXF_SMPTE )
544 m_Writer = new h__Writer(DefaultSMPTEDict());
546 m_Writer = new h__Writer(DefaultInteropDict());
548 m_Writer->m_Info = Info;
550 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
552 if ( ASDCP_SUCCESS(result) )
553 result = m_Writer->SetSourceStream(ADesc);
555 if ( ASDCP_FAILURE(result) )
561 // Writes a frame of essence to the MXF file. If the optional AESEncContext
562 // argument is present, the essence is encrypted prior to writing.
563 // Fails if the file is not open, is finalized, or an operating system
566 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
568 if ( m_Writer.empty() )
571 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
574 // Closes the MXF file, writing the index and other closing information.
576 ASDCP::PCM::MXFWriter::Finalize()
578 if ( m_Writer.empty() )
581 return m_Writer->Finalize();
585 // end AS_DCP_PCM.cpp