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
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 ASDCP::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 ASDCP::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__ASDCPReader
201 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
205 AudioDescriptor m_ADesc;
207 h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(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_16
243 && m_ADesc.EditRate != EditRate_18
244 && m_ADesc.EditRate != EditRate_20
245 && m_ADesc.EditRate != EditRate_22
246 && m_ADesc.EditRate != EditRate_23_98 )
248 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
249 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
251 // oh, they gave us the audio sampling rate instead, assume 24/1
252 if ( m_ADesc.EditRate == SampleRate_48k )
254 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
255 m_ADesc.EditRate = EditRate_24;
259 // or we just drop the hammer
260 return RESULT_FORMAT;
264 if( ASDCP_SUCCESS(result) )
265 result = InitMXFIndex();
267 if( ASDCP_SUCCESS(result) )
270 // TODO: test file for sane CBR index BytesPerEditUnit
279 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
280 AESDecContext* Ctx, HMACContext* HMAC)
282 if ( ! m_File.IsOpen() )
286 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
289 //------------------------------------------------------------------------------------------
294 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
299 fprintf(stream, "Frame: %06u, %7u bytes\n",
300 m_FrameNumber, m_Size);
303 Kumu::hexdump(m_Data, dump_len, stream);
306 //------------------------------------------------------------------------------------------
308 ASDCP::PCM::MXFReader::MXFReader()
310 m_Reader = new h__Reader(DefaultCompositeDict());
314 ASDCP::PCM::MXFReader::~MXFReader()
316 if ( m_Reader && m_Reader->m_File.IsOpen() )
320 // Warning: direct manipulation of MXF structures can interfere
321 // with the normal operation of the wrapper. Caveat emptor!
323 ASDCP::MXF::OPAtomHeader&
324 ASDCP::PCM::MXFReader::OPAtomHeader()
326 if ( m_Reader.empty() )
328 assert(g_OPAtomHeader);
329 return *g_OPAtomHeader;
332 return m_Reader->m_HeaderPart;
335 // Warning: direct manipulation of MXF structures can interfere
336 // with the normal operation of the wrapper. Caveat emptor!
338 ASDCP::MXF::OPAtomIndexFooter&
339 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
341 if ( m_Reader.empty() )
343 assert(g_OPAtomIndexFooter);
344 return *g_OPAtomIndexFooter;
347 return m_Reader->m_FooterPart;
350 // Open the file for reading. The file must exist. Returns error if the
351 // operation cannot be completed.
353 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
355 return m_Reader->OpenRead(filename);
358 // Reads a frame of essence from the MXF file. If the optional AESEncContext
359 // argument is present, the essence is decrypted after reading. If the MXF
360 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
361 // will contain the ciphertext frame data.
363 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
364 AESDecContext* Ctx, HMACContext* HMAC) const
366 if ( m_Reader && m_Reader->m_File.IsOpen() )
367 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
373 // Fill the struct with the values from the file's header.
374 // Returns RESULT_INIT if the file is not open.
376 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
378 if ( m_Reader && m_Reader->m_File.IsOpen() )
380 ADesc = m_Reader->m_ADesc;
387 // Fill the struct with the values from the file's header.
388 // Returns RESULT_INIT if the file is not open.
390 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
392 if ( m_Reader && m_Reader->m_File.IsOpen() )
394 Info = m_Reader->m_Info;
403 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
405 if ( m_Reader && m_Reader->m_File.IsOpen() )
406 m_Reader->m_HeaderPart.Dump(stream);
412 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
414 if ( m_Reader->m_File.IsOpen() )
415 m_Reader->m_FooterPart.Dump(stream);
420 ASDCP::PCM::MXFReader::Close() const
422 if ( m_Reader && m_Reader->m_File.IsOpen() )
432 //------------------------------------------------------------------------------------------
435 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
437 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
441 AudioDescriptor m_ADesc;
442 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
444 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
445 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
450 Result_t OpenWrite(const char*, ui32_t HeaderSize);
451 Result_t SetSourceStream(const AudioDescriptor&);
452 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
458 // Open the file for writing. The file must not exist. Returns error if
459 // the operation cannot be completed.
461 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
463 if ( ! m_State.Test_BEGIN() )
466 Result_t result = m_File.OpenWrite(filename);
468 if ( ASDCP_SUCCESS(result) )
470 m_HeaderSize = HeaderSize;
471 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
472 result = m_State.Goto_INIT();
479 // Automatically sets the MXF file's metadata from the WAV parser info.
481 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
483 if ( ! m_State.Test_INIT() )
486 if ( ADesc.EditRate != EditRate_24
487 && ADesc.EditRate != EditRate_25
488 && ADesc.EditRate != EditRate_30
489 && ADesc.EditRate != EditRate_48
490 && ADesc.EditRate != EditRate_50
491 && ADesc.EditRate != EditRate_60
492 && ADesc.EditRate != EditRate_96
493 && ADesc.EditRate != EditRate_100
494 && ADesc.EditRate != EditRate_120
495 && ADesc.EditRate != EditRate_16
496 && ADesc.EditRate != EditRate_18
497 && ADesc.EditRate != EditRate_20
498 && ADesc.EditRate != EditRate_22
499 && ADesc.EditRate != EditRate_23_98 )
501 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
502 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
503 return RESULT_RAW_FORMAT;
506 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
508 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
509 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
510 return RESULT_RAW_FORMAT;
516 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
518 if ( ASDCP_SUCCESS(result) )
520 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
521 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
522 result = m_State.Goto_READY();
525 if ( ASDCP_SUCCESS(result) )
527 ui32_t TCFrameRate = m_ADesc.EditRate.Numerator;
529 if ( m_ADesc.EditRate == EditRate_23_98 )
531 else if ( m_ADesc.EditRate == EditRate_18 )
533 else if ( m_ADesc.EditRate == EditRate_22 )
536 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
537 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
538 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
548 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
551 Result_t result = RESULT_OK;
553 if ( m_State.Test_READY() )
554 result = m_State.Goto_RUNNING(); // first time through
556 if ( ASDCP_SUCCESS(result) )
557 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
559 if ( ASDCP_SUCCESS(result) )
565 // Closes the MXF file, writing the index and other closing information.
568 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
570 if ( ! m_State.Test_RUNNING() )
573 m_State.Goto_FINAL();
575 return WriteMXFFooter();
579 //------------------------------------------------------------------------------------------
584 ASDCP::PCM::MXFWriter::MXFWriter()
588 ASDCP::PCM::MXFWriter::~MXFWriter()
592 // Warning: direct manipulation of MXF structures can interfere
593 // with the normal operation of the wrapper. Caveat emptor!
595 ASDCP::MXF::OPAtomHeader&
596 ASDCP::PCM::MXFWriter::OPAtomHeader()
598 if ( m_Writer.empty() )
600 assert(g_OPAtomHeader);
601 return *g_OPAtomHeader;
604 return m_Writer->m_HeaderPart;
607 // Warning: direct manipulation of MXF structures can interfere
608 // with the normal operation of the wrapper. Caveat emptor!
610 ASDCP::MXF::OPAtomIndexFooter&
611 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
613 if ( m_Writer.empty() )
615 assert(g_OPAtomIndexFooter);
616 return *g_OPAtomIndexFooter;
619 return m_Writer->m_FooterPart;
622 // Open the file for writing. The file must not exist. Returns error if
623 // the operation cannot be completed.
625 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
626 const AudioDescriptor& ADesc, ui32_t HeaderSize)
628 if ( Info.LabelSetType == LS_MXF_SMPTE )
629 m_Writer = new h__Writer(DefaultSMPTEDict());
631 m_Writer = new h__Writer(DefaultInteropDict());
633 m_Writer->m_Info = Info;
635 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
637 if ( ASDCP_SUCCESS(result) )
638 result = m_Writer->SetSourceStream(ADesc);
640 if ( ASDCP_FAILURE(result) )
646 // Writes a frame of essence to the MXF file. If the optional AESEncContext
647 // argument is present, the essence is encrypted prior to writing.
648 // Fails if the file is not open, is finalized, or an operating system
651 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
653 if ( m_Writer.empty() )
656 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
659 // Closes the MXF file, writing the index and other closing information.
661 ASDCP::PCM::MXFWriter::Finalize()
663 if ( m_Writer.empty() )
666 return m_Writer->Finalize();
670 // end AS_DCP_PCM.cpp