2 Copyright (c) 2004-2013, 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 ASDCP::PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
46 ASDCP_TEST_NULL(ADescObj);
47 ADescObj->SampleRate = ADesc.EditRate;
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;
57 ADescObj->ChannelAssignment.Reset();
59 switch ( ADesc.ChannelFormat )
62 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul;
66 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul;
70 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul;
74 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul;
78 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul;
87 ASDCP::MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
89 ASDCP_TEST_NULL(ADescObj);
90 ADesc.EditRate = ADescObj->SampleRate;
91 ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
92 ADesc.Locked = ADescObj->Locked;
93 ADesc.ChannelCount = ADescObj->ChannelCount;
94 ADesc.QuantizationBits = ADescObj->QuantizationBits;
95 ADesc.BlockAlign = ADescObj->BlockAlign;
96 ADesc.AvgBps = ADescObj->AvgBps;
97 ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
98 assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
99 ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
101 ADesc.ChannelFormat = PCM::CF_NONE;
103 if ( ADescObj->ChannelAssignment.HasValue() )
105 if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul )
106 ADesc.ChannelFormat = PCM::CF_CFG_1;
108 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul )
109 ADesc.ChannelFormat = PCM::CF_CFG_2;
111 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul )
112 ADesc.ChannelFormat = PCM::CF_CFG_3;
114 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul )
115 ADesc.ChannelFormat = PCM::CF_CFG_4;
117 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul )
118 ADesc.ChannelFormat = PCM::CF_CFG_5;
126 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
128 strm << " SampleRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
129 strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
130 strm << " Locked: " << (unsigned) ADesc.Locked << std::endl;
131 strm << " ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
132 strm << " QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
133 strm << " BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
134 strm << " AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
135 strm << " LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
136 strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
137 strm << " ChannelFormat: ";
138 switch (ADesc.ChannelFormat)
142 strm << "No Channel Format";
146 strm << "Config 1 (5.1 with optional HI/VI)";
150 strm << "Config 2 (5.1 + center surround with optional HI/VI)";
154 strm << "Config 3 (7.1 with optional HI/VI)";
162 strm << "Config 5 (7.1 DS with optional HI/VI)";
172 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
179 AudioSamplingRate: %d/%d\n\
182 QuantizationBits: %u\n\
186 ContainerDuration: %u\n\
187 ChannelFormat: %u\n",
188 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
189 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
192 ADesc.QuantizationBits,
196 ADesc.ContainerDuration,
205 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
207 ui32_t CBR_frame_size = 0;
209 if ( Info.EncryptedEssence )
215 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
216 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
220 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
223 return CBR_frame_size;
227 //------------------------------------------------------------------------------------------
230 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
232 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
236 AudioDescriptor m_ADesc;
238 h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d) {}
239 virtual ~h__Reader() {}
240 Result_t OpenRead(const char*);
241 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
248 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
250 Result_t result = OpenMXFRead(filename);
252 if( ASDCP_SUCCESS(result) )
254 InterchangeObject* Object = 0
256 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
260 DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
261 return RESULT_FORMAT;
264 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
268 // check for sample/frame rate sanity
269 if ( ASDCP_SUCCESS(result)
270 && m_ADesc.EditRate != EditRate_24
271 && m_ADesc.EditRate != EditRate_25
272 && m_ADesc.EditRate != EditRate_30
273 && m_ADesc.EditRate != EditRate_48
274 && m_ADesc.EditRate != EditRate_50
275 && m_ADesc.EditRate != EditRate_60
276 && m_ADesc.EditRate != EditRate_96
277 && m_ADesc.EditRate != EditRate_100
278 && m_ADesc.EditRate != EditRate_120
279 && m_ADesc.EditRate != EditRate_16
280 && m_ADesc.EditRate != EditRate_18
281 && m_ADesc.EditRate != EditRate_20
282 && m_ADesc.EditRate != EditRate_22
283 && m_ADesc.EditRate != EditRate_23_98 )
285 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
286 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
288 // oh, they gave us the audio sampling rate instead, assume 24/1
289 if ( m_ADesc.EditRate == SampleRate_48k )
291 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
292 m_ADesc.EditRate = EditRate_24;
296 DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
297 // or we just drop the hammer
298 return RESULT_FORMAT;
302 // TODO: test file for sane CBR index BytesPerEditUnit
311 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
312 AESDecContext* Ctx, HMACContext* HMAC)
314 if ( ! m_File.IsOpen() )
318 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
321 //------------------------------------------------------------------------------------------
326 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
331 fprintf(stream, "Frame: %06u, %7u bytes\n",
332 m_FrameNumber, m_Size);
335 Kumu::hexdump(m_Data, dump_len, stream);
338 //------------------------------------------------------------------------------------------
340 ASDCP::PCM::MXFReader::MXFReader()
342 m_Reader = new h__Reader(DefaultCompositeDict());
346 ASDCP::PCM::MXFReader::~MXFReader()
348 if ( m_Reader && m_Reader->m_File.IsOpen() )
352 // Warning: direct manipulation of MXF structures can interfere
353 // with the normal operation of the wrapper. Caveat emptor!
355 ASDCP::MXF::OP1aHeader&
356 ASDCP::PCM::MXFReader::OP1aHeader()
358 if ( m_Reader.empty() )
360 assert(g_OP1aHeader);
361 return *g_OP1aHeader;
364 return m_Reader->m_HeaderPart;
367 // Warning: direct manipulation of MXF structures can interfere
368 // with the normal operation of the wrapper. Caveat emptor!
370 ASDCP::MXF::OPAtomIndexFooter&
371 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
373 if ( m_Reader.empty() )
375 assert(g_OPAtomIndexFooter);
376 return *g_OPAtomIndexFooter;
379 return m_Reader->m_IndexAccess;
382 // Warning: direct manipulation of MXF structures can interfere
383 // with the normal operation of the wrapper. Caveat emptor!
386 ASDCP::PCM::MXFReader::RIP()
388 if ( m_Reader.empty() )
394 return m_Reader->m_RIP;
397 // Open the file for reading. The file must exist. Returns error if the
398 // operation cannot be completed.
400 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
402 return m_Reader->OpenRead(filename);
405 // Reads a frame of essence from the MXF file. If the optional AESEncContext
406 // argument is present, the essence is decrypted after reading. If the MXF
407 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
408 // will contain the ciphertext frame data.
410 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
411 AESDecContext* Ctx, HMACContext* HMAC) const
413 if ( m_Reader && m_Reader->m_File.IsOpen() )
414 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
421 ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
423 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
426 // Fill the struct with the values from the file's header.
427 // Returns RESULT_INIT if the file is not open.
429 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
431 if ( m_Reader && m_Reader->m_File.IsOpen() )
433 ADesc = m_Reader->m_ADesc;
440 // Fill the struct with the values from the file's header.
441 // Returns RESULT_INIT if the file is not open.
443 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
445 if ( m_Reader && m_Reader->m_File.IsOpen() )
447 Info = m_Reader->m_Info;
456 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
458 if ( m_Reader && m_Reader->m_File.IsOpen() )
459 m_Reader->m_HeaderPart.Dump(stream);
465 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
467 if ( m_Reader->m_File.IsOpen() )
468 m_Reader->m_IndexAccess.Dump(stream);
473 ASDCP::PCM::MXFReader::Close() const
475 if ( m_Reader && m_Reader->m_File.IsOpen() )
485 //------------------------------------------------------------------------------------------
488 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
490 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
494 AudioDescriptor m_ADesc;
495 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
497 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d) {
498 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
501 virtual ~h__Writer(){}
503 Result_t OpenWrite(const char*, ui32_t HeaderSize);
504 Result_t SetSourceStream(const AudioDescriptor&);
505 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
511 // Open the file for writing. The file must not exist. Returns error if
512 // the operation cannot be completed.
514 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
516 if ( ! m_State.Test_BEGIN() )
519 Result_t result = m_File.OpenWrite(filename);
521 if ( ASDCP_SUCCESS(result) )
523 m_HeaderSize = HeaderSize;
524 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
525 result = m_State.Goto_INIT();
532 // Automatically sets the MXF file's metadata from the WAV parser info.
534 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
536 if ( ! m_State.Test_INIT() )
539 if ( ADesc.EditRate != EditRate_24
540 && ADesc.EditRate != EditRate_25
541 && ADesc.EditRate != EditRate_30
542 && ADesc.EditRate != EditRate_48
543 && ADesc.EditRate != EditRate_50
544 && ADesc.EditRate != EditRate_60
545 && ADesc.EditRate != EditRate_96
546 && ADesc.EditRate != EditRate_100
547 && ADesc.EditRate != EditRate_120
548 && ADesc.EditRate != EditRate_16
549 && ADesc.EditRate != EditRate_18
550 && ADesc.EditRate != EditRate_20
551 && ADesc.EditRate != EditRate_22
552 && ADesc.EditRate != EditRate_23_98 )
554 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
555 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
556 return RESULT_RAW_FORMAT;
559 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
561 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
562 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
563 return RESULT_RAW_FORMAT;
569 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
571 if ( ASDCP_SUCCESS(result) )
573 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
574 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
575 result = m_State.Goto_READY();
578 if ( ASDCP_SUCCESS(result) )
580 result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
581 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
582 m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
583 calc_CBR_frame_size(m_Info, m_ADesc));
593 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
596 Result_t result = RESULT_OK;
598 if ( m_State.Test_READY() )
599 result = m_State.Goto_RUNNING(); // first time through
601 if ( ASDCP_SUCCESS(result) )
602 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
604 if ( ASDCP_SUCCESS(result) )
610 // Closes the MXF file, writing the index and other closing information.
613 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
615 if ( ! m_State.Test_RUNNING() )
618 m_State.Goto_FINAL();
620 return WriteASDCPFooter();
624 //------------------------------------------------------------------------------------------
629 ASDCP::PCM::MXFWriter::MXFWriter()
633 ASDCP::PCM::MXFWriter::~MXFWriter()
637 // Warning: direct manipulation of MXF structures can interfere
638 // with the normal operation of the wrapper. Caveat emptor!
640 ASDCP::MXF::OP1aHeader&
641 ASDCP::PCM::MXFWriter::OP1aHeader()
643 if ( m_Writer.empty() )
645 assert(g_OP1aHeader);
646 return *g_OP1aHeader;
649 return m_Writer->m_HeaderPart;
652 // Warning: direct manipulation of MXF structures can interfere
653 // with the normal operation of the wrapper. Caveat emptor!
655 ASDCP::MXF::OPAtomIndexFooter&
656 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
658 if ( m_Writer.empty() )
660 assert(g_OPAtomIndexFooter);
661 return *g_OPAtomIndexFooter;
664 return m_Writer->m_FooterPart;
667 // Warning: direct manipulation of MXF structures can interfere
668 // with the normal operation of the wrapper. Caveat emptor!
671 ASDCP::PCM::MXFWriter::RIP()
673 if ( m_Writer.empty() )
679 return m_Writer->m_RIP;
682 // Open the file for writing. The file must not exist. Returns error if
683 // the operation cannot be completed.
685 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
686 const AudioDescriptor& ADesc, ui32_t HeaderSize)
688 if ( Info.LabelSetType == LS_MXF_SMPTE )
689 m_Writer = new h__Writer(DefaultSMPTEDict());
691 m_Writer = new h__Writer(DefaultInteropDict());
693 m_Writer->m_Info = Info;
695 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
697 if ( ASDCP_SUCCESS(result) )
698 result = m_Writer->SetSourceStream(ADesc);
700 if ( ASDCP_FAILURE(result) )
706 // Writes a frame of essence to the MXF file. If the optional AESEncContext
707 // argument is present, the essence is encrypted prior to writing.
708 // Fails if the file is not open, is finalized, or an operating system
711 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
713 if ( m_Writer.empty() )
716 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
719 // Closes the MXF file, writing the index and other closing information.
721 ASDCP::PCM::MXFWriter::Finalize()
723 if ( m_Writer.empty() )
726 return m_Writer->Finalize();
730 // end AS_DCP_PCM.cpp