2 Copyright (c) 2004-2006, 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"
35 //------------------------------------------------------------------------------------------
37 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of wave audio";
38 static std::string SOUND_DEF_LABEL = "Sound Track";
42 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
44 ASDCP_TEST_NULL(ADescObj);
45 ADescObj->SampleRate = ADesc.SampleRate;
46 ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
47 ADescObj->Locked = ADesc.Locked;
48 ADescObj->ChannelCount = ADesc.ChannelCount;
49 ADescObj->QuantizationBits = ADesc.QuantizationBits;
50 ADescObj->BlockAlign = ADesc.BlockAlign;
51 ADescObj->AvgBps = ADesc.AvgBps;
52 ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
53 ADescObj->ContainerDuration = ADesc.ContainerDuration;
59 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
61 ASDCP_TEST_NULL(ADescObj);
62 ADesc.SampleRate = ADescObj->SampleRate;
63 ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
64 ADesc.Locked = ADescObj->Locked;
65 ADesc.ChannelCount = ADescObj->ChannelCount;
66 ADesc.QuantizationBits = ADescObj->QuantizationBits;
67 ADesc.BlockAlign = ADescObj->BlockAlign;
68 ADesc.AvgBps = ADescObj->AvgBps;
69 ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
70 ADesc.ContainerDuration = ADescObj->ContainerDuration;
75 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
82 AudioSamplingRate: %d/%d\n\
85 QuantizationBits: %u\n\
89 ContainerDuration: %u\n",
90 ADesc.SampleRate.Numerator ,ADesc.SampleRate.Denominator,
91 ADesc.AudioSamplingRate.Numerator ,ADesc.AudioSamplingRate.Denominator,
94 ADesc.QuantizationBits,
98 ADesc.ContainerDuration
106 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
108 ui32_t CBR_frame_size = 0;
110 if ( Info.EncryptedEssence )
116 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
117 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
121 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
124 return CBR_frame_size;
128 //------------------------------------------------------------------------------------------
131 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
133 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
136 AudioDescriptor m_ADesc;
140 Result_t OpenRead(const char*);
141 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
148 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
150 Result_t result = OpenMXFRead(filename);
152 if( ASDCP_SUCCESS(result) )
154 InterchangeObject* Object;
155 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
158 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
162 // check for sample/frame rate sanity
163 if ( ASDCP_SUCCESS(result)
164 && m_ADesc.SampleRate != EditRate_24
165 && m_ADesc.SampleRate != EditRate_48
166 && m_ADesc.SampleRate != EditRate_23_98 )
168 DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
169 m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
171 // oh, they gave us the audio sampling rate instead, assume 24/1
172 if ( m_ADesc.SampleRate == SampleRate_48k )
174 DefaultLogSink().Warn("adjusting SampleRate to 24/1\n");
175 m_ADesc.SampleRate.Numerator = 24;
176 m_ADesc.SampleRate.Denominator = 1;
180 // or we just drop the hammer
181 return RESULT_FORMAT;
185 if( ASDCP_SUCCESS(result) )
186 result = InitMXFIndex();
188 if( ASDCP_SUCCESS(result) )
191 // TODO: test file for sane CBR index BytesPerEditUnit
200 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
201 AESDecContext* Ctx, HMACContext* HMAC)
203 if ( ! m_File.IsOpen() )
206 return ReadEKLVFrame(FrameNum, FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
209 //------------------------------------------------------------------------------------------
214 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
219 fprintf(stream, "Frame: %06u, %7u bytes\n",
220 m_FrameNumber, m_Size);
223 Kumu::hexdump(m_Data, dump_len, stream);
226 //------------------------------------------------------------------------------------------
228 ASDCP::PCM::MXFReader::MXFReader()
230 m_Reader = new h__Reader;
234 ASDCP::PCM::MXFReader::~MXFReader()
236 if ( m_Reader && m_Reader->m_File.IsOpen() )
240 // Open the file for reading. The file must exist. Returns error if the
241 // operation cannot be completed.
243 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
245 return m_Reader->OpenRead(filename);
248 // Reads a frame of essence from the MXF file. If the optional AESEncContext
249 // argument is present, the essence is decrypted after reading. If the MXF
250 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
251 // will contain the ciphertext frame data.
253 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
254 AESDecContext* Ctx, HMACContext* HMAC) const
256 if ( m_Reader && m_Reader->m_File.IsOpen() )
257 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
263 // Fill the struct with the values from the file's header.
264 // Returns RESULT_INIT if the file is not open.
266 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
268 if ( m_Reader && m_Reader->m_File.IsOpen() )
270 ADesc = m_Reader->m_ADesc;
277 // Fill the struct with the values from the file's header.
278 // Returns RESULT_INIT if the file is not open.
280 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
282 if ( m_Reader && m_Reader->m_File.IsOpen() )
284 Info = m_Reader->m_Info;
293 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
295 if ( m_Reader && m_Reader->m_File.IsOpen() )
296 m_Reader->m_HeaderPart.Dump(stream);
302 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
304 if ( m_Reader->m_File.IsOpen() )
305 m_Reader->m_FooterPart.Dump(stream);
309 //------------------------------------------------------------------------------------------
312 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
315 AudioDescriptor m_ADesc;
316 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
319 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
322 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
327 Result_t OpenWrite(const char*, ui32_t HeaderSize);
328 Result_t SetSourceStream(const AudioDescriptor&);
329 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
335 // Open the file for writing. The file must not exist. Returns error if
336 // the operation cannot be completed.
338 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
340 if ( ! m_State.Test_BEGIN() )
343 Result_t result = m_File.OpenWrite(filename);
345 if ( ASDCP_SUCCESS(result) )
347 m_HeaderSize = HeaderSize;
348 m_EssenceDescriptor = new WaveAudioDescriptor;
349 result = m_State.Goto_INIT();
356 // Automatically sets the MXF file's metadata from the WAV parser info.
358 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
360 if ( ! m_State.Test_INIT() )
363 if ( ADesc.SampleRate != EditRate_24
364 && ADesc.SampleRate != EditRate_48
365 && ADesc.SampleRate != EditRate_23_98 )
367 DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
368 ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
369 return RESULT_RAW_FORMAT;
372 if ( ADesc.AudioSamplingRate != SampleRate_48k )
374 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %d/%d\n",
375 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
376 return RESULT_RAW_FORMAT;
380 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
382 if ( ASDCP_SUCCESS(result) )
383 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
384 SOUND_DEF_LABEL, UL(Dict::ul(MDD_SoundDataDef)),
385 m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
387 if ( ASDCP_SUCCESS(result) )
389 memcpy(m_EssenceUL, Dict::ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
390 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
391 result = m_State.Goto_READY();
401 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
404 Result_t result = RESULT_OK;
406 if ( m_State.Test_READY() )
407 result = m_State.Goto_RUNNING(); // first time through
409 if ( ASDCP_SUCCESS(result) )
410 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
412 if ( ASDCP_SUCCESS(result) )
418 // Closes the MXF file, writing the index and other closing information.
421 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
423 if ( ! m_State.Test_RUNNING() )
426 m_State.Goto_FINAL();
428 return WriteMXFFooter();
432 //------------------------------------------------------------------------------------------
437 ASDCP::PCM::MXFWriter::MXFWriter()
441 ASDCP::PCM::MXFWriter::~MXFWriter()
446 // Open the file for writing. The file must not exist. Returns error if
447 // the operation cannot be completed.
449 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
450 const AudioDescriptor& ADesc, ui32_t HeaderSize)
452 m_Writer = new h__Writer;
454 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
456 if ( ASDCP_SUCCESS(result) )
458 m_Writer->m_Info = Info;
459 result = m_Writer->SetSourceStream(ADesc);
462 if ( ASDCP_FAILURE(result) )
468 // Writes a frame of essence to the MXF file. If the optional AESEncContext
469 // argument is present, the essence is encrypted prior to writing.
470 // Fails if the file is not open, is finalized, or an operating system
473 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
475 if ( m_Writer.empty() )
478 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
481 // Closes the MXF file, writing the index and other closing information.
483 ASDCP::PCM::MXFWriter::Finalize()
485 if ( m_Writer.empty() )
488 return m_Writer->Finalize();
492 // end AS_DCP_PCM.cpp