2 Copyright (c) 2004-2005, 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"
38 //------------------------------------------------------------------------------------------
42 ASDCP::MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
44 ASDCP_TEST_NULL(ADescObj);
45 ADesc.SampleRate = ADescObj->SampleRate;
46 ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
47 ADesc.Locked = ADescObj->Locked;
48 ADesc.ChannelCount = ADescObj->ChannelCount;
49 ADesc.QuantizationBits = ADescObj->QuantizationBits;
50 ADesc.BlockAlign = ADescObj->BlockAlign;
51 ADesc.AvgBps = ADescObj->AvgBps;
52 ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
53 ADesc.ContainerDuration = ADescObj->ContainerDuration;
58 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
64 SampleRate: %lu/%lu\n\
65 AudioSamplingRate: %lu/%lu\n\
68 QuantizationBits: %lu\n\
72 ContainerDuration: %lu\n",
73 ADesc.SampleRate.Numerator ,ADesc.SampleRate.Denominator,
74 ADesc.AudioSamplingRate.Numerator ,ADesc.AudioSamplingRate.Denominator,
77 ADesc.QuantizationBits,
81 ADesc.ContainerDuration
89 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
91 ui32_t CBR_frame_size = 0;
93 if ( Info.EncryptedEssence )
99 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
100 + ( Info.UsesHMAC ? klv_intpack_size : (klv_length_size * 3) );
104 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + klv_key_size + klv_length_size;
107 return CBR_frame_size;
111 //------------------------------------------------------------------------------------------
114 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
116 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
119 AudioDescriptor m_ADesc;
123 Result_t OpenRead(const char*);
124 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
131 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
133 Result_t result = OpenMXFRead(filename);
135 if( ASDCP_SUCCESS(result) )
137 InterchangeObject* Object;
138 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
141 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
145 // check for sample/frame rate sanity
146 if ( m_ADesc.SampleRate != EditRate_24
147 && m_ADesc.SampleRate != EditRate_48
148 && m_ADesc.SampleRate != EditRate_23_98 )
150 DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
151 m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
153 // oh, they gave us the audio sampling rate instead, assume 24/1
154 if ( m_ADesc.SampleRate == SampleRate_48k )
156 DefaultLogSink().Warn("adjusting SampleRate to 24/1\n");
157 m_ADesc.SampleRate.Numerator = 24;
158 m_ADesc.SampleRate.Denominator = 1;
162 // or we just drop the hammer
163 return RESULT_FORMAT;
167 if( ASDCP_SUCCESS(result) )
168 result = InitMXFIndex();
170 if( ASDCP_SUCCESS(result) )
171 result = InitInfo(m_Info);
173 // TODO: test file for sane CBR index BytesPerEditUnit
182 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
183 AESDecContext* Ctx, HMACContext* HMAC)
185 if ( ! m_File.IsOpen() )
188 return ReadEKLVPacket(FrameNum, FrameBuf, WAVEssenceUL_Data, Ctx, HMAC);
191 //------------------------------------------------------------------------------------------
196 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
201 fprintf(stream, "Frame: %06lu, %7lu bytes\n",
202 m_FrameNumber, m_Size);
205 hexdump(m_Data, dump_len, stream);
208 //------------------------------------------------------------------------------------------
210 ASDCP::PCM::MXFReader::MXFReader()
212 m_Reader = new h__Reader;
216 ASDCP::PCM::MXFReader::~MXFReader()
218 if ( m_Reader && m_Reader->m_File.IsOpen() )
222 // Open the file for reading. The file must exist. Returns error if the
223 // operation cannot be completed.
225 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
227 return m_Reader->OpenRead(filename);
230 // Reads a frame of essence from the MXF file. If the optional AESEncContext
231 // argument is present, the essence is decrypted after reading. If the MXF
232 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
233 // will contain the ciphertext frame data.
235 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
236 AESDecContext* Ctx, HMACContext* HMAC) const
238 if ( m_Reader && m_Reader->m_File.IsOpen() )
239 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
245 // Fill the struct with the values from the file's header.
246 // Returns RESULT_INIT if the file is not open.
248 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
250 if ( m_Reader && m_Reader->m_File.IsOpen() )
252 ADesc = m_Reader->m_ADesc;
259 // Fill the struct with the values from the file's header.
260 // Returns RESULT_INIT if the file is not open.
262 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
264 if ( m_Reader && m_Reader->m_File.IsOpen() )
266 Info = m_Reader->m_Info;
275 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
277 if ( m_Reader && m_Reader->m_File.IsOpen() )
278 m_Reader->m_HeaderPart.Dump(stream);
284 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
286 if ( m_Reader->m_File.IsOpen() )
287 m_Reader->m_FooterPart.Dump(stream);
291 //------------------------------------------------------------------------------------------
295 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
298 AudioDescriptor m_ADesc;
300 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
305 Result_t OpenWrite(const char*, ui32_t HeaderSize);
306 Result_t SetSourceStream(const AudioDescriptor&);
307 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
313 // Open the file for writing. The file must not exist. Returns error if
314 // the operation cannot be completed.
316 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
318 if ( ! m_State.Test_BEGIN() )
321 m_File = new MXFFile;
323 Result_t result = m_File.OpenWrite(filename);
325 if ( ASDCP_SUCCESS(result) )
327 m_EssenceDescriptor = new MDObject("WaveAudioDescriptor");
328 result = m_State.Goto_INIT();
335 // Automatically sets the MXF file's metadata from the WAV parser info.
337 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
339 if ( ! m_State.Test_INIT() )
342 if ( ADesc.SampleRate != EditRate_24
343 && ADesc.SampleRate != EditRate_48
344 && ADesc.SampleRate != EditRate_23_98 )
346 DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %lu/%lu\n",
347 ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
348 return RESULT_RAW_FORMAT;
351 if ( ADesc.AudioSamplingRate != SampleRate_48k )
353 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %lu/%lu\n",
354 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
355 return RESULT_RAW_FORMAT;
359 Result_t result = PCM_ADesc_to_MD(m_ADesc, *m_EssenceDescriptor);
361 if ( ASDCP_SUCCESS(result) )
363 result = WriteMXFHeader(ESS_PCM_24b_48k, m_ADesc.SampleRate,
364 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
367 if ( ASDCP_SUCCESS(result) )
368 result = m_State.Goto_READY();
377 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
380 Result_t result = RESULT_OK;
382 if ( m_State.Test_READY() )
383 result = m_State.Goto_RUNNING(); // first time through
385 if ( ASDCP_SUCCESS(result) )
386 result = WriteEKLVPacket(FrameBuf, WAVEssenceUL_Data, Ctx, HMAC);
388 if ( ASDCP_SUCCESS(result) )
394 // Closes the MXF file, writing the index and other closing information.
397 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
399 if ( ! m_State.Test_RUNNING() )
405 m_State.Goto_FINAL();
407 return WriteMXFFooter(ESS_PCM_24b_48k);
411 //------------------------------------------------------------------------------------------
416 ASDCP::PCM::MXFWriter::MXFWriter()
420 ASDCP::PCM::MXFWriter::~MXFWriter()
425 // Open the file for writing. The file must not exist. Returns error if
426 // the operation cannot be completed.
428 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
429 const AudioDescriptor& ADesc, ui32_t HeaderSize)
431 m_Writer = new h__Writer;
433 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
435 if ( ASDCP_SUCCESS(result) )
437 m_Writer->m_Info = Info;
438 result = m_Writer->SetSourceStream(ADesc);
441 if ( ASDCP_FAILURE(result) )
447 // Writes a frame of essence to the MXF file. If the optional AESEncContext
448 // argument is present, the essence is encrypted prior to writing.
449 // Fails if the file is not open, is finalized, or an operating system
452 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
454 if ( m_Writer.empty() )
457 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
460 // Closes the MXF file, writing the index and other closing information.
462 ASDCP::PCM::MXFWriter::Finalize()
464 if ( m_Writer.empty() )
467 return m_Writer->Finalize();
473 // end AS_DCP_PCM.cpp