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;
91 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
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;
105 ADesc.ChannelFormat = PCM::CF_NONE;
107 if ( ADescObj->ChannelAssignment.HasValue() )
109 if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul )
110 ADesc.ChannelFormat = PCM::CF_CFG_1;
112 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul )
113 ADesc.ChannelFormat = PCM::CF_CFG_2;
115 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul )
116 ADesc.ChannelFormat = PCM::CF_CFG_3;
118 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul )
119 ADesc.ChannelFormat = PCM::CF_CFG_4;
121 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul )
122 ADesc.ChannelFormat = PCM::CF_CFG_5;
130 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
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;
147 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
154 AudioSamplingRate: %d/%d\n\
157 QuantizationBits: %u\n\
161 ContainerDuration: %u\n",
162 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
163 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
166 ADesc.QuantizationBits,
170 ADesc.ContainerDuration
178 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
180 ui32_t CBR_frame_size = 0;
182 if ( Info.EncryptedEssence )
188 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
189 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
193 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
196 return CBR_frame_size;
200 //------------------------------------------------------------------------------------------
203 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
205 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
209 AudioDescriptor m_ADesc;
211 h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
213 Result_t OpenRead(const char*);
214 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
221 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
223 Result_t result = OpenMXFRead(filename);
225 if( ASDCP_SUCCESS(result) )
227 InterchangeObject* Object;
228 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
231 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
235 /* This check has been removed so that DCP-o-matic can use any edit rate
239 // check for sample/frame rate sanity
240 if ( ASDCP_SUCCESS(result)
241 && m_ADesc.EditRate != EditRate_24
242 && m_ADesc.EditRate != EditRate_25
243 && m_ADesc.EditRate != EditRate_30
244 && m_ADesc.EditRate != EditRate_48
245 && m_ADesc.EditRate != EditRate_50
246 && m_ADesc.EditRate != EditRate_60
247 && m_ADesc.EditRate != EditRate_96
248 && m_ADesc.EditRate != EditRate_100
249 && m_ADesc.EditRate != EditRate_120
250 && m_ADesc.EditRate != EditRate_23_98 )
252 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
253 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
255 // oh, they gave us the audio sampling rate instead, assume 24/1
256 if ( m_ADesc.EditRate == SampleRate_48k )
258 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
259 m_ADesc.EditRate = EditRate_24;
263 // or we just drop the hammer
264 return RESULT_FORMAT;
269 if( ASDCP_SUCCESS(result) )
270 result = InitMXFIndex();
272 if( ASDCP_SUCCESS(result) )
275 // TODO: test file for sane CBR index BytesPerEditUnit
284 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
285 AESDecContext* Ctx, HMACContext* HMAC)
287 if ( ! m_File.IsOpen() )
291 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
294 //------------------------------------------------------------------------------------------
299 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
304 fprintf(stream, "Frame: %06u, %7u bytes\n",
305 m_FrameNumber, m_Size);
308 Kumu::hexdump(m_Data, dump_len, stream);
311 //------------------------------------------------------------------------------------------
313 ASDCP::PCM::MXFReader::MXFReader()
315 m_Reader = new h__Reader(DefaultCompositeDict());
319 ASDCP::PCM::MXFReader::~MXFReader()
321 if ( m_Reader && m_Reader->m_File.IsOpen() )
325 // Warning: direct manipulation of MXF structures can interfere
326 // with the normal operation of the wrapper. Caveat emptor!
328 ASDCP::MXF::OPAtomHeader&
329 ASDCP::PCM::MXFReader::OPAtomHeader()
331 if ( m_Reader.empty() )
333 assert(g_OPAtomHeader);
334 return *g_OPAtomHeader;
337 return m_Reader->m_HeaderPart;
340 // Warning: direct manipulation of MXF structures can interfere
341 // with the normal operation of the wrapper. Caveat emptor!
343 ASDCP::MXF::OPAtomIndexFooter&
344 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
346 if ( m_Reader.empty() )
348 assert(g_OPAtomIndexFooter);
349 return *g_OPAtomIndexFooter;
352 return m_Reader->m_FooterPart;
355 // Open the file for reading. The file must exist. Returns error if the
356 // operation cannot be completed.
358 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
360 return m_Reader->OpenRead(filename);
363 // Reads a frame of essence from the MXF file. If the optional AESEncContext
364 // argument is present, the essence is decrypted after reading. If the MXF
365 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
366 // will contain the ciphertext frame data.
368 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
369 AESDecContext* Ctx, HMACContext* HMAC) const
371 if ( m_Reader && m_Reader->m_File.IsOpen() )
372 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
378 // Fill the struct with the values from the file's header.
379 // Returns RESULT_INIT if the file is not open.
381 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
383 if ( m_Reader && m_Reader->m_File.IsOpen() )
385 ADesc = m_Reader->m_ADesc;
392 // Fill the struct with the values from the file's header.
393 // Returns RESULT_INIT if the file is not open.
395 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
397 if ( m_Reader && m_Reader->m_File.IsOpen() )
399 Info = m_Reader->m_Info;
408 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
410 if ( m_Reader && m_Reader->m_File.IsOpen() )
411 m_Reader->m_HeaderPart.Dump(stream);
417 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
419 if ( m_Reader->m_File.IsOpen() )
420 m_Reader->m_FooterPart.Dump(stream);
425 ASDCP::PCM::MXFReader::Close() const
427 if ( m_Reader && m_Reader->m_File.IsOpen() )
437 //------------------------------------------------------------------------------------------
440 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
442 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
446 AudioDescriptor m_ADesc;
447 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
449 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
450 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
455 Result_t OpenWrite(const char*, ui32_t HeaderSize);
456 Result_t SetSourceStream(const AudioDescriptor&);
457 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
463 // Open the file for writing. The file must not exist. Returns error if
464 // the operation cannot be completed.
466 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
468 if ( ! m_State.Test_BEGIN() )
471 Result_t result = m_File.OpenWrite(filename);
473 if ( ASDCP_SUCCESS(result) )
475 m_HeaderSize = HeaderSize;
476 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
477 result = m_State.Goto_INIT();
484 // Automatically sets the MXF file's metadata from the WAV parser info.
486 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
488 if ( ! m_State.Test_INIT() )
492 /* This check has been removed so that DCP-o-matic can use anye dit rate
495 if ( ADesc.EditRate != EditRate_24
496 && ADesc.EditRate != EditRate_25
497 && ADesc.EditRate != EditRate_30
498 && ADesc.EditRate != EditRate_48
499 && ADesc.EditRate != EditRate_50
500 && ADesc.EditRate != EditRate_60
501 && ADesc.EditRate != EditRate_96
502 && ADesc.EditRate != EditRate_100
503 && ADesc.EditRate != EditRate_120
504 && ADesc.EditRate != EditRate_23_98 )
506 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
507 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
508 return RESULT_RAW_FORMAT;
512 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
514 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
515 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
516 return RESULT_RAW_FORMAT;
522 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
524 if ( ASDCP_SUCCESS(result) )
526 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
527 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
528 result = m_State.Goto_READY();
531 if ( ASDCP_SUCCESS(result) )
533 ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98 ) ? 24 : m_ADesc.EditRate.Numerator;
535 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
536 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
537 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
547 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
550 Result_t result = RESULT_OK;
552 if ( m_State.Test_READY() )
553 result = m_State.Goto_RUNNING(); // first time through
555 if ( ASDCP_SUCCESS(result) )
556 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
558 if ( ASDCP_SUCCESS(result) )
564 // Closes the MXF file, writing the index and other closing information.
567 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
569 if ( ! m_State.Test_RUNNING() )
572 m_State.Goto_FINAL();
574 return WriteMXFFooter();
578 //------------------------------------------------------------------------------------------
583 ASDCP::PCM::MXFWriter::MXFWriter()
587 ASDCP::PCM::MXFWriter::~MXFWriter()
591 // Warning: direct manipulation of MXF structures can interfere
592 // with the normal operation of the wrapper. Caveat emptor!
594 ASDCP::MXF::OPAtomHeader&
595 ASDCP::PCM::MXFWriter::OPAtomHeader()
597 if ( m_Writer.empty() )
599 assert(g_OPAtomHeader);
600 return *g_OPAtomHeader;
603 return m_Writer->m_HeaderPart;
606 // Warning: direct manipulation of MXF structures can interfere
607 // with the normal operation of the wrapper. Caveat emptor!
609 ASDCP::MXF::OPAtomIndexFooter&
610 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
612 if ( m_Writer.empty() )
614 assert(g_OPAtomIndexFooter);
615 return *g_OPAtomIndexFooter;
618 return m_Writer->m_FooterPart;
621 // Open the file for writing. The file must not exist. Returns error if
622 // the operation cannot be completed.
624 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
625 const AudioDescriptor& ADesc, ui32_t HeaderSize)
627 if ( Info.LabelSetType == LS_MXF_SMPTE )
628 m_Writer = new h__Writer(DefaultSMPTEDict());
630 m_Writer = new h__Writer(DefaultInteropDict());
632 m_Writer->m_Info = Info;
634 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
636 if ( ASDCP_SUCCESS(result) )
637 result = m_Writer->SetSourceStream(ADesc);
639 if ( ASDCP_FAILURE(result) )
645 // Writes a frame of essence to the MXF file. If the optional AESEncContext
646 // argument is present, the essence is encrypted prior to writing.
647 // Fails if the file is not open, is finalized, or an operating system
650 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
652 if ( m_Writer.empty() )
655 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
658 // Closes the MXF file, writing the index and other closing information.
660 ASDCP::PCM::MXFWriter::Finalize()
662 if ( m_Writer.empty() )
665 return m_Writer->Finalize();
669 // end AS_DCP_PCM.cpp