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 const ASDCP::Dictionary *sg_dict = &DefaultSMPTEDict();
40 static MXF::OPAtomHeader sg_OPAtomHeader(sg_dict);
41 static MXF::OPAtomIndexFooter sg_OPAtomIndexFooter(sg_dict);
43 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of wave audio";
44 static std::string SOUND_DEF_LABEL = "Sound Track";
48 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
50 ASDCP_TEST_NULL(ADescObj);
51 ADescObj->SampleRate = ADesc.EditRate;
52 ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
53 ADescObj->Locked = ADesc.Locked;
54 ADescObj->ChannelCount = ADesc.ChannelCount;
55 ADescObj->QuantizationBits = ADesc.QuantizationBits;
56 ADescObj->BlockAlign = ADesc.BlockAlign;
57 ADescObj->AvgBps = ADesc.AvgBps;
58 ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
59 ADescObj->ContainerDuration = ADesc.ContainerDuration;
61 ADescObj->ChannelAssignment.Reset();
63 switch ( ADesc.ChannelFormat )
66 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul;
70 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul;
74 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul;
78 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul;
82 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 // check for sample/frame rate sanity
236 if ( ASDCP_SUCCESS(result)
237 && m_ADesc.EditRate != EditRate_24
238 && m_ADesc.EditRate != EditRate_25
239 && m_ADesc.EditRate != EditRate_30
240 && m_ADesc.EditRate != EditRate_48
241 && m_ADesc.EditRate != EditRate_50
242 && m_ADesc.EditRate != EditRate_60
243 && m_ADesc.EditRate != EditRate_96
244 && m_ADesc.EditRate != EditRate_100
245 && m_ADesc.EditRate != EditRate_120
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() )
327 return sg_OPAtomHeader;
329 return m_Reader->m_HeaderPart;
332 // Warning: direct manipulation of MXF structures can interfere
333 // with the normal operation of the wrapper. Caveat emptor!
335 ASDCP::MXF::OPAtomIndexFooter&
336 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
338 if ( m_Reader.empty() )
339 return sg_OPAtomIndexFooter;
341 return m_Reader->m_FooterPart;
344 // Open the file for reading. The file must exist. Returns error if the
345 // operation cannot be completed.
347 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
349 return m_Reader->OpenRead(filename);
352 // Reads a frame of essence from the MXF file. If the optional AESEncContext
353 // argument is present, the essence is decrypted after reading. If the MXF
354 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
355 // will contain the ciphertext frame data.
357 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
358 AESDecContext* Ctx, HMACContext* HMAC) const
360 if ( m_Reader && m_Reader->m_File.IsOpen() )
361 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
367 // Fill the struct with the values from the file's header.
368 // Returns RESULT_INIT if the file is not open.
370 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
372 if ( m_Reader && m_Reader->m_File.IsOpen() )
374 ADesc = m_Reader->m_ADesc;
381 // Fill the struct with the values from the file's header.
382 // Returns RESULT_INIT if the file is not open.
384 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
386 if ( m_Reader && m_Reader->m_File.IsOpen() )
388 Info = m_Reader->m_Info;
397 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
399 if ( m_Reader && m_Reader->m_File.IsOpen() )
400 m_Reader->m_HeaderPart.Dump(stream);
406 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
408 if ( m_Reader->m_File.IsOpen() )
409 m_Reader->m_FooterPart.Dump(stream);
414 ASDCP::PCM::MXFReader::Close() const
416 if ( m_Reader && m_Reader->m_File.IsOpen() )
426 //------------------------------------------------------------------------------------------
429 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
431 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
435 AudioDescriptor m_ADesc;
436 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
438 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
439 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
444 Result_t OpenWrite(const char*, ui32_t HeaderSize);
445 Result_t SetSourceStream(const AudioDescriptor&);
446 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
452 // Open the file for writing. The file must not exist. Returns error if
453 // the operation cannot be completed.
455 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
457 if ( ! m_State.Test_BEGIN() )
460 Result_t result = m_File.OpenWrite(filename);
462 if ( ASDCP_SUCCESS(result) )
464 m_HeaderSize = HeaderSize;
465 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
466 result = m_State.Goto_INIT();
473 // Automatically sets the MXF file's metadata from the WAV parser info.
475 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
477 if ( ! m_State.Test_INIT() )
480 if ( ADesc.EditRate != EditRate_24
481 && ADesc.EditRate != EditRate_25
482 && ADesc.EditRate != EditRate_30
483 && ADesc.EditRate != EditRate_48
484 && ADesc.EditRate != EditRate_50
485 && ADesc.EditRate != EditRate_60
486 && ADesc.EditRate != EditRate_96
487 && ADesc.EditRate != EditRate_100
488 && ADesc.EditRate != EditRate_120
489 && ADesc.EditRate != EditRate_23_98 )
491 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
492 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
493 return RESULT_RAW_FORMAT;
496 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
498 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
499 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
500 return RESULT_RAW_FORMAT;
506 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
508 if ( ASDCP_SUCCESS(result) )
510 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
511 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
512 result = m_State.Goto_READY();
515 if ( ASDCP_SUCCESS(result) )
517 ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98 ) ? 24 : m_ADesc.EditRate.Numerator;
519 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
520 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
521 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
531 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
534 Result_t result = RESULT_OK;
536 if ( m_State.Test_READY() )
537 result = m_State.Goto_RUNNING(); // first time through
539 if ( ASDCP_SUCCESS(result) )
540 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
542 if ( ASDCP_SUCCESS(result) )
548 // Closes the MXF file, writing the index and other closing information.
551 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
553 if ( ! m_State.Test_RUNNING() )
556 m_State.Goto_FINAL();
558 return WriteMXFFooter();
562 //------------------------------------------------------------------------------------------
567 ASDCP::PCM::MXFWriter::MXFWriter()
571 ASDCP::PCM::MXFWriter::~MXFWriter()
575 // Warning: direct manipulation of MXF structures can interfere
576 // with the normal operation of the wrapper. Caveat emptor!
578 ASDCP::MXF::OPAtomHeader&
579 ASDCP::PCM::MXFWriter::OPAtomHeader()
581 if ( m_Writer.empty() )
582 return sg_OPAtomHeader;
584 return m_Writer->m_HeaderPart;
587 // Warning: direct manipulation of MXF structures can interfere
588 // with the normal operation of the wrapper. Caveat emptor!
590 ASDCP::MXF::OPAtomIndexFooter&
591 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
593 if ( m_Writer.empty() )
594 return sg_OPAtomIndexFooter;
596 return m_Writer->m_FooterPart;
599 // Open the file for writing. The file must not exist. Returns error if
600 // the operation cannot be completed.
602 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
603 const AudioDescriptor& ADesc, ui32_t HeaderSize)
605 if ( Info.LabelSetType == LS_MXF_SMPTE )
606 m_Writer = new h__Writer(DefaultSMPTEDict());
608 m_Writer = new h__Writer(DefaultInteropDict());
610 m_Writer->m_Info = Info;
612 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
614 if ( ASDCP_SUCCESS(result) )
615 result = m_Writer->SetSourceStream(ADesc);
617 if ( ASDCP_FAILURE(result) )
623 // Writes a frame of essence to the MXF file. If the optional AESEncContext
624 // argument is present, the essence is encrypted prior to writing.
625 // Fails if the file is not open, is finalized, or an operating system
628 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
630 if ( m_Writer.empty() )
633 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
636 // Closes the MXF file, writing the index and other closing information.
638 ASDCP::PCM::MXFWriter::Finalize()
640 if ( m_Writer.empty() )
643 return m_Writer->Finalize();
647 // end AS_DCP_PCM.cpp