2 Copyright (c) 2004-2008, 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"
37 //------------------------------------------------------------------------------------------
39 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of wave audio";
40 static std::string SOUND_DEF_LABEL = "Sound Track";
44 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
46 ASDCP_TEST_NULL(ADescObj);
47 ADescObj->SampleRate = ADesc.SampleRate;
48 ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
49 ADescObj->Locked = ADesc.Locked;
50 ADescObj->ChannelCount = ADesc.ChannelCount;
51 ADescObj->QuantizationBits = ADesc.QuantizationBits;
52 ADescObj->BlockAlign = ADesc.BlockAlign;
53 ADescObj->AvgBps = ADesc.AvgBps;
54 ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
55 ADescObj->ContainerDuration = ADesc.ContainerDuration;
61 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
63 ASDCP_TEST_NULL(ADescObj);
64 ADesc.SampleRate = ADescObj->SampleRate;
65 ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
66 ADesc.Locked = ADescObj->Locked;
67 ADesc.ChannelCount = ADescObj->ChannelCount;
68 ADesc.QuantizationBits = ADescObj->QuantizationBits;
69 ADesc.BlockAlign = ADescObj->BlockAlign;
70 ADesc.AvgBps = ADescObj->AvgBps;
71 ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
72 assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
73 ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
79 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
81 strm << " SampleRate: " << ADesc.SampleRate.Numerator << "/" << ADesc.SampleRate.Denominator << std::endl;
82 strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
83 strm << " Locked: " << (unsigned) ADesc.Locked << std::endl;
84 strm << " ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
85 strm << " QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
86 strm << " BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
87 strm << " AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
88 strm << " LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
89 strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
96 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
103 AudioSamplingRate: %d/%d\n\
106 QuantizationBits: %u\n\
110 ContainerDuration: %u\n",
111 ADesc.SampleRate.Numerator ,ADesc.SampleRate.Denominator,
112 ADesc.AudioSamplingRate.Numerator ,ADesc.AudioSamplingRate.Denominator,
115 ADesc.QuantizationBits,
119 ADesc.ContainerDuration
127 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
129 ui32_t CBR_frame_size = 0;
131 if ( Info.EncryptedEssence )
137 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
138 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
142 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
145 return CBR_frame_size;
149 //------------------------------------------------------------------------------------------
152 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
154 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
157 AudioDescriptor m_ADesc;
161 Result_t OpenRead(const char*);
162 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
169 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
171 Result_t result = OpenMXFRead(filename);
173 if( ASDCP_SUCCESS(result) )
175 InterchangeObject* Object;
176 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
179 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
183 // check for sample/frame rate sanity
184 if ( ASDCP_SUCCESS(result)
185 && m_ADesc.SampleRate != EditRate_24
186 && m_ADesc.SampleRate != EditRate_48
187 && m_ADesc.SampleRate != EditRate_23_98 )
189 DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
190 m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
192 // oh, they gave us the audio sampling rate instead, assume 24/1
193 if ( m_ADesc.SampleRate == SampleRate_48k )
195 DefaultLogSink().Warn("adjusting SampleRate to 24/1\n");
196 m_ADesc.SampleRate.Numerator = 24;
197 m_ADesc.SampleRate.Denominator = 1;
201 // or we just drop the hammer
202 return RESULT_FORMAT;
206 if( ASDCP_SUCCESS(result) )
207 result = InitMXFIndex();
209 if( ASDCP_SUCCESS(result) )
212 // TODO: test file for sane CBR index BytesPerEditUnit
221 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
222 AESDecContext* Ctx, HMACContext* HMAC)
224 if ( ! m_File.IsOpen() )
227 return ReadEKLVFrame(FrameNum, FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
230 //------------------------------------------------------------------------------------------
235 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
240 fprintf(stream, "Frame: %06u, %7u bytes\n",
241 m_FrameNumber, m_Size);
244 Kumu::hexdump(m_Data, dump_len, stream);
247 //------------------------------------------------------------------------------------------
249 ASDCP::PCM::MXFReader::MXFReader()
251 m_Reader = new h__Reader;
255 ASDCP::PCM::MXFReader::~MXFReader()
257 if ( m_Reader && m_Reader->m_File.IsOpen() )
261 // Open the file for reading. The file must exist. Returns error if the
262 // operation cannot be completed.
264 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
266 return m_Reader->OpenRead(filename);
269 // Reads a frame of essence from the MXF file. If the optional AESEncContext
270 // argument is present, the essence is decrypted after reading. If the MXF
271 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
272 // will contain the ciphertext frame data.
274 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
275 AESDecContext* Ctx, HMACContext* HMAC) const
277 if ( m_Reader && m_Reader->m_File.IsOpen() )
278 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
284 // Fill the struct with the values from the file's header.
285 // Returns RESULT_INIT if the file is not open.
287 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
289 if ( m_Reader && m_Reader->m_File.IsOpen() )
291 ADesc = m_Reader->m_ADesc;
298 // Fill the struct with the values from the file's header.
299 // Returns RESULT_INIT if the file is not open.
301 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
303 if ( m_Reader && m_Reader->m_File.IsOpen() )
305 Info = m_Reader->m_Info;
314 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
316 if ( m_Reader && m_Reader->m_File.IsOpen() )
317 m_Reader->m_HeaderPart.Dump(stream);
323 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
325 if ( m_Reader->m_File.IsOpen() )
326 m_Reader->m_FooterPart.Dump(stream);
330 //------------------------------------------------------------------------------------------
333 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
336 AudioDescriptor m_ADesc;
337 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
340 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
343 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
348 Result_t OpenWrite(const char*, ui32_t HeaderSize);
349 Result_t SetSourceStream(const AudioDescriptor&);
350 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
356 // Open the file for writing. The file must not exist. Returns error if
357 // the operation cannot be completed.
359 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
361 if ( ! m_State.Test_BEGIN() )
364 Result_t result = m_File.OpenWrite(filename);
366 if ( ASDCP_SUCCESS(result) )
368 m_HeaderSize = HeaderSize;
369 m_EssenceDescriptor = new WaveAudioDescriptor;
370 result = m_State.Goto_INIT();
377 // Automatically sets the MXF file's metadata from the WAV parser info.
379 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
381 if ( ! m_State.Test_INIT() )
384 if ( ADesc.SampleRate != EditRate_24
385 && ADesc.SampleRate != EditRate_48
386 && ADesc.SampleRate != EditRate_23_98 )
388 DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
389 ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
390 return RESULT_RAW_FORMAT;
393 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
395 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
396 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
397 return RESULT_RAW_FORMAT;
401 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
403 if ( ASDCP_SUCCESS(result) )
404 result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
405 SOUND_DEF_LABEL, UL(Dict::ul(MDD_SoundDataDef)),
406 m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
408 if ( ASDCP_SUCCESS(result) )
410 memcpy(m_EssenceUL, Dict::ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
411 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
412 result = m_State.Goto_READY();
422 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
425 Result_t result = RESULT_OK;
427 if ( m_State.Test_READY() )
428 result = m_State.Goto_RUNNING(); // first time through
430 if ( ASDCP_SUCCESS(result) )
431 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
433 if ( ASDCP_SUCCESS(result) )
439 // Closes the MXF file, writing the index and other closing information.
442 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
444 if ( ! m_State.Test_RUNNING() )
447 m_State.Goto_FINAL();
449 return WriteMXFFooter();
453 //------------------------------------------------------------------------------------------
458 ASDCP::PCM::MXFWriter::MXFWriter()
462 ASDCP::PCM::MXFWriter::~MXFWriter()
467 // Open the file for writing. The file must not exist. Returns error if
468 // the operation cannot be completed.
470 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
471 const AudioDescriptor& ADesc, ui32_t HeaderSize)
473 m_Writer = new h__Writer;
474 m_Writer->m_Info = Info;
476 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
478 if ( ASDCP_SUCCESS(result) )
479 result = m_Writer->SetSourceStream(ADesc);
481 if ( ASDCP_FAILURE(result) )
487 // Writes a frame of essence to the MXF file. If the optional AESEncContext
488 // argument is present, the essence is encrypted prior to writing.
489 // Fails if the file is not open, is finalized, or an operating system
492 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
494 if ( m_Writer.empty() )
497 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
500 // Closes the MXF file, writing the index and other closing information.
502 ASDCP::PCM::MXFWriter::Finalize()
504 if ( m_Writer.empty() )
507 return m_Writer->Finalize();
511 // end AS_DCP_PCM.cpp