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";
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_23_98 )
241 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
242 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
244 // oh, they gave us the audio sampling rate instead, assume 24/1
245 if ( m_ADesc.EditRate == SampleRate_48k )
247 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
248 m_ADesc.EditRate = EditRate_24;
252 // or we just drop the hammer
253 return RESULT_FORMAT;
257 if( ASDCP_SUCCESS(result) )
258 result = InitMXFIndex();
260 if( ASDCP_SUCCESS(result) )
263 // TODO: test file for sane CBR index BytesPerEditUnit
272 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
273 AESDecContext* Ctx, HMACContext* HMAC)
275 if ( ! m_File.IsOpen() )
279 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
282 //------------------------------------------------------------------------------------------
287 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
292 fprintf(stream, "Frame: %06u, %7u bytes\n",
293 m_FrameNumber, m_Size);
296 Kumu::hexdump(m_Data, dump_len, stream);
299 //------------------------------------------------------------------------------------------
301 ASDCP::PCM::MXFReader::MXFReader()
303 m_Reader = new h__Reader(DefaultCompositeDict());
307 ASDCP::PCM::MXFReader::~MXFReader()
309 if ( m_Reader && m_Reader->m_File.IsOpen() )
313 // Open the file for reading. The file must exist. Returns error if the
314 // operation cannot be completed.
316 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
318 return m_Reader->OpenRead(filename);
321 // Reads a frame of essence from the MXF file. If the optional AESEncContext
322 // argument is present, the essence is decrypted after reading. If the MXF
323 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
324 // will contain the ciphertext frame data.
326 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
327 AESDecContext* Ctx, HMACContext* HMAC) const
329 if ( m_Reader && m_Reader->m_File.IsOpen() )
330 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
336 // Fill the struct with the values from the file's header.
337 // Returns RESULT_INIT if the file is not open.
339 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
341 if ( m_Reader && m_Reader->m_File.IsOpen() )
343 ADesc = m_Reader->m_ADesc;
350 // Fill the struct with the values from the file's header.
351 // Returns RESULT_INIT if the file is not open.
353 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
355 if ( m_Reader && m_Reader->m_File.IsOpen() )
357 Info = m_Reader->m_Info;
366 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
368 if ( m_Reader && m_Reader->m_File.IsOpen() )
369 m_Reader->m_HeaderPart.Dump(stream);
375 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
377 if ( m_Reader->m_File.IsOpen() )
378 m_Reader->m_FooterPart.Dump(stream);
383 ASDCP::PCM::MXFReader::Close() const
385 if ( m_Reader && m_Reader->m_File.IsOpen() )
395 //------------------------------------------------------------------------------------------
398 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
400 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
404 AudioDescriptor m_ADesc;
405 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
407 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
408 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
413 Result_t OpenWrite(const char*, ui32_t HeaderSize);
414 Result_t SetSourceStream(const AudioDescriptor&);
415 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
421 // Open the file for writing. The file must not exist. Returns error if
422 // the operation cannot be completed.
424 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
426 if ( ! m_State.Test_BEGIN() )
429 Result_t result = m_File.OpenWrite(filename);
431 if ( ASDCP_SUCCESS(result) )
433 m_HeaderSize = HeaderSize;
434 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
435 result = m_State.Goto_INIT();
442 // Automatically sets the MXF file's metadata from the WAV parser info.
444 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
446 if ( ! m_State.Test_INIT() )
449 if ( ADesc.EditRate != EditRate_24
450 && ADesc.EditRate != EditRate_25
451 && ADesc.EditRate != EditRate_30
452 && ADesc.EditRate != EditRate_48
453 && ADesc.EditRate != EditRate_50
454 && ADesc.EditRate != EditRate_60
455 && ADesc.EditRate != EditRate_23_98 )
457 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
458 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
459 return RESULT_RAW_FORMAT;
462 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
464 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
465 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
466 return RESULT_RAW_FORMAT;
472 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
474 if ( ASDCP_SUCCESS(result) )
476 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
477 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
478 result = m_State.Goto_READY();
481 if ( ASDCP_SUCCESS(result) )
483 ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98 ) ? 24 : m_ADesc.EditRate.Numerator;
485 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
486 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
487 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
497 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
500 Result_t result = RESULT_OK;
502 if ( m_State.Test_READY() )
503 result = m_State.Goto_RUNNING(); // first time through
505 if ( ASDCP_SUCCESS(result) )
506 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
508 if ( ASDCP_SUCCESS(result) )
514 // Closes the MXF file, writing the index and other closing information.
517 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
519 if ( ! m_State.Test_RUNNING() )
522 m_State.Goto_FINAL();
524 return WriteMXFFooter();
528 //------------------------------------------------------------------------------------------
533 ASDCP::PCM::MXFWriter::MXFWriter()
537 ASDCP::PCM::MXFWriter::~MXFWriter()
542 // Open the file for writing. The file must not exist. Returns error if
543 // the operation cannot be completed.
545 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
546 const AudioDescriptor& ADesc, ui32_t HeaderSize)
548 if ( Info.LabelSetType == LS_MXF_SMPTE )
549 m_Writer = new h__Writer(DefaultSMPTEDict());
551 m_Writer = new h__Writer(DefaultInteropDict());
553 m_Writer->m_Info = Info;
555 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
557 if ( ASDCP_SUCCESS(result) )
558 result = m_Writer->SetSourceStream(ADesc);
560 if ( ASDCP_FAILURE(result) )
566 // Writes a frame of essence to the MXF file. If the optional AESEncContext
567 // argument is present, the essence is encrypted prior to writing.
568 // Fails if the file is not open, is finalized, or an operating system
571 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
573 if ( m_Writer.empty() )
576 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
579 // Closes the MXF file, writing the index and other closing information.
581 ASDCP::PCM::MXFWriter::Finalize()
583 if ( m_Writer.empty() )
586 return m_Writer->Finalize();
590 // end AS_DCP_PCM.cpp