2 Copyright (c) 2004-2012, 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
28 \version $Id: AS_DCP_PCM.cpp,v 1.36 2012/02/07 18:54:25 jhurst Exp $
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";
44 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
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;
57 ADescObj->ChannelAssignment.Reset();
59 switch ( ADesc.ChannelFormat )
62 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul;
66 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul;
70 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul;
74 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul;
78 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul;
87 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
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;
101 ADesc.ChannelFormat = PCM::CF_NONE;
103 if ( ADescObj->ChannelAssignment.HasValue() )
105 if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul )
106 ADesc.ChannelFormat = PCM::CF_CFG_1;
108 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul )
109 ADesc.ChannelFormat = PCM::CF_CFG_2;
111 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul )
112 ADesc.ChannelFormat = PCM::CF_CFG_3;
114 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul )
115 ADesc.ChannelFormat = PCM::CF_CFG_4;
117 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul )
118 ADesc.ChannelFormat = PCM::CF_CFG_5;
126 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
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;
143 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
150 AudioSamplingRate: %d/%d\n\
153 QuantizationBits: %u\n\
157 ContainerDuration: %u\n",
158 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
159 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
162 ADesc.QuantizationBits,
166 ADesc.ContainerDuration
174 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
176 ui32_t CBR_frame_size = 0;
178 if ( Info.EncryptedEssence )
184 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
185 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
189 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
192 return CBR_frame_size;
196 //------------------------------------------------------------------------------------------
199 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
201 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
205 AudioDescriptor m_ADesc;
207 h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
209 Result_t OpenRead(const char*);
210 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
217 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
219 Result_t result = OpenMXFRead(filename);
221 if( ASDCP_SUCCESS(result) )
223 InterchangeObject* Object;
224 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
227 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
231 // check for sample/frame rate sanity
232 if ( ASDCP_SUCCESS(result)
233 && m_ADesc.EditRate != EditRate_24
234 && m_ADesc.EditRate != EditRate_25
235 && m_ADesc.EditRate != EditRate_30
236 && m_ADesc.EditRate != EditRate_48
237 && m_ADesc.EditRate != EditRate_50
238 && m_ADesc.EditRate != EditRate_60
239 && m_ADesc.EditRate != EditRate_96
240 && m_ADesc.EditRate != EditRate_100
241 && m_ADesc.EditRate != EditRate_120
242 && m_ADesc.EditRate != EditRate_23_98 )
244 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
245 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
247 // oh, they gave us the audio sampling rate instead, assume 24/1
248 if ( m_ADesc.EditRate == SampleRate_48k )
250 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
251 m_ADesc.EditRate = EditRate_24;
255 // or we just drop the hammer
256 return RESULT_FORMAT;
260 if( ASDCP_SUCCESS(result) )
261 result = InitMXFIndex();
263 if( ASDCP_SUCCESS(result) )
266 // TODO: test file for sane CBR index BytesPerEditUnit
275 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
276 AESDecContext* Ctx, HMACContext* HMAC)
278 if ( ! m_File.IsOpen() )
282 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
285 //------------------------------------------------------------------------------------------
290 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
295 fprintf(stream, "Frame: %06u, %7u bytes\n",
296 m_FrameNumber, m_Size);
299 Kumu::hexdump(m_Data, dump_len, stream);
302 //------------------------------------------------------------------------------------------
304 ASDCP::PCM::MXFReader::MXFReader()
306 m_Reader = new h__Reader(DefaultCompositeDict());
310 ASDCP::PCM::MXFReader::~MXFReader()
312 if ( m_Reader && m_Reader->m_File.IsOpen() )
316 // Warning: direct manipulation of MXF structures can interfere
317 // with the normal operation of the wrapper. Caveat emptor!
319 ASDCP::MXF::OPAtomHeader&
320 ASDCP::PCM::MXFReader::OPAtomHeader()
322 if ( m_Reader.empty() )
324 assert(g_OPAtomHeader);
325 return *g_OPAtomHeader;
328 return m_Reader->m_HeaderPart;
331 // Warning: direct manipulation of MXF structures can interfere
332 // with the normal operation of the wrapper. Caveat emptor!
334 ASDCP::MXF::OPAtomIndexFooter&
335 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
337 if ( m_Reader.empty() )
339 assert(g_OPAtomIndexFooter);
340 return *g_OPAtomIndexFooter;
343 return m_Reader->m_FooterPart;
346 // Open the file for reading. The file must exist. Returns error if the
347 // operation cannot be completed.
349 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
351 return m_Reader->OpenRead(filename);
354 // Reads a frame of essence from the MXF file. If the optional AESEncContext
355 // argument is present, the essence is decrypted after reading. If the MXF
356 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
357 // will contain the ciphertext frame data.
359 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
360 AESDecContext* Ctx, HMACContext* HMAC) const
362 if ( m_Reader && m_Reader->m_File.IsOpen() )
363 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
369 // Fill the struct with the values from the file's header.
370 // Returns RESULT_INIT if the file is not open.
372 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
374 if ( m_Reader && m_Reader->m_File.IsOpen() )
376 ADesc = m_Reader->m_ADesc;
383 // Fill the struct with the values from the file's header.
384 // Returns RESULT_INIT if the file is not open.
386 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
388 if ( m_Reader && m_Reader->m_File.IsOpen() )
390 Info = m_Reader->m_Info;
399 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
401 if ( m_Reader && m_Reader->m_File.IsOpen() )
402 m_Reader->m_HeaderPart.Dump(stream);
408 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
410 if ( m_Reader->m_File.IsOpen() )
411 m_Reader->m_FooterPart.Dump(stream);
416 ASDCP::PCM::MXFReader::Close() const
418 if ( m_Reader && m_Reader->m_File.IsOpen() )
428 //------------------------------------------------------------------------------------------
431 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
433 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
437 AudioDescriptor m_ADesc;
438 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
440 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
441 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
446 Result_t OpenWrite(const char*, ui32_t HeaderSize);
447 Result_t SetSourceStream(const AudioDescriptor&);
448 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
454 // Open the file for writing. The file must not exist. Returns error if
455 // the operation cannot be completed.
457 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
459 if ( ! m_State.Test_BEGIN() )
462 Result_t result = m_File.OpenWrite(filename);
464 if ( ASDCP_SUCCESS(result) )
466 m_HeaderSize = HeaderSize;
467 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
468 result = m_State.Goto_INIT();
475 // Automatically sets the MXF file's metadata from the WAV parser info.
477 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
479 if ( ! m_State.Test_INIT() )
482 if ( ADesc.EditRate != EditRate_24
483 && ADesc.EditRate != EditRate_25
484 && ADesc.EditRate != EditRate_30
485 && ADesc.EditRate != EditRate_48
486 && ADesc.EditRate != EditRate_50
487 && ADesc.EditRate != EditRate_60
488 && ADesc.EditRate != EditRate_96
489 && ADesc.EditRate != EditRate_100
490 && ADesc.EditRate != EditRate_120
491 && ADesc.EditRate != EditRate_23_98 )
493 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
494 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
495 return RESULT_RAW_FORMAT;
498 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
500 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
501 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
502 return RESULT_RAW_FORMAT;
508 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
510 if ( ASDCP_SUCCESS(result) )
512 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
513 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
514 result = m_State.Goto_READY();
517 if ( ASDCP_SUCCESS(result) )
519 ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98 ) ? 24 : m_ADesc.EditRate.Numerator;
521 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
522 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
523 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
533 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
536 Result_t result = RESULT_OK;
538 if ( m_State.Test_READY() )
539 result = m_State.Goto_RUNNING(); // first time through
541 if ( ASDCP_SUCCESS(result) )
542 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
544 if ( ASDCP_SUCCESS(result) )
550 // Closes the MXF file, writing the index and other closing information.
553 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
555 if ( ! m_State.Test_RUNNING() )
558 m_State.Goto_FINAL();
560 return WriteMXFFooter();
564 //------------------------------------------------------------------------------------------
569 ASDCP::PCM::MXFWriter::MXFWriter()
573 ASDCP::PCM::MXFWriter::~MXFWriter()
577 // Warning: direct manipulation of MXF structures can interfere
578 // with the normal operation of the wrapper. Caveat emptor!
580 ASDCP::MXF::OPAtomHeader&
581 ASDCP::PCM::MXFWriter::OPAtomHeader()
583 if ( m_Writer.empty() )
585 assert(g_OPAtomHeader);
586 return *g_OPAtomHeader;
589 return m_Writer->m_HeaderPart;
592 // Warning: direct manipulation of MXF structures can interfere
593 // with the normal operation of the wrapper. Caveat emptor!
595 ASDCP::MXF::OPAtomIndexFooter&
596 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
598 if ( m_Writer.empty() )
600 assert(g_OPAtomIndexFooter);
601 return *g_OPAtomIndexFooter;
604 return m_Writer->m_FooterPart;
607 // Open the file for writing. The file must not exist. Returns error if
608 // the operation cannot be completed.
610 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
611 const AudioDescriptor& ADesc, ui32_t HeaderSize)
613 if ( Info.LabelSetType == LS_MXF_SMPTE )
614 m_Writer = new h__Writer(DefaultSMPTEDict());
616 m_Writer = new h__Writer(DefaultInteropDict());
618 m_Writer->m_Info = Info;
620 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
622 if ( ASDCP_SUCCESS(result) )
623 result = m_Writer->SetSourceStream(ADesc);
625 if ( ASDCP_FAILURE(result) )
631 // Writes a frame of essence to the MXF file. If the optional AESEncContext
632 // argument is present, the essence is encrypted prior to writing.
633 // Fails if the file is not open, is finalized, or an operating system
636 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
638 if ( m_Writer.empty() )
641 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
644 // Closes the MXF file, writing the index and other closing information.
646 ASDCP::PCM::MXFWriter::Finalize()
648 if ( m_Writer.empty() )
651 return m_Writer->Finalize();
655 // end AS_DCP_PCM.cpp