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);
199 AudioDescriptor m_ADesc;
203 Result_t OpenRead(const char*);
204 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
211 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
213 Result_t result = OpenMXFRead(filename);
215 if( ASDCP_SUCCESS(result) )
217 InterchangeObject* Object;
218 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
221 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
225 // check for sample/frame rate sanity
226 if ( ASDCP_SUCCESS(result)
227 && m_ADesc.SampleRate != EditRate_24
228 && m_ADesc.SampleRate != EditRate_48
229 && m_ADesc.SampleRate != EditRate_23_98 )
231 DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
232 m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
234 // oh, they gave us the audio sampling rate instead, assume 24/1
235 if ( m_ADesc.SampleRate == SampleRate_48k )
237 DefaultLogSink().Warn("adjusting SampleRate to 24/1\n");
238 m_ADesc.SampleRate.Numerator = 24;
239 m_ADesc.SampleRate.Denominator = 1;
243 // or we just drop the hammer
244 return RESULT_FORMAT;
248 if( ASDCP_SUCCESS(result) )
249 result = InitMXFIndex();
251 if( ASDCP_SUCCESS(result) )
254 // TODO: test file for sane CBR index BytesPerEditUnit
263 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
264 AESDecContext* Ctx, HMACContext* HMAC)
266 if ( ! m_File.IsOpen() )
269 return ReadEKLVFrame(FrameNum, FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
272 //------------------------------------------------------------------------------------------
277 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
282 fprintf(stream, "Frame: %06u, %7u bytes\n",
283 m_FrameNumber, m_Size);
286 Kumu::hexdump(m_Data, dump_len, stream);
289 //------------------------------------------------------------------------------------------
291 ASDCP::PCM::MXFReader::MXFReader()
293 m_Reader = new h__Reader;
297 ASDCP::PCM::MXFReader::~MXFReader()
299 if ( m_Reader && m_Reader->m_File.IsOpen() )
303 // Open the file for reading. The file must exist. Returns error if the
304 // operation cannot be completed.
306 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
308 return m_Reader->OpenRead(filename);
311 // Reads a frame of essence from the MXF file. If the optional AESEncContext
312 // argument is present, the essence is decrypted after reading. If the MXF
313 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
314 // will contain the ciphertext frame data.
316 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
317 AESDecContext* Ctx, HMACContext* HMAC) const
319 if ( m_Reader && m_Reader->m_File.IsOpen() )
320 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
326 // Fill the struct with the values from the file's header.
327 // Returns RESULT_INIT if the file is not open.
329 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
331 if ( m_Reader && m_Reader->m_File.IsOpen() )
333 ADesc = m_Reader->m_ADesc;
340 // Fill the struct with the values from the file's header.
341 // Returns RESULT_INIT if the file is not open.
343 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
345 if ( m_Reader && m_Reader->m_File.IsOpen() )
347 Info = m_Reader->m_Info;
356 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
358 if ( m_Reader && m_Reader->m_File.IsOpen() )
359 m_Reader->m_HeaderPart.Dump(stream);
365 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
367 if ( m_Reader->m_File.IsOpen() )
368 m_Reader->m_FooterPart.Dump(stream);
372 //------------------------------------------------------------------------------------------
375 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
378 AudioDescriptor m_ADesc;
379 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
382 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
385 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
390 Result_t OpenWrite(const char*, ui32_t HeaderSize);
391 Result_t SetSourceStream(const AudioDescriptor&);
392 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
398 // Open the file for writing. The file must not exist. Returns error if
399 // the operation cannot be completed.
401 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
403 if ( ! m_State.Test_BEGIN() )
406 Result_t result = m_File.OpenWrite(filename);
408 if ( ASDCP_SUCCESS(result) )
410 m_HeaderSize = HeaderSize;
411 m_EssenceDescriptor = new WaveAudioDescriptor;
412 result = m_State.Goto_INIT();
419 // Automatically sets the MXF file's metadata from the WAV parser info.
421 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
423 if ( ! m_State.Test_INIT() )
426 if ( ADesc.SampleRate != EditRate_24
427 && ADesc.SampleRate != EditRate_48
428 && ADesc.SampleRate != EditRate_23_98 )
430 DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
431 ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
432 return RESULT_RAW_FORMAT;
435 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
437 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
438 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
439 return RESULT_RAW_FORMAT;
443 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
445 if ( ASDCP_SUCCESS(result) )
446 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
447 SOUND_DEF_LABEL, UL(Dict::ul(MDD_SoundDataDef)),
448 m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
450 if ( ASDCP_SUCCESS(result) )
452 memcpy(m_EssenceUL, Dict::ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
453 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
454 result = m_State.Goto_READY();
464 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
467 Result_t result = RESULT_OK;
469 if ( m_State.Test_READY() )
470 result = m_State.Goto_RUNNING(); // first time through
472 if ( ASDCP_SUCCESS(result) )
473 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
475 if ( ASDCP_SUCCESS(result) )
481 // Closes the MXF file, writing the index and other closing information.
484 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
486 if ( ! m_State.Test_RUNNING() )
489 m_State.Goto_FINAL();
491 return WriteMXFFooter();
495 //------------------------------------------------------------------------------------------
500 ASDCP::PCM::MXFWriter::MXFWriter()
504 ASDCP::PCM::MXFWriter::~MXFWriter()
509 // Open the file for writing. The file must not exist. Returns error if
510 // the operation cannot be completed.
512 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
513 const AudioDescriptor& ADesc, ui32_t HeaderSize)
515 m_Writer = new h__Writer;
516 m_Writer->m_Info = Info;
518 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
520 if ( ASDCP_SUCCESS(result) )
521 result = m_Writer->SetSourceStream(ADesc);
523 if ( ASDCP_FAILURE(result) )
529 // Writes a frame of essence to the MXF file. If the optional AESEncContext
530 // argument is present, the essence is encrypted prior to writing.
531 // Fails if the file is not open, is finalized, or an operating system
534 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
536 if ( m_Writer.empty() )
539 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
542 // Closes the MXF file, writing the index and other closing information.
544 ASDCP::PCM::MXFWriter::Finalize()
546 if ( m_Writer.empty() )
549 return m_Writer->Finalize();
553 // end AS_DCP_PCM.cpp