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 ( 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 Kumu::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;
315 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
318 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
321 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
326 Result_t OpenWrite(const char*, ui32_t HeaderSize);
327 Result_t SetSourceStream(const AudioDescriptor&);
328 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
334 // Open the file for writing. The file must not exist. Returns error if
335 // the operation cannot be completed.
337 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
339 if ( ! m_State.Test_BEGIN() )
342 Result_t result = m_File.OpenWrite(filename);
344 if ( ASDCP_SUCCESS(result) )
346 m_HeaderSize = HeaderSize;
347 m_EssenceDescriptor = new WaveAudioDescriptor;
348 result = m_State.Goto_INIT();
355 // Automatically sets the MXF file's metadata from the WAV parser info.
357 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
359 if ( ! m_State.Test_INIT() )
362 if ( ADesc.SampleRate != EditRate_24
363 && ADesc.SampleRate != EditRate_48
364 && ADesc.SampleRate != EditRate_23_98 )
366 DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
367 ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
368 return RESULT_RAW_FORMAT;
371 if ( ADesc.AudioSamplingRate != SampleRate_48k )
373 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %d/%d\n",
374 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
375 return RESULT_RAW_FORMAT;
379 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
381 if ( ASDCP_SUCCESS(result) )
382 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
383 SOUND_DEF_LABEL, UL(Dict::ul(MDD_SoundDataDef)),
384 m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
386 if ( ASDCP_SUCCESS(result) )
388 memcpy(m_EssenceUL, Dict::ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
389 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
390 result = m_State.Goto_READY();
400 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
403 Result_t result = RESULT_OK;
405 if ( m_State.Test_READY() )
406 result = m_State.Goto_RUNNING(); // first time through
408 if ( ASDCP_SUCCESS(result) )
409 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
411 if ( ASDCP_SUCCESS(result) )
417 // Closes the MXF file, writing the index and other closing information.
420 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
422 if ( ! m_State.Test_RUNNING() )
425 m_State.Goto_FINAL();
427 return WriteMXFFooter();
431 //------------------------------------------------------------------------------------------
436 ASDCP::PCM::MXFWriter::MXFWriter()
440 ASDCP::PCM::MXFWriter::~MXFWriter()
445 // Open the file for writing. The file must not exist. Returns error if
446 // the operation cannot be completed.
448 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
449 const AudioDescriptor& ADesc, ui32_t HeaderSize)
451 m_Writer = new h__Writer;
453 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
455 if ( ASDCP_SUCCESS(result) )
457 m_Writer->m_Info = Info;
458 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