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.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-1 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 // check for sample/frame rate sanity
280 if ( ASDCP_SUCCESS(result)
281 && m_ADesc.EditRate != EditRate_24
282 && m_ADesc.EditRate != EditRate_25
283 && m_ADesc.EditRate != EditRate_30
284 && m_ADesc.EditRate != EditRate_48
285 && m_ADesc.EditRate != EditRate_50
286 && m_ADesc.EditRate != EditRate_60
287 && m_ADesc.EditRate != EditRate_96
288 && m_ADesc.EditRate != EditRate_100
289 && m_ADesc.EditRate != EditRate_120
290 && m_ADesc.EditRate != EditRate_16
291 && m_ADesc.EditRate != EditRate_18
292 && m_ADesc.EditRate != EditRate_20
293 && m_ADesc.EditRate != EditRate_22
294 && m_ADesc.EditRate != EditRate_23_98 )
296 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
297 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
299 // oh, they gave us the audio sampling rate instead, assume 24/1
300 if ( m_ADesc.EditRate == SampleRate_48k )
302 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
303 m_ADesc.EditRate = EditRate_24;
307 DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
308 // or we just drop the hammer
309 return RESULT_FORMAT;
313 // TODO: test file for sane CBR index BytesPerEditUnit
322 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
323 AESDecContext* Ctx, HMACContext* HMAC)
325 if ( ! m_File.IsOpen() )
329 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
332 //------------------------------------------------------------------------------------------
337 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
342 fprintf(stream, "Frame: %06u, %7u bytes\n",
343 m_FrameNumber, m_Size);
346 Kumu::hexdump(m_Data, dump_len, stream);
349 //------------------------------------------------------------------------------------------
351 ASDCP::PCM::MXFReader::MXFReader()
353 m_Reader = new h__Reader(DefaultCompositeDict());
357 ASDCP::PCM::MXFReader::~MXFReader()
359 if ( m_Reader && m_Reader->m_File.IsOpen() )
363 // Warning: direct manipulation of MXF structures can interfere
364 // with the normal operation of the wrapper. Caveat emptor!
366 ASDCP::MXF::OP1aHeader&
367 ASDCP::PCM::MXFReader::OP1aHeader()
369 if ( m_Reader.empty() )
371 assert(g_OP1aHeader);
372 return *g_OP1aHeader;
375 return m_Reader->m_HeaderPart;
378 // Warning: direct manipulation of MXF structures can interfere
379 // with the normal operation of the wrapper. Caveat emptor!
381 ASDCP::MXF::OPAtomIndexFooter&
382 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
384 if ( m_Reader.empty() )
386 assert(g_OPAtomIndexFooter);
387 return *g_OPAtomIndexFooter;
390 return m_Reader->m_IndexAccess;
393 // Warning: direct manipulation of MXF structures can interfere
394 // with the normal operation of the wrapper. Caveat emptor!
397 ASDCP::PCM::MXFReader::RIP()
399 if ( m_Reader.empty() )
405 return m_Reader->m_RIP;
408 // Open the file for reading. The file must exist. Returns error if the
409 // operation cannot be completed.
411 ASDCP::PCM::MXFReader::OpenRead(const std::string& filename) const
413 return m_Reader->OpenRead(filename);
416 // Reads a frame of essence from the MXF file. If the optional AESEncContext
417 // argument is present, the essence is decrypted after reading. If the MXF
418 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
419 // will contain the ciphertext frame data.
421 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
422 AESDecContext* Ctx, HMACContext* HMAC) const
424 if ( m_Reader && m_Reader->m_File.IsOpen() )
425 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
432 ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
434 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
437 // Fill the struct with the values from the file's header.
438 // Returns RESULT_INIT if the file is not open.
440 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
442 if ( m_Reader && m_Reader->m_File.IsOpen() )
444 ADesc = m_Reader->m_ADesc;
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::FillWriterInfo(WriterInfo& Info) const
456 if ( m_Reader && m_Reader->m_File.IsOpen() )
458 Info = m_Reader->m_Info;
467 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
469 if ( m_Reader && m_Reader->m_File.IsOpen() )
470 m_Reader->m_HeaderPart.Dump(stream);
476 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
478 if ( m_Reader->m_File.IsOpen() )
479 m_Reader->m_IndexAccess.Dump(stream);
484 ASDCP::PCM::MXFReader::Close() const
486 if ( m_Reader && m_Reader->m_File.IsOpen() )
496 //------------------------------------------------------------------------------------------
499 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
501 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
505 AudioDescriptor m_ADesc;
506 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
508 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d) {
509 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
512 virtual ~h__Writer(){}
514 Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
515 Result_t SetSourceStream(const AudioDescriptor&);
516 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
522 // Open the file for writing. The file must not exist. Returns error if
523 // the operation cannot be completed.
525 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
527 if ( ! m_State.Test_BEGIN() )
530 Result_t result = m_File.OpenWrite(filename);
532 if ( ASDCP_SUCCESS(result) )
534 m_HeaderSize = HeaderSize;
535 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
536 result = m_State.Goto_INIT();
543 // Automatically sets the MXF file's metadata from the WAV parser info.
545 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
547 if ( ! m_State.Test_INIT() )
550 if ( ADesc.EditRate != EditRate_24
551 && ADesc.EditRate != EditRate_25
552 && ADesc.EditRate != EditRate_30
553 && ADesc.EditRate != EditRate_48
554 && ADesc.EditRate != EditRate_50
555 && ADesc.EditRate != EditRate_60
556 && ADesc.EditRate != EditRate_96
557 && ADesc.EditRate != EditRate_100
558 && ADesc.EditRate != EditRate_120
559 && ADesc.EditRate != EditRate_16
560 && ADesc.EditRate != EditRate_18
561 && ADesc.EditRate != EditRate_20
562 && ADesc.EditRate != EditRate_22
563 && ADesc.EditRate != EditRate_23_98 )
565 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
566 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
567 return RESULT_RAW_FORMAT;
570 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
572 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
573 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
574 return RESULT_RAW_FORMAT;
580 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
582 if ( ASDCP_SUCCESS(result) )
584 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
585 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
586 result = m_State.Goto_READY();
589 if ( ASDCP_SUCCESS(result) )
591 result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingFrame)),
592 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
593 m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
594 calc_CBR_frame_size(m_Info, m_ADesc));
604 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
607 Result_t result = RESULT_OK;
609 if ( m_State.Test_READY() )
610 result = m_State.Goto_RUNNING(); // first time through
612 if ( ASDCP_SUCCESS(result) )
613 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
615 if ( ASDCP_SUCCESS(result) )
621 // Closes the MXF file, writing the index and other closing information.
624 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
626 if ( ! m_State.Test_RUNNING() )
629 m_State.Goto_FINAL();
631 return WriteASDCPFooter();
635 //------------------------------------------------------------------------------------------
640 ASDCP::PCM::MXFWriter::MXFWriter()
644 ASDCP::PCM::MXFWriter::~MXFWriter()
648 // Warning: direct manipulation of MXF structures can interfere
649 // with the normal operation of the wrapper. Caveat emptor!
651 ASDCP::MXF::OP1aHeader&
652 ASDCP::PCM::MXFWriter::OP1aHeader()
654 if ( m_Writer.empty() )
656 assert(g_OP1aHeader);
657 return *g_OP1aHeader;
660 return m_Writer->m_HeaderPart;
663 // Warning: direct manipulation of MXF structures can interfere
664 // with the normal operation of the wrapper. Caveat emptor!
666 ASDCP::MXF::OPAtomIndexFooter&
667 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
669 if ( m_Writer.empty() )
671 assert(g_OPAtomIndexFooter);
672 return *g_OPAtomIndexFooter;
675 return m_Writer->m_FooterPart;
678 // Warning: direct manipulation of MXF structures can interfere
679 // with the normal operation of the wrapper. Caveat emptor!
682 ASDCP::PCM::MXFWriter::RIP()
684 if ( m_Writer.empty() )
690 return m_Writer->m_RIP;
693 // Open the file for writing. The file must not exist. Returns error if
694 // the operation cannot be completed.
696 ASDCP::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
697 const AudioDescriptor& ADesc, ui32_t HeaderSize)
699 if ( Info.LabelSetType == LS_MXF_SMPTE )
700 m_Writer = new h__Writer(DefaultSMPTEDict());
702 m_Writer = new h__Writer(DefaultInteropDict());
704 m_Writer->m_Info = Info;
706 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
708 if ( ASDCP_SUCCESS(result) )
709 result = m_Writer->SetSourceStream(ADesc);
711 if ( ASDCP_FAILURE(result) )
717 // Writes a frame of essence to the MXF file. If the optional AESEncContext
718 // argument is present, the essence is encrypted prior to writing.
719 // Fails if the file is not open, is finalized, or an operating system
722 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
724 if ( m_Writer.empty() )
727 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
730 // Closes the MXF file, writing the index and other closing information.
732 ASDCP::PCM::MXFWriter::Finalize()
734 if ( m_Writer.empty() )
737 return m_Writer->Finalize();
741 // end AS_DCP_PCM.cpp