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
28 \version $Id: AS_DCP_PCM.cpp,v 1.47 2014/10/02 21:02:24 jhurst Exp $
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;
94 ASDCP::MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
96 ASDCP_TEST_NULL(ADescObj);
97 ADesc.EditRate = ADescObj->SampleRate;
98 ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
99 ADesc.Locked = ADescObj->Locked;
100 ADesc.ChannelCount = ADescObj->ChannelCount;
101 ADesc.QuantizationBits = ADescObj->QuantizationBits;
102 ADesc.BlockAlign = ADescObj->BlockAlign;
103 ADesc.AvgBps = ADescObj->AvgBps;
104 ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
105 assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
106 ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
108 ADesc.ChannelFormat = PCM::CF_NONE;
110 if ( ! ADescObj->ChannelAssignment.empty() )
112 if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_1_5p1).ul )
113 ADesc.ChannelFormat = PCM::CF_CFG_1;
115 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_2_6p1).ul )
116 ADesc.ChannelFormat = PCM::CF_CFG_2;
118 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_3_7p1).ul )
119 ADesc.ChannelFormat = PCM::CF_CFG_3;
121 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_4_WTF).ul )
122 ADesc.ChannelFormat = PCM::CF_CFG_4;
124 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_5_7p1_DS).ul )
125 ADesc.ChannelFormat = PCM::CF_CFG_5;
127 else if ( ADescObj->ChannelAssignment == DefaultSMPTEDict().Type(MDD_DCAudioChannelCfg_MCA).ul )
128 ADesc.ChannelFormat = PCM::CF_CFG_6;
136 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
138 strm << " SampleRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
139 strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
140 strm << " Locked: " << (unsigned) ADesc.Locked << std::endl;
141 strm << " ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
142 strm << " QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
143 strm << " BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
144 strm << " AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
145 strm << " LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
146 strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
147 strm << " ChannelFormat: ";
148 switch (ADesc.ChannelFormat)
152 strm << "No Channel Format";
156 strm << "Config 1 (5.1 with optional HI/VI)";
160 strm << "Config 2 (5.1 + center surround with optional HI/VI)";
164 strm << "Config 3 (7.1 with optional HI/VI)";
172 strm << "Config 5 (7.1 DS with optional HI/VI)";
176 strm << "Config 6 (ST 377-4 MCA)";
186 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
193 AudioSamplingRate: %d/%d\n\
196 QuantizationBits: %u\n\
200 ContainerDuration: %u\n\
201 ChannelFormat: %u\n",
202 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
203 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
206 ADesc.QuantizationBits,
210 ADesc.ContainerDuration,
219 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
221 ui32_t CBR_frame_size = 0;
223 if ( Info.EncryptedEssence )
229 + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
230 + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
234 CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
237 return CBR_frame_size;
241 //------------------------------------------------------------------------------------------
244 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
246 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
250 AudioDescriptor m_ADesc;
252 h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d) {}
253 virtual ~h__Reader() {}
254 Result_t OpenRead(const std::string&);
255 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
262 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename)
264 Result_t result = OpenMXFRead(filename);
266 if( ASDCP_SUCCESS(result) )
268 InterchangeObject* Object = 0
270 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
274 DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
275 return RESULT_FORMAT;
278 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
282 if ( m_ADesc.ContainerDuration == 0 )
284 DefaultLogSink().Error("ContainerDuration unset.\n");
285 return RESULT_FORMAT;
289 /* This check has been removed so that DCP-o-matic can use any edit rate it wants */
290 // check for sample/frame rate sanity
291 if ( ASDCP_SUCCESS(result)
292 && m_ADesc.EditRate != EditRate_24
293 && m_ADesc.EditRate != EditRate_25
294 && m_ADesc.EditRate != EditRate_30
295 && m_ADesc.EditRate != EditRate_48
296 && m_ADesc.EditRate != EditRate_50
297 && m_ADesc.EditRate != EditRate_60
298 && m_ADesc.EditRate != EditRate_96
299 && m_ADesc.EditRate != EditRate_100
300 && m_ADesc.EditRate != EditRate_120
301 && m_ADesc.EditRate != EditRate_16
302 && m_ADesc.EditRate != EditRate_18
303 && m_ADesc.EditRate != EditRate_20
304 && m_ADesc.EditRate != EditRate_22
305 && m_ADesc.EditRate != EditRate_23_98 )
307 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
308 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
310 // oh, they gave us the audio sampling rate instead, assume 24/1
311 if ( m_ADesc.EditRate == SampleRate_48k || m_ADesc.EditRate == SampleRate_96k )
313 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
314 m_ADesc.EditRate = EditRate_24;
318 DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
319 // or we just drop the hammer
320 return RESULT_FORMAT;
325 // TODO: test file for sane CBR index BytesPerEditUnit
334 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
335 AESDecContext* Ctx, HMACContext* HMAC)
337 if ( ! m_File.IsOpen() )
340 if ( (FrameNum+1) > m_ADesc.ContainerDuration )
346 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
349 //------------------------------------------------------------------------------------------
354 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
359 fprintf(stream, "Frame: %06u, %7u bytes\n",
360 m_FrameNumber, m_Size);
363 Kumu::hexdump(m_Data, dump_len, stream);
366 //------------------------------------------------------------------------------------------
368 ASDCP::PCM::MXFReader::MXFReader()
370 m_Reader = new h__Reader(DefaultCompositeDict());
374 ASDCP::PCM::MXFReader::~MXFReader()
376 if ( m_Reader && m_Reader->m_File.IsOpen() )
380 // Warning: direct manipulation of MXF structures can interfere
381 // with the normal operation of the wrapper. Caveat emptor!
383 ASDCP::MXF::OP1aHeader&
384 ASDCP::PCM::MXFReader::OP1aHeader()
386 if ( m_Reader.empty() )
388 assert(g_OP1aHeader);
389 return *g_OP1aHeader;
392 return m_Reader->m_HeaderPart;
395 // Warning: direct manipulation of MXF structures can interfere
396 // with the normal operation of the wrapper. Caveat emptor!
398 ASDCP::MXF::OPAtomIndexFooter&
399 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
401 if ( m_Reader.empty() )
403 assert(g_OPAtomIndexFooter);
404 return *g_OPAtomIndexFooter;
407 return m_Reader->m_IndexAccess;
410 // Warning: direct manipulation of MXF structures can interfere
411 // with the normal operation of the wrapper. Caveat emptor!
414 ASDCP::PCM::MXFReader::RIP()
416 if ( m_Reader.empty() )
422 return m_Reader->m_RIP;
425 // Open the file for reading. The file must exist. Returns error if the
426 // operation cannot be completed.
428 ASDCP::PCM::MXFReader::OpenRead(const std::string& filename) const
430 return m_Reader->OpenRead(filename);
433 // Reads a frame of essence from the MXF file. If the optional AESEncContext
434 // argument is present, the essence is decrypted after reading. If the MXF
435 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
436 // will contain the ciphertext frame data.
438 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
439 AESDecContext* Ctx, HMACContext* HMAC) const
441 if ( m_Reader && m_Reader->m_File.IsOpen() )
442 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
449 ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
451 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
454 // Fill the struct with the values from the file's header.
455 // Returns RESULT_INIT if the file is not open.
457 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
459 if ( m_Reader && m_Reader->m_File.IsOpen() )
461 ADesc = m_Reader->m_ADesc;
468 // Fill the struct with the values from the file's header.
469 // Returns RESULT_INIT if the file is not open.
471 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
473 if ( m_Reader && m_Reader->m_File.IsOpen() )
475 Info = m_Reader->m_Info;
484 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
486 if ( m_Reader && m_Reader->m_File.IsOpen() )
487 m_Reader->m_HeaderPart.Dump(stream);
493 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
495 if ( m_Reader->m_File.IsOpen() )
496 m_Reader->m_IndexAccess.Dump(stream);
501 ASDCP::PCM::MXFReader::Close() const
503 if ( m_Reader && m_Reader->m_File.IsOpen() )
513 //------------------------------------------------------------------------------------------
516 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
518 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
522 AudioDescriptor m_ADesc;
523 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
525 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d) {
526 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
529 virtual ~h__Writer(){}
531 Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
532 Result_t SetSourceStream(const AudioDescriptor&);
533 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
539 // Open the file for writing. The file must not exist. Returns error if
540 // the operation cannot be completed.
542 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
544 if ( ! m_State.Test_BEGIN() )
547 Result_t result = m_File.OpenWrite(filename);
549 if ( ASDCP_SUCCESS(result) )
551 m_HeaderSize = HeaderSize;
552 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
553 result = m_State.Goto_INIT();
560 // Automatically sets the MXF file's metadata from the WAV parser info.
562 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
564 if ( ! m_State.Test_INIT() )
568 /* This check has been removed so that DCP-o-matic can use any edit rate it wants */
569 if ( ADesc.EditRate != EditRate_24
570 && ADesc.EditRate != EditRate_25
571 && ADesc.EditRate != EditRate_30
572 && ADesc.EditRate != EditRate_48
573 && ADesc.EditRate != EditRate_50
574 && ADesc.EditRate != EditRate_60
575 && ADesc.EditRate != EditRate_96
576 && ADesc.EditRate != EditRate_100
577 && ADesc.EditRate != EditRate_120
578 && ADesc.EditRate != EditRate_16
579 && ADesc.EditRate != EditRate_18
580 && ADesc.EditRate != EditRate_20
581 && ADesc.EditRate != EditRate_22
582 && ADesc.EditRate != EditRate_23_98 )
584 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
585 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
586 return RESULT_RAW_FORMAT;
590 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
592 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
593 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
594 return RESULT_RAW_FORMAT;
600 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
602 if ( ASDCP_SUCCESS(result) )
604 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
605 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
606 result = m_State.Goto_READY();
609 if ( ASDCP_SUCCESS(result) )
611 result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingFrame)),
612 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
613 m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
614 calc_CBR_frame_size(m_Info, m_ADesc));
624 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
627 Result_t result = RESULT_OK;
629 if ( m_State.Test_READY() )
630 result = m_State.Goto_RUNNING(); // first time through
632 if ( ASDCP_SUCCESS(result) )
633 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
635 if ( ASDCP_SUCCESS(result) )
641 // Closes the MXF file, writing the index and other closing information.
644 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
646 if ( ! m_State.Test_RUNNING() )
649 m_State.Goto_FINAL();
651 return WriteASDCPFooter();
655 //------------------------------------------------------------------------------------------
660 ASDCP::PCM::MXFWriter::MXFWriter()
664 ASDCP::PCM::MXFWriter::~MXFWriter()
668 // Warning: direct manipulation of MXF structures can interfere
669 // with the normal operation of the wrapper. Caveat emptor!
671 ASDCP::MXF::OP1aHeader&
672 ASDCP::PCM::MXFWriter::OP1aHeader()
674 if ( m_Writer.empty() )
676 assert(g_OP1aHeader);
677 return *g_OP1aHeader;
680 return m_Writer->m_HeaderPart;
683 // Warning: direct manipulation of MXF structures can interfere
684 // with the normal operation of the wrapper. Caveat emptor!
686 ASDCP::MXF::OPAtomIndexFooter&
687 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
689 if ( m_Writer.empty() )
691 assert(g_OPAtomIndexFooter);
692 return *g_OPAtomIndexFooter;
695 return m_Writer->m_FooterPart;
698 // Warning: direct manipulation of MXF structures can interfere
699 // with the normal operation of the wrapper. Caveat emptor!
702 ASDCP::PCM::MXFWriter::RIP()
704 if ( m_Writer.empty() )
710 return m_Writer->m_RIP;
713 // Open the file for writing. The file must not exist. Returns error if
714 // the operation cannot be completed.
716 ASDCP::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
717 const AudioDescriptor& ADesc, ui32_t HeaderSize)
719 if ( Info.LabelSetType == LS_MXF_SMPTE )
720 m_Writer = new h__Writer(DefaultSMPTEDict());
722 m_Writer = new h__Writer(DefaultInteropDict());
724 m_Writer->m_Info = Info;
726 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
728 if ( ASDCP_SUCCESS(result) )
729 result = m_Writer->SetSourceStream(ADesc);
731 if ( ASDCP_FAILURE(result) )
737 // Writes a frame of essence to the MXF file. If the optional AESEncContext
738 // argument is present, the essence is encrypted prior to writing.
739 // Fails if the file is not open, is finalized, or an operating system
742 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
744 if ( m_Writer.empty() )
747 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
750 // Closes the MXF file, writing the index and other closing information.
752 ASDCP::PCM::MXFWriter::Finalize()
754 if ( m_Writer.empty() )
757 return m_Writer->Finalize();
761 // end AS_DCP_PCM.cpp