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 assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
71 ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
76 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
83 AudioSamplingRate: %d/%d\n\
86 QuantizationBits: %u\n\
90 ContainerDuration: %u\n",
91 ADesc.SampleRate.Numerator ,ADesc.SampleRate.Denominator,
92 ADesc.AudioSamplingRate.Numerator ,ADesc.AudioSamplingRate.Denominator,
95 ADesc.QuantizationBits,
99 ADesc.ContainerDuration
107 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
109 ui32_t CBR_frame_size = 0;
111 if ( Info.EncryptedEssence )
117 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
118 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
122 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
125 return CBR_frame_size;
129 //------------------------------------------------------------------------------------------
132 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
134 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
137 AudioDescriptor m_ADesc;
141 Result_t OpenRead(const char*);
142 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
149 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
151 Result_t result = OpenMXFRead(filename);
153 if( ASDCP_SUCCESS(result) )
155 InterchangeObject* Object;
156 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
159 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
163 // check for sample/frame rate sanity
164 if ( ASDCP_SUCCESS(result)
165 && m_ADesc.SampleRate != EditRate_24
166 && m_ADesc.SampleRate != EditRate_48
167 && m_ADesc.SampleRate != EditRate_23_98 )
169 DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
170 m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
172 // oh, they gave us the audio sampling rate instead, assume 24/1
173 if ( m_ADesc.SampleRate == SampleRate_48k )
175 DefaultLogSink().Warn("adjusting SampleRate to 24/1\n");
176 m_ADesc.SampleRate.Numerator = 24;
177 m_ADesc.SampleRate.Denominator = 1;
181 // or we just drop the hammer
182 return RESULT_FORMAT;
186 if( ASDCP_SUCCESS(result) )
187 result = InitMXFIndex();
189 if( ASDCP_SUCCESS(result) )
192 // TODO: test file for sane CBR index BytesPerEditUnit
201 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
202 AESDecContext* Ctx, HMACContext* HMAC)
204 if ( ! m_File.IsOpen() )
207 return ReadEKLVFrame(FrameNum, FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
210 //------------------------------------------------------------------------------------------
215 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
220 fprintf(stream, "Frame: %06u, %7u bytes\n",
221 m_FrameNumber, m_Size);
224 Kumu::hexdump(m_Data, dump_len, stream);
227 //------------------------------------------------------------------------------------------
229 ASDCP::PCM::MXFReader::MXFReader()
231 m_Reader = new h__Reader;
235 ASDCP::PCM::MXFReader::~MXFReader()
237 if ( m_Reader && m_Reader->m_File.IsOpen() )
241 // Open the file for reading. The file must exist. Returns error if the
242 // operation cannot be completed.
244 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
246 return m_Reader->OpenRead(filename);
249 // Reads a frame of essence from the MXF file. If the optional AESEncContext
250 // argument is present, the essence is decrypted after reading. If the MXF
251 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
252 // will contain the ciphertext frame data.
254 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
255 AESDecContext* Ctx, HMACContext* HMAC) const
257 if ( m_Reader && m_Reader->m_File.IsOpen() )
258 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
264 // Fill the struct with the values from the file's header.
265 // Returns RESULT_INIT if the file is not open.
267 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
269 if ( m_Reader && m_Reader->m_File.IsOpen() )
271 ADesc = m_Reader->m_ADesc;
278 // Fill the struct with the values from the file's header.
279 // Returns RESULT_INIT if the file is not open.
281 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
283 if ( m_Reader && m_Reader->m_File.IsOpen() )
285 Info = m_Reader->m_Info;
294 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
296 if ( m_Reader && m_Reader->m_File.IsOpen() )
297 m_Reader->m_HeaderPart.Dump(stream);
303 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
305 if ( m_Reader->m_File.IsOpen() )
306 m_Reader->m_FooterPart.Dump(stream);
310 //------------------------------------------------------------------------------------------
313 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
316 AudioDescriptor m_ADesc;
317 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
320 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
323 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
328 Result_t OpenWrite(const char*, ui32_t HeaderSize);
329 Result_t SetSourceStream(const AudioDescriptor&);
330 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
336 // Open the file for writing. The file must not exist. Returns error if
337 // the operation cannot be completed.
339 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
341 if ( ! m_State.Test_BEGIN() )
344 Result_t result = m_File.OpenWrite(filename);
346 if ( ASDCP_SUCCESS(result) )
348 m_HeaderSize = HeaderSize;
349 m_EssenceDescriptor = new WaveAudioDescriptor;
350 result = m_State.Goto_INIT();
357 // Automatically sets the MXF file's metadata from the WAV parser info.
359 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
361 if ( ! m_State.Test_INIT() )
364 if ( ADesc.SampleRate != EditRate_24
365 && ADesc.SampleRate != EditRate_48
366 && ADesc.SampleRate != EditRate_23_98 )
368 DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
369 ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
370 return RESULT_RAW_FORMAT;
373 if ( ADesc.AudioSamplingRate != SampleRate_48k )
375 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %d/%d\n",
376 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
377 return RESULT_RAW_FORMAT;
381 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
383 if ( ASDCP_SUCCESS(result) )
384 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
385 SOUND_DEF_LABEL, UL(Dict::ul(MDD_SoundDataDef)),
386 m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
388 if ( ASDCP_SUCCESS(result) )
390 memcpy(m_EssenceUL, Dict::ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
391 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
392 result = m_State.Goto_READY();
402 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
405 Result_t result = RESULT_OK;
407 if ( m_State.Test_READY() )
408 result = m_State.Goto_RUNNING(); // first time through
410 if ( ASDCP_SUCCESS(result) )
411 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
413 if ( ASDCP_SUCCESS(result) )
419 // Closes the MXF file, writing the index and other closing information.
422 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
424 if ( ! m_State.Test_RUNNING() )
427 m_State.Goto_FINAL();
429 return WriteMXFFooter();
433 //------------------------------------------------------------------------------------------
438 ASDCP::PCM::MXFWriter::MXFWriter()
442 ASDCP::PCM::MXFWriter::~MXFWriter()
447 // Open the file for writing. The file must not exist. Returns error if
448 // the operation cannot be completed.
450 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
451 const AudioDescriptor& ADesc, ui32_t HeaderSize)
453 m_Writer = new h__Writer;
454 m_Writer->m_Info = Info;
456 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
458 if ( ASDCP_SUCCESS(result) )
459 result = m_Writer->SetSourceStream(ADesc);
461 if ( ASDCP_FAILURE(result) )
467 // Writes a frame of essence to the MXF file. If the optional AESEncContext
468 // argument is present, the essence is encrypted prior to writing.
469 // Fails if the file is not open, is finalized, or an operating system
472 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
474 if ( m_Writer.empty() )
477 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
480 // Closes the MXF file, writing the index and other closing information.
482 ASDCP::PCM::MXFWriter::Finalize()
484 if ( m_Writer.empty() )
487 return m_Writer->Finalize();
491 // end AS_DCP_PCM.cpp