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;
255 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
258 result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
262 // check for sample/frame rate sanity
263 if ( ASDCP_SUCCESS(result)
264 && m_ADesc.EditRate != EditRate_24
265 && m_ADesc.EditRate != EditRate_25
266 && m_ADesc.EditRate != EditRate_30
267 && m_ADesc.EditRate != EditRate_48
268 && m_ADesc.EditRate != EditRate_50
269 && m_ADesc.EditRate != EditRate_60
270 && m_ADesc.EditRate != EditRate_96
271 && m_ADesc.EditRate != EditRate_100
272 && m_ADesc.EditRate != EditRate_120
273 && m_ADesc.EditRate != EditRate_16
274 && m_ADesc.EditRate != EditRate_18
275 && m_ADesc.EditRate != EditRate_20
276 && m_ADesc.EditRate != EditRate_22
277 && m_ADesc.EditRate != EditRate_23_98 )
279 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
280 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
282 // oh, they gave us the audio sampling rate instead, assume 24/1
283 if ( m_ADesc.EditRate == SampleRate_48k )
285 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
286 m_ADesc.EditRate = EditRate_24;
290 DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
291 // or we just drop the hammer
292 return RESULT_FORMAT;
296 // TODO: test file for sane CBR index BytesPerEditUnit
305 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
306 AESDecContext* Ctx, HMACContext* HMAC)
308 if ( ! m_File.IsOpen() )
312 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
315 //------------------------------------------------------------------------------------------
320 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
325 fprintf(stream, "Frame: %06u, %7u bytes\n",
326 m_FrameNumber, m_Size);
329 Kumu::hexdump(m_Data, dump_len, stream);
332 //------------------------------------------------------------------------------------------
334 ASDCP::PCM::MXFReader::MXFReader()
336 m_Reader = new h__Reader(DefaultCompositeDict());
340 ASDCP::PCM::MXFReader::~MXFReader()
342 if ( m_Reader && m_Reader->m_File.IsOpen() )
346 // Warning: direct manipulation of MXF structures can interfere
347 // with the normal operation of the wrapper. Caveat emptor!
349 ASDCP::MXF::OP1aHeader&
350 ASDCP::PCM::MXFReader::OP1aHeader()
352 if ( m_Reader.empty() )
354 assert(g_OP1aHeader);
355 return *g_OP1aHeader;
358 return m_Reader->m_HeaderPart;
361 // Warning: direct manipulation of MXF structures can interfere
362 // with the normal operation of the wrapper. Caveat emptor!
364 ASDCP::MXF::OPAtomIndexFooter&
365 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
367 if ( m_Reader.empty() )
369 assert(g_OPAtomIndexFooter);
370 return *g_OPAtomIndexFooter;
373 return m_Reader->m_IndexAccess;
376 // Warning: direct manipulation of MXF structures can interfere
377 // with the normal operation of the wrapper. Caveat emptor!
380 ASDCP::PCM::MXFReader::RIP()
382 if ( m_Reader.empty() )
388 return m_Reader->m_RIP;
391 // Open the file for reading. The file must exist. Returns error if the
392 // operation cannot be completed.
394 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
396 return m_Reader->OpenRead(filename);
399 // Reads a frame of essence from the MXF file. If the optional AESEncContext
400 // argument is present, the essence is decrypted after reading. If the MXF
401 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
402 // will contain the ciphertext frame data.
404 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
405 AESDecContext* Ctx, HMACContext* HMAC) const
407 if ( m_Reader && m_Reader->m_File.IsOpen() )
408 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
415 ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
417 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
420 // Fill the struct with the values from the file's header.
421 // Returns RESULT_INIT if the file is not open.
423 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
425 if ( m_Reader && m_Reader->m_File.IsOpen() )
427 ADesc = m_Reader->m_ADesc;
434 // Fill the struct with the values from the file's header.
435 // Returns RESULT_INIT if the file is not open.
437 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
439 if ( m_Reader && m_Reader->m_File.IsOpen() )
441 Info = m_Reader->m_Info;
450 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
452 if ( m_Reader && m_Reader->m_File.IsOpen() )
453 m_Reader->m_HeaderPart.Dump(stream);
459 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
461 if ( m_Reader->m_File.IsOpen() )
462 m_Reader->m_IndexAccess.Dump(stream);
467 ASDCP::PCM::MXFReader::Close() const
469 if ( m_Reader && m_Reader->m_File.IsOpen() )
479 //------------------------------------------------------------------------------------------
482 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
484 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
488 AudioDescriptor m_ADesc;
489 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
491 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d) {
492 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
495 virtual ~h__Writer(){}
497 Result_t OpenWrite(const char*, ui32_t HeaderSize);
498 Result_t SetSourceStream(const AudioDescriptor&);
499 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
505 // Open the file for writing. The file must not exist. Returns error if
506 // the operation cannot be completed.
508 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
510 if ( ! m_State.Test_BEGIN() )
513 Result_t result = m_File.OpenWrite(filename);
515 if ( ASDCP_SUCCESS(result) )
517 m_HeaderSize = HeaderSize;
518 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
519 result = m_State.Goto_INIT();
526 // Automatically sets the MXF file's metadata from the WAV parser info.
528 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
530 if ( ! m_State.Test_INIT() )
533 if ( ADesc.EditRate != EditRate_24
534 && ADesc.EditRate != EditRate_25
535 && ADesc.EditRate != EditRate_30
536 && ADesc.EditRate != EditRate_48
537 && ADesc.EditRate != EditRate_50
538 && ADesc.EditRate != EditRate_60
539 && ADesc.EditRate != EditRate_96
540 && ADesc.EditRate != EditRate_100
541 && ADesc.EditRate != EditRate_120
542 && ADesc.EditRate != EditRate_16
543 && ADesc.EditRate != EditRate_18
544 && ADesc.EditRate != EditRate_20
545 && ADesc.EditRate != EditRate_22
546 && ADesc.EditRate != EditRate_23_98 )
548 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
549 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
550 return RESULT_RAW_FORMAT;
553 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
555 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
556 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
557 return RESULT_RAW_FORMAT;
563 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
565 if ( ASDCP_SUCCESS(result) )
567 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
568 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
569 result = m_State.Goto_READY();
572 if ( ASDCP_SUCCESS(result) )
574 ui32_t TCFrameRate = m_ADesc.EditRate.Numerator;
576 if ( m_ADesc.EditRate == EditRate_23_98 )
578 else if ( m_ADesc.EditRate == EditRate_18 )
580 else if ( m_ADesc.EditRate == EditRate_22 )
583 result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
584 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
585 m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
595 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
598 Result_t result = RESULT_OK;
600 if ( m_State.Test_READY() )
601 result = m_State.Goto_RUNNING(); // first time through
603 if ( ASDCP_SUCCESS(result) )
604 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
606 if ( ASDCP_SUCCESS(result) )
612 // Closes the MXF file, writing the index and other closing information.
615 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
617 if ( ! m_State.Test_RUNNING() )
620 m_State.Goto_FINAL();
622 return WriteASDCPFooter();
626 //------------------------------------------------------------------------------------------
631 ASDCP::PCM::MXFWriter::MXFWriter()
635 ASDCP::PCM::MXFWriter::~MXFWriter()
639 // Warning: direct manipulation of MXF structures can interfere
640 // with the normal operation of the wrapper. Caveat emptor!
642 ASDCP::MXF::OP1aHeader&
643 ASDCP::PCM::MXFWriter::OP1aHeader()
645 if ( m_Writer.empty() )
647 assert(g_OP1aHeader);
648 return *g_OP1aHeader;
651 return m_Writer->m_HeaderPart;
654 // Warning: direct manipulation of MXF structures can interfere
655 // with the normal operation of the wrapper. Caveat emptor!
657 ASDCP::MXF::OPAtomIndexFooter&
658 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
660 if ( m_Writer.empty() )
662 assert(g_OPAtomIndexFooter);
663 return *g_OPAtomIndexFooter;
666 return m_Writer->m_FooterPart;
669 // Warning: direct manipulation of MXF structures can interfere
670 // with the normal operation of the wrapper. Caveat emptor!
673 ASDCP::PCM::MXFWriter::RIP()
675 if ( m_Writer.empty() )
681 return m_Writer->m_RIP;
684 // Open the file for writing. The file must not exist. Returns error if
685 // the operation cannot be completed.
687 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
688 const AudioDescriptor& ADesc, ui32_t HeaderSize)
690 if ( Info.LabelSetType == LS_MXF_SMPTE )
691 m_Writer = new h__Writer(DefaultSMPTEDict());
693 m_Writer = new h__Writer(DefaultInteropDict());
695 m_Writer->m_Info = Info;
697 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
699 if ( ASDCP_SUCCESS(result) )
700 result = m_Writer->SetSourceStream(ADesc);
702 if ( ASDCP_FAILURE(result) )
708 // Writes a frame of essence to the MXF file. If the optional AESEncContext
709 // argument is present, the essence is encrypted prior to writing.
710 // Fails if the file is not open, is finalized, or an operating system
713 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
715 if ( m_Writer.empty() )
718 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
721 // Closes the MXF file, writing the index and other closing information.
723 ASDCP::PCM::MXFWriter::Finalize()
725 if ( m_Writer.empty() )
728 return m_Writer->Finalize();
732 // end AS_DCP_PCM.cpp