2 Copyright (c) 2004-2009, 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";
42 static byte_t SNDFMT_CFG_1_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
43 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x01, 0x00 };
45 static byte_t SNDFMT_CFG_2_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
46 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x02, 0x00 };
48 static byte_t SNDFMT_CFG_3_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
49 0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x03, 0x00 };
53 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
55 ASDCP_TEST_NULL(ADescObj);
56 ADescObj->SampleRate = ADesc.SampleRate;
57 ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
58 ADescObj->Locked = ADesc.Locked;
59 ADescObj->ChannelCount = ADesc.ChannelCount;
60 ADescObj->QuantizationBits = ADesc.QuantizationBits;
61 ADescObj->BlockAlign = ADesc.BlockAlign;
62 ADescObj->AvgBps = ADesc.AvgBps;
63 ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
64 ADescObj->ContainerDuration = ADesc.ContainerDuration;
66 ADescObj->ChannelAssignment.Reset();
68 switch ( ADesc.ChannelFormat )
71 ADescObj->ChannelAssignment = UL(SNDFMT_CFG_1_UL);
75 ADescObj->ChannelAssignment = UL(SNDFMT_CFG_2_UL);
79 ADescObj->ChannelAssignment = UL(SNDFMT_CFG_3_UL);
88 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
90 ASDCP_TEST_NULL(ADescObj);
91 ADesc.SampleRate = ADescObj->SampleRate;
92 ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
93 ADesc.Locked = ADescObj->Locked;
94 ADesc.ChannelCount = ADescObj->ChannelCount;
95 ADesc.QuantizationBits = ADescObj->QuantizationBits;
96 ADesc.BlockAlign = ADescObj->BlockAlign;
97 ADesc.AvgBps = ADescObj->AvgBps;
98 ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
99 assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
100 ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
102 ADesc.ChannelFormat = PCM::CF_NONE;
104 if ( ADescObj->ChannelAssignment.HasValue() )
106 if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_1_UL) )
107 ADesc.ChannelFormat = PCM::CF_CFG_1;
109 else if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_2_UL) )
110 ADesc.ChannelFormat = PCM::CF_CFG_2;
112 else if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_3_UL) )
113 ADesc.ChannelFormat = PCM::CF_CFG_3;
121 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
123 strm << " SampleRate: " << ADesc.SampleRate.Numerator << "/" << ADesc.SampleRate.Denominator << std::endl;
124 strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
125 strm << " Locked: " << (unsigned) ADesc.Locked << std::endl;
126 strm << " ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
127 strm << " QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
128 strm << " BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
129 strm << " AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
130 strm << " LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
131 strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
138 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
145 AudioSamplingRate: %d/%d\n\
148 QuantizationBits: %u\n\
152 ContainerDuration: %u\n",
153 ADesc.SampleRate.Numerator ,ADesc.SampleRate.Denominator,
154 ADesc.AudioSamplingRate.Numerator ,ADesc.AudioSamplingRate.Denominator,
157 ADesc.QuantizationBits,
161 ADesc.ContainerDuration
169 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
171 ui32_t CBR_frame_size = 0;
173 if ( Info.EncryptedEssence )
179 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
180 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
184 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
187 return CBR_frame_size;
191 //------------------------------------------------------------------------------------------
194 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
196 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
200 AudioDescriptor m_ADesc;
202 h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
204 Result_t OpenRead(const char*);
205 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
212 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
214 Result_t result = OpenMXFRead(filename);
216 if( ASDCP_SUCCESS(result) )
218 InterchangeObject* Object;
219 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
222 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
226 // check for sample/frame rate sanity
227 if ( ASDCP_SUCCESS(result)
228 && m_ADesc.SampleRate != EditRate_24
229 && m_ADesc.SampleRate != EditRate_48
230 && m_ADesc.SampleRate != EditRate_23_98 )
232 DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
233 m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
235 // oh, they gave us the audio sampling rate instead, assume 24/1
236 if ( m_ADesc.SampleRate == SampleRate_48k )
238 DefaultLogSink().Warn("adjusting SampleRate to 24/1\n");
239 m_ADesc.SampleRate.Numerator = 24;
240 m_ADesc.SampleRate.Denominator = 1;
244 // or we just drop the hammer
245 return RESULT_FORMAT;
249 if( ASDCP_SUCCESS(result) )
250 result = InitMXFIndex();
252 if( ASDCP_SUCCESS(result) )
255 // TODO: test file for sane CBR index BytesPerEditUnit
264 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
265 AESDecContext* Ctx, HMACContext* HMAC)
267 if ( ! m_File.IsOpen() )
271 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
274 //------------------------------------------------------------------------------------------
279 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
284 fprintf(stream, "Frame: %06u, %7u bytes\n",
285 m_FrameNumber, m_Size);
288 Kumu::hexdump(m_Data, dump_len, stream);
291 //------------------------------------------------------------------------------------------
293 ASDCP::PCM::MXFReader::MXFReader()
295 m_Reader = new h__Reader(DefaultCompositeDict());
299 ASDCP::PCM::MXFReader::~MXFReader()
301 if ( m_Reader && m_Reader->m_File.IsOpen() )
305 // Open the file for reading. The file must exist. Returns error if the
306 // operation cannot be completed.
308 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
310 return m_Reader->OpenRead(filename);
313 // Reads a frame of essence from the MXF file. If the optional AESEncContext
314 // argument is present, the essence is decrypted after reading. If the MXF
315 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
316 // will contain the ciphertext frame data.
318 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
319 AESDecContext* Ctx, HMACContext* HMAC) const
321 if ( m_Reader && m_Reader->m_File.IsOpen() )
322 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
328 // Fill the struct with the values from the file's header.
329 // Returns RESULT_INIT if the file is not open.
331 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
333 if ( m_Reader && m_Reader->m_File.IsOpen() )
335 ADesc = m_Reader->m_ADesc;
342 // Fill the struct with the values from the file's header.
343 // Returns RESULT_INIT if the file is not open.
345 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
347 if ( m_Reader && m_Reader->m_File.IsOpen() )
349 Info = m_Reader->m_Info;
358 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
360 if ( m_Reader && m_Reader->m_File.IsOpen() )
361 m_Reader->m_HeaderPart.Dump(stream);
367 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
369 if ( m_Reader->m_File.IsOpen() )
370 m_Reader->m_FooterPart.Dump(stream);
374 //------------------------------------------------------------------------------------------
377 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
379 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
383 AudioDescriptor m_ADesc;
384 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
386 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
387 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
392 Result_t OpenWrite(const char*, ui32_t HeaderSize);
393 Result_t SetSourceStream(const AudioDescriptor&);
394 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
400 // Open the file for writing. The file must not exist. Returns error if
401 // the operation cannot be completed.
403 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
405 if ( ! m_State.Test_BEGIN() )
408 Result_t result = m_File.OpenWrite(filename);
410 if ( ASDCP_SUCCESS(result) )
412 m_HeaderSize = HeaderSize;
413 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
414 result = m_State.Goto_INIT();
421 // Automatically sets the MXF file's metadata from the WAV parser info.
423 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
425 if ( ! m_State.Test_INIT() )
428 if ( ADesc.SampleRate != EditRate_24
429 && ADesc.SampleRate != EditRate_48
430 && ADesc.SampleRate != EditRate_23_98 )
432 DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
433 ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
434 return RESULT_RAW_FORMAT;
437 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
439 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
440 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
441 return RESULT_RAW_FORMAT;
446 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
448 if ( ASDCP_SUCCESS(result) )
449 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
450 SOUND_DEF_LABEL, UL(m_Dict->ul(MDD_SoundDataDef)),
451 m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
453 if ( ASDCP_SUCCESS(result) )
455 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
456 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
457 result = m_State.Goto_READY();
467 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
470 Result_t result = RESULT_OK;
472 if ( m_State.Test_READY() )
473 result = m_State.Goto_RUNNING(); // first time through
475 if ( ASDCP_SUCCESS(result) )
476 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
478 if ( ASDCP_SUCCESS(result) )
484 // Closes the MXF file, writing the index and other closing information.
487 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
489 if ( ! m_State.Test_RUNNING() )
492 m_State.Goto_FINAL();
494 return WriteMXFFooter();
498 //------------------------------------------------------------------------------------------
503 ASDCP::PCM::MXFWriter::MXFWriter()
507 ASDCP::PCM::MXFWriter::~MXFWriter()
512 // Open the file for writing. The file must not exist. Returns error if
513 // the operation cannot be completed.
515 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
516 const AudioDescriptor& ADesc, ui32_t HeaderSize)
518 if ( Info.LabelSetType == LS_MXF_SMPTE )
519 m_Writer = new h__Writer(DefaultSMPTEDict());
521 m_Writer = new h__Writer(DefaultInteropDict());
523 m_Writer->m_Info = Info;
525 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
527 if ( ASDCP_SUCCESS(result) )
528 result = m_Writer->SetSourceStream(ADesc);
530 if ( ASDCP_FAILURE(result) )
536 // Writes a frame of essence to the MXF file. If the optional AESEncContext
537 // argument is present, the essence is encrypted prior to writing.
538 // Fails if the file is not open, is finalized, or an operating system
541 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
543 if ( m_Writer.empty() )
546 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
549 // Closes the MXF file, writing the index and other closing information.
551 ASDCP::PCM::MXFWriter::Finalize()
553 if ( m_Writer.empty() )
556 return m_Writer->Finalize();
560 // end AS_DCP_PCM.cpp