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)
81 SampleRate: %lu/%lu\n\
82 AudioSamplingRate: %lu/%lu\n\
85 QuantizationBits: %lu\n\
89 ContainerDuration: %lu\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 ( m_ADesc.SampleRate != EditRate_24
164 && m_ADesc.SampleRate != EditRate_48
165 && m_ADesc.SampleRate != EditRate_23_98 )
167 DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
168 m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
170 // oh, they gave us the audio sampling rate instead, assume 24/1
171 if ( m_ADesc.SampleRate == SampleRate_48k )
173 DefaultLogSink().Warn("adjusting SampleRate to 24/1\n");
174 m_ADesc.SampleRate.Numerator = 24;
175 m_ADesc.SampleRate.Denominator = 1;
179 // or we just drop the hammer
180 return RESULT_FORMAT;
184 if( ASDCP_SUCCESS(result) )
185 result = InitMXFIndex();
187 if( ASDCP_SUCCESS(result) )
190 // TODO: test file for sane CBR index BytesPerEditUnit
199 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
200 AESDecContext* Ctx, HMACContext* HMAC)
202 if ( ! m_File.IsOpen() )
205 return ReadEKLVPacket(FrameNum, FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
208 //------------------------------------------------------------------------------------------
213 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
218 fprintf(stream, "Frame: %06lu, %7lu bytes\n",
219 m_FrameNumber, m_Size);
222 hexdump(m_Data, dump_len, stream);
225 //------------------------------------------------------------------------------------------
227 ASDCP::PCM::MXFReader::MXFReader()
229 m_Reader = new h__Reader;
233 ASDCP::PCM::MXFReader::~MXFReader()
235 if ( m_Reader && m_Reader->m_File.IsOpen() )
239 // Open the file for reading. The file must exist. Returns error if the
240 // operation cannot be completed.
242 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
244 return m_Reader->OpenRead(filename);
247 // Reads a frame of essence from the MXF file. If the optional AESEncContext
248 // argument is present, the essence is decrypted after reading. If the MXF
249 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
250 // will contain the ciphertext frame data.
252 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
253 AESDecContext* Ctx, HMACContext* HMAC) const
255 if ( m_Reader && m_Reader->m_File.IsOpen() )
256 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
262 // Fill the struct with the values from the file's header.
263 // Returns RESULT_INIT if the file is not open.
265 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
267 if ( m_Reader && m_Reader->m_File.IsOpen() )
269 ADesc = m_Reader->m_ADesc;
276 // Fill the struct with the values from the file's header.
277 // Returns RESULT_INIT if the file is not open.
279 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
281 if ( m_Reader && m_Reader->m_File.IsOpen() )
283 Info = m_Reader->m_Info;
292 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
294 if ( m_Reader && m_Reader->m_File.IsOpen() )
295 m_Reader->m_HeaderPart.Dump(stream);
301 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
303 if ( m_Reader->m_File.IsOpen() )
304 m_Reader->m_FooterPart.Dump(stream);
308 //------------------------------------------------------------------------------------------
311 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
314 AudioDescriptor m_ADesc;
316 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
321 Result_t OpenWrite(const char*, ui32_t HeaderSize);
322 Result_t SetSourceStream(const AudioDescriptor&);
323 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
329 // Open the file for writing. The file must not exist. Returns error if
330 // the operation cannot be completed.
332 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
334 if ( ! m_State.Test_BEGIN() )
337 Result_t result = m_File.OpenWrite(filename);
339 if ( ASDCP_SUCCESS(result) )
341 m_HeaderSize = HeaderSize;
342 m_EssenceDescriptor = new WaveAudioDescriptor;
343 result = m_State.Goto_INIT();
350 // Automatically sets the MXF file's metadata from the WAV parser info.
352 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
354 if ( ! m_State.Test_INIT() )
357 if ( ADesc.SampleRate != EditRate_24
358 && ADesc.SampleRate != EditRate_48
359 && ADesc.SampleRate != EditRate_23_98 )
361 DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %lu/%lu\n",
362 ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
363 return RESULT_RAW_FORMAT;
366 if ( ADesc.AudioSamplingRate != SampleRate_48k )
368 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %lu/%lu\n",
369 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
370 return RESULT_RAW_FORMAT;
374 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
376 if ( ASDCP_SUCCESS(result) )
377 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
378 SOUND_DEF_LABEL, UL(Dict::ul(MDD_SoundDataDef)),
379 m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
381 if ( ASDCP_SUCCESS(result) )
382 result = m_State.Goto_READY();
391 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
394 Result_t result = RESULT_OK;
396 if ( m_State.Test_READY() )
397 result = m_State.Goto_RUNNING(); // first time through
399 if ( ASDCP_SUCCESS(result) )
400 result = WriteEKLVPacket(FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
402 if ( ASDCP_SUCCESS(result) )
408 // Closes the MXF file, writing the index and other closing information.
411 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
413 if ( ! m_State.Test_RUNNING() )
416 m_State.Goto_FINAL();
418 return WriteMXFFooter();
422 //------------------------------------------------------------------------------------------
427 ASDCP::PCM::MXFWriter::MXFWriter()
431 ASDCP::PCM::MXFWriter::~MXFWriter()
436 // Open the file for writing. The file must not exist. Returns error if
437 // the operation cannot be completed.
439 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
440 const AudioDescriptor& ADesc, ui32_t HeaderSize)
442 m_Writer = new h__Writer;
444 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
446 if ( ASDCP_SUCCESS(result) )
448 m_Writer->m_Info = Info;
449 result = m_Writer->SetSourceStream(ADesc);
452 if ( ASDCP_FAILURE(result) )
458 // Writes a frame of essence to the MXF file. If the optional AESEncContext
459 // argument is present, the essence is encrypted prior to writing.
460 // Fails if the file is not open, is finalized, or an operating system
463 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
465 if ( m_Writer.empty() )
468 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
471 // Closes the MXF file, writing the index and other closing information.
473 ASDCP::PCM::MXFWriter::Finalize()
475 if ( m_Writer.empty() )
478 return m_Writer->Finalize();
482 // end AS_DCP_PCM.cpp