2 Copyright (c) 2004-2016, 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.get().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;
82 ADescObj->ChannelAssignment = DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_MCA).ul;
91 ASDCP::MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
93 ASDCP_TEST_NULL(ADescObj);
94 ADesc.EditRate = ADescObj->SampleRate;
95 ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
96 ADesc.Locked = ADescObj->Locked;
97 ADesc.ChannelCount = ADescObj->ChannelCount;
98 ADesc.QuantizationBits = ADescObj->QuantizationBits;
99 ADesc.BlockAlign = ADescObj->BlockAlign;
100 ADesc.AvgBps = ADescObj->AvgBps;
101 ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
102 assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
103 ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
105 ADesc.ChannelFormat = PCM::CF_NONE;
107 if ( ! ADescObj->ChannelAssignment.empty() )
109 if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul )
110 ADesc.ChannelFormat = PCM::CF_CFG_1;
112 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul )
113 ADesc.ChannelFormat = PCM::CF_CFG_2;
115 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul )
116 ADesc.ChannelFormat = PCM::CF_CFG_3;
118 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul )
119 ADesc.ChannelFormat = PCM::CF_CFG_4;
121 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul )
122 ADesc.ChannelFormat = PCM::CF_CFG_5;
124 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_MCA).ul )
125 ADesc.ChannelFormat = PCM::CF_CFG_6;
133 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
135 strm << " SampleRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
136 strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
137 strm << " Locked: " << (unsigned) ADesc.Locked << std::endl;
138 strm << " ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
139 strm << " QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
140 strm << " BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
141 strm << " AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
142 strm << " LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
143 strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
144 strm << " ChannelFormat: ";
145 switch (ADesc.ChannelFormat)
149 strm << "No Channel Format";
153 strm << "Config 1 (5.1 with optional HI/VI)";
157 strm << "Config 2 (5.1 + center surround with optional HI/VI)";
161 strm << "Config 3 (7.1 with optional HI/VI)";
169 strm << "Config 5 (7.1 DS with optional HI/VI)";
173 strm << "Config 6 (ST 377-4 MCA)";
183 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
190 AudioSamplingRate: %d/%d\n\
193 QuantizationBits: %u\n\
197 ContainerDuration: %u\n\
198 ChannelFormat: %u\n",
199 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
200 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
203 ADesc.QuantizationBits,
207 ADesc.ContainerDuration,
216 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
218 ui32_t CBR_frame_size = 0;
220 if ( Info.EncryptedEssence )
226 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
227 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
231 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
234 return CBR_frame_size;
238 //------------------------------------------------------------------------------------------
241 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
243 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
247 AudioDescriptor m_ADesc;
249 h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d) {}
250 virtual ~h__Reader() {}
251 Result_t OpenRead(const std::string&);
252 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
259 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename)
261 Result_t result = OpenMXFRead(filename);
263 if( ASDCP_SUCCESS(result) )
265 InterchangeObject* Object = 0
267 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
271 DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
272 return RESULT_FORMAT;
275 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
279 if ( m_ADesc.ContainerDuration == 0 )
281 DefaultLogSink().Error("ContainerDuration unset.\n");
282 return RESULT_FORMAT;
285 // check for sample/frame rate sanity
286 if ( ASDCP_SUCCESS(result)
287 && m_ADesc.EditRate != EditRate_24
288 && m_ADesc.EditRate != EditRate_25
289 && m_ADesc.EditRate != EditRate_30
290 && m_ADesc.EditRate != EditRate_48
291 && m_ADesc.EditRate != EditRate_50
292 && m_ADesc.EditRate != EditRate_60
293 && m_ADesc.EditRate != EditRate_96
294 && m_ADesc.EditRate != EditRate_100
295 && m_ADesc.EditRate != EditRate_120
296 && m_ADesc.EditRate != EditRate_192
297 && m_ADesc.EditRate != EditRate_200
298 && m_ADesc.EditRate != EditRate_240
299 && m_ADesc.EditRate != EditRate_16
300 && m_ADesc.EditRate != EditRate_18
301 && m_ADesc.EditRate != EditRate_20
302 && m_ADesc.EditRate != EditRate_22
303 && m_ADesc.EditRate != EditRate_23_98 )
305 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
306 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
308 // oh, they gave us the audio sampling rate instead, assume 24/1
309 if ( m_ADesc.EditRate == SampleRate_48k || m_ADesc.EditRate == SampleRate_96k )
311 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
312 m_ADesc.EditRate = EditRate_24;
316 DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
317 // or we just drop the hammer
318 return RESULT_FORMAT;
322 // TODO: test file for sane CBR index BytesPerEditUnit
331 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
332 AESDecContext* Ctx, HMACContext* HMAC)
334 if ( ! m_File.IsOpen() )
337 if ( (FrameNum+1) > m_ADesc.ContainerDuration )
343 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
346 //------------------------------------------------------------------------------------------
351 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
356 fprintf(stream, "Frame: %06u, %7u bytes\n",
357 m_FrameNumber, m_Size);
360 Kumu::hexdump(m_Data, dump_len, stream);
363 //------------------------------------------------------------------------------------------
365 ASDCP::PCM::MXFReader::MXFReader()
367 m_Reader = new h__Reader(DefaultCompositeDict());
371 ASDCP::PCM::MXFReader::~MXFReader()
373 if ( m_Reader && m_Reader->m_File.IsOpen() )
377 // Warning: direct manipulation of MXF structures can interfere
378 // with the normal operation of the wrapper. Caveat emptor!
380 ASDCP::MXF::OP1aHeader&
381 ASDCP::PCM::MXFReader::OP1aHeader()
383 if ( m_Reader.empty() )
385 assert(g_OP1aHeader);
386 return *g_OP1aHeader;
389 return m_Reader->m_HeaderPart;
392 // Warning: direct manipulation of MXF structures can interfere
393 // with the normal operation of the wrapper. Caveat emptor!
395 ASDCP::MXF::OPAtomIndexFooter&
396 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
398 if ( m_Reader.empty() )
400 assert(g_OPAtomIndexFooter);
401 return *g_OPAtomIndexFooter;
404 return m_Reader->m_IndexAccess;
407 // Warning: direct manipulation of MXF structures can interfere
408 // with the normal operation of the wrapper. Caveat emptor!
411 ASDCP::PCM::MXFReader::RIP()
413 if ( m_Reader.empty() )
419 return m_Reader->m_RIP;
422 // Open the file for reading. The file must exist. Returns error if the
423 // operation cannot be completed.
425 ASDCP::PCM::MXFReader::OpenRead(const std::string& filename) const
427 return m_Reader->OpenRead(filename);
430 // Reads a frame of essence from the MXF file. If the optional AESEncContext
431 // argument is present, the essence is decrypted after reading. If the MXF
432 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
433 // will contain the ciphertext frame data.
435 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
436 AESDecContext* Ctx, HMACContext* HMAC) const
438 if ( m_Reader && m_Reader->m_File.IsOpen() )
439 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
446 ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
448 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
451 // Fill the struct with the values from the file's header.
452 // Returns RESULT_INIT if the file is not open.
454 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
456 if ( m_Reader && m_Reader->m_File.IsOpen() )
458 ADesc = m_Reader->m_ADesc;
465 // Fill the struct with the values from the file's header.
466 // Returns RESULT_INIT if the file is not open.
468 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
470 if ( m_Reader && m_Reader->m_File.IsOpen() )
472 Info = m_Reader->m_Info;
481 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
483 if ( m_Reader && m_Reader->m_File.IsOpen() )
484 m_Reader->m_HeaderPart.Dump(stream);
490 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
492 if ( m_Reader->m_File.IsOpen() )
493 m_Reader->m_IndexAccess.Dump(stream);
498 ASDCP::PCM::MXFReader::Close() const
500 if ( m_Reader && m_Reader->m_File.IsOpen() )
510 //------------------------------------------------------------------------------------------
513 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
515 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
519 AudioDescriptor m_ADesc;
520 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
522 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d) {
523 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
526 virtual ~h__Writer(){}
528 Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
529 Result_t SetSourceStream(const AudioDescriptor&);
530 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
536 // Open the file for writing. The file must not exist. Returns error if
537 // the operation cannot be completed.
539 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
541 if ( ! m_State.Test_BEGIN() )
544 Result_t result = m_File.OpenWrite(filename);
546 if ( ASDCP_SUCCESS(result) )
548 m_HeaderSize = HeaderSize;
549 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
550 result = m_State.Goto_INIT();
557 // Automatically sets the MXF file's metadata from the WAV parser info.
559 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
561 if ( ! m_State.Test_INIT() )
564 if ( ADesc.EditRate != EditRate_24
565 && ADesc.EditRate != EditRate_25
566 && ADesc.EditRate != EditRate_30
567 && ADesc.EditRate != EditRate_48
568 && ADesc.EditRate != EditRate_50
569 && ADesc.EditRate != EditRate_60
570 && ADesc.EditRate != EditRate_96
571 && ADesc.EditRate != EditRate_100
572 && ADesc.EditRate != EditRate_120
573 && ADesc.EditRate != EditRate_192
574 && ADesc.EditRate != EditRate_200
575 && ADesc.EditRate != EditRate_240
576 && ADesc.EditRate != EditRate_16
577 && ADesc.EditRate != EditRate_18
578 && ADesc.EditRate != EditRate_20
579 && ADesc.EditRate != EditRate_22
580 && ADesc.EditRate != EditRate_23_98 )
582 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
583 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
584 return RESULT_RAW_FORMAT;
587 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
589 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
590 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
591 return RESULT_RAW_FORMAT;
597 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
599 if ( ASDCP_SUCCESS(result) )
601 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
602 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
603 result = m_State.Goto_READY();
606 if ( ASDCP_SUCCESS(result) )
608 result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingFrame)),
609 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
610 m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
611 calc_CBR_frame_size(m_Info, m_ADesc));
621 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
624 Result_t result = RESULT_OK;
626 if ( m_State.Test_READY() )
627 result = m_State.Goto_RUNNING(); // first time through
629 if ( ASDCP_SUCCESS(result) )
630 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
632 if ( ASDCP_SUCCESS(result) )
638 // Closes the MXF file, writing the index and other closing information.
641 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
643 if ( ! m_State.Test_RUNNING() )
646 m_State.Goto_FINAL();
648 return WriteASDCPFooter();
652 //------------------------------------------------------------------------------------------
657 ASDCP::PCM::MXFWriter::MXFWriter()
661 ASDCP::PCM::MXFWriter::~MXFWriter()
665 // Warning: direct manipulation of MXF structures can interfere
666 // with the normal operation of the wrapper. Caveat emptor!
668 ASDCP::MXF::OP1aHeader&
669 ASDCP::PCM::MXFWriter::OP1aHeader()
671 if ( m_Writer.empty() )
673 assert(g_OP1aHeader);
674 return *g_OP1aHeader;
677 return m_Writer->m_HeaderPart;
680 // Warning: direct manipulation of MXF structures can interfere
681 // with the normal operation of the wrapper. Caveat emptor!
683 ASDCP::MXF::OPAtomIndexFooter&
684 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
686 if ( m_Writer.empty() )
688 assert(g_OPAtomIndexFooter);
689 return *g_OPAtomIndexFooter;
692 return m_Writer->m_FooterPart;
695 // Warning: direct manipulation of MXF structures can interfere
696 // with the normal operation of the wrapper. Caveat emptor!
699 ASDCP::PCM::MXFWriter::RIP()
701 if ( m_Writer.empty() )
707 return m_Writer->m_RIP;
710 // Open the file for writing. The file must not exist. Returns error if
711 // the operation cannot be completed.
713 ASDCP::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
714 const AudioDescriptor& ADesc, ui32_t HeaderSize)
716 if ( Info.LabelSetType == LS_MXF_SMPTE )
717 m_Writer = new h__Writer(DefaultSMPTEDict());
719 m_Writer = new h__Writer(DefaultInteropDict());
721 m_Writer->m_Info = Info;
723 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
725 if ( ASDCP_SUCCESS(result) )
726 result = m_Writer->SetSourceStream(ADesc);
728 if ( ASDCP_FAILURE(result) )
734 // Writes a frame of essence to the MXF file. If the optional AESEncContext
735 // argument is present, the essence is encrypted prior to writing.
736 // Fails if the file is not open, is finalized, or an operating system
739 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
741 if ( m_Writer.empty() )
744 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
747 // Closes the MXF file, writing the index and other closing information.
749 ASDCP::PCM::MXFWriter::Finalize()
751 if ( m_Writer.empty() )
754 return m_Writer->Finalize();
758 // end AS_DCP_PCM.cpp