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 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_16
297 && m_ADesc.EditRate != EditRate_18
298 && m_ADesc.EditRate != EditRate_20
299 && m_ADesc.EditRate != EditRate_22
300 && m_ADesc.EditRate != EditRate_23_98 )
302 DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
303 m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
305 // oh, they gave us the audio sampling rate instead, assume 24/1
306 if ( m_ADesc.EditRate == SampleRate_48k || m_ADesc.EditRate == SampleRate_96k )
308 DefaultLogSink().Warn("adjusting EditRate to 24/1\n");
309 m_ADesc.EditRate = EditRate_24;
313 DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
314 // or we just drop the hammer
315 return RESULT_FORMAT;
319 // TODO: test file for sane CBR index BytesPerEditUnit
328 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
329 AESDecContext* Ctx, HMACContext* HMAC)
331 if ( ! m_File.IsOpen() )
334 if ( (FrameNum+1) > m_ADesc.ContainerDuration )
340 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
343 //------------------------------------------------------------------------------------------
348 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
353 fprintf(stream, "Frame: %06u, %7u bytes\n",
354 m_FrameNumber, m_Size);
357 Kumu::hexdump(m_Data, dump_len, stream);
360 //------------------------------------------------------------------------------------------
362 ASDCP::PCM::MXFReader::MXFReader()
364 m_Reader = new h__Reader(DefaultCompositeDict());
368 ASDCP::PCM::MXFReader::~MXFReader()
370 if ( m_Reader && m_Reader->m_File.IsOpen() )
374 // Warning: direct manipulation of MXF structures can interfere
375 // with the normal operation of the wrapper. Caveat emptor!
377 ASDCP::MXF::OP1aHeader&
378 ASDCP::PCM::MXFReader::OP1aHeader()
380 if ( m_Reader.empty() )
382 assert(g_OP1aHeader);
383 return *g_OP1aHeader;
386 return m_Reader->m_HeaderPart;
389 // Warning: direct manipulation of MXF structures can interfere
390 // with the normal operation of the wrapper. Caveat emptor!
392 ASDCP::MXF::OPAtomIndexFooter&
393 ASDCP::PCM::MXFReader::OPAtomIndexFooter()
395 if ( m_Reader.empty() )
397 assert(g_OPAtomIndexFooter);
398 return *g_OPAtomIndexFooter;
401 return m_Reader->m_IndexAccess;
404 // Warning: direct manipulation of MXF structures can interfere
405 // with the normal operation of the wrapper. Caveat emptor!
408 ASDCP::PCM::MXFReader::RIP()
410 if ( m_Reader.empty() )
416 return m_Reader->m_RIP;
419 // Open the file for reading. The file must exist. Returns error if the
420 // operation cannot be completed.
422 ASDCP::PCM::MXFReader::OpenRead(const std::string& filename) const
424 return m_Reader->OpenRead(filename);
427 // Reads a frame of essence from the MXF file. If the optional AESEncContext
428 // argument is present, the essence is decrypted after reading. If the MXF
429 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
430 // will contain the ciphertext frame data.
432 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
433 AESDecContext* Ctx, HMACContext* HMAC) const
435 if ( m_Reader && m_Reader->m_File.IsOpen() )
436 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
443 ASDCP::PCM::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
445 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
448 // Fill the struct with the values from the file's header.
449 // Returns RESULT_INIT if the file is not open.
451 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
453 if ( m_Reader && m_Reader->m_File.IsOpen() )
455 ADesc = m_Reader->m_ADesc;
462 // Fill the struct with the values from the file's header.
463 // Returns RESULT_INIT if the file is not open.
465 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
467 if ( m_Reader && m_Reader->m_File.IsOpen() )
469 Info = m_Reader->m_Info;
478 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
480 if ( m_Reader && m_Reader->m_File.IsOpen() )
481 m_Reader->m_HeaderPart.Dump(stream);
487 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
489 if ( m_Reader->m_File.IsOpen() )
490 m_Reader->m_IndexAccess.Dump(stream);
495 ASDCP::PCM::MXFReader::Close() const
497 if ( m_Reader && m_Reader->m_File.IsOpen() )
507 //------------------------------------------------------------------------------------------
510 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
512 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
516 AudioDescriptor m_ADesc;
517 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
519 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d) {
520 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
523 virtual ~h__Writer(){}
525 Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
526 Result_t SetSourceStream(const AudioDescriptor&);
527 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
533 // Open the file for writing. The file must not exist. Returns error if
534 // the operation cannot be completed.
536 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
538 if ( ! m_State.Test_BEGIN() )
541 Result_t result = m_File.OpenWrite(filename);
543 if ( ASDCP_SUCCESS(result) )
545 m_HeaderSize = HeaderSize;
546 m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
547 result = m_State.Goto_INIT();
554 // Automatically sets the MXF file's metadata from the WAV parser info.
556 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
558 if ( ! m_State.Test_INIT() )
561 if ( ADesc.EditRate != EditRate_24
562 && ADesc.EditRate != EditRate_25
563 && ADesc.EditRate != EditRate_30
564 && ADesc.EditRate != EditRate_48
565 && ADesc.EditRate != EditRate_50
566 && ADesc.EditRate != EditRate_60
567 && ADesc.EditRate != EditRate_96
568 && ADesc.EditRate != EditRate_100
569 && ADesc.EditRate != EditRate_120
570 && ADesc.EditRate != EditRate_16
571 && ADesc.EditRate != EditRate_18
572 && ADesc.EditRate != EditRate_20
573 && ADesc.EditRate != EditRate_22
574 && ADesc.EditRate != EditRate_23_98 )
576 DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
577 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
578 return RESULT_RAW_FORMAT;
581 if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
583 DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
584 ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
585 return RESULT_RAW_FORMAT;
591 Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
593 if ( ASDCP_SUCCESS(result) )
595 memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
596 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
597 result = m_State.Goto_READY();
600 if ( ASDCP_SUCCESS(result) )
602 result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingFrame)),
603 SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
604 m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
605 calc_CBR_frame_size(m_Info, m_ADesc));
615 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
618 Result_t result = RESULT_OK;
620 if ( m_State.Test_READY() )
621 result = m_State.Goto_RUNNING(); // first time through
623 if ( ASDCP_SUCCESS(result) )
624 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
626 if ( ASDCP_SUCCESS(result) )
632 // Closes the MXF file, writing the index and other closing information.
635 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
637 if ( ! m_State.Test_RUNNING() )
640 m_State.Goto_FINAL();
642 return WriteASDCPFooter();
646 //------------------------------------------------------------------------------------------
651 ASDCP::PCM::MXFWriter::MXFWriter()
655 ASDCP::PCM::MXFWriter::~MXFWriter()
659 // Warning: direct manipulation of MXF structures can interfere
660 // with the normal operation of the wrapper. Caveat emptor!
662 ASDCP::MXF::OP1aHeader&
663 ASDCP::PCM::MXFWriter::OP1aHeader()
665 if ( m_Writer.empty() )
667 assert(g_OP1aHeader);
668 return *g_OP1aHeader;
671 return m_Writer->m_HeaderPart;
674 // Warning: direct manipulation of MXF structures can interfere
675 // with the normal operation of the wrapper. Caveat emptor!
677 ASDCP::MXF::OPAtomIndexFooter&
678 ASDCP::PCM::MXFWriter::OPAtomIndexFooter()
680 if ( m_Writer.empty() )
682 assert(g_OPAtomIndexFooter);
683 return *g_OPAtomIndexFooter;
686 return m_Writer->m_FooterPart;
689 // Warning: direct manipulation of MXF structures can interfere
690 // with the normal operation of the wrapper. Caveat emptor!
693 ASDCP::PCM::MXFWriter::RIP()
695 if ( m_Writer.empty() )
701 return m_Writer->m_RIP;
704 // Open the file for writing. The file must not exist. Returns error if
705 // the operation cannot be completed.
707 ASDCP::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
708 const AudioDescriptor& ADesc, ui32_t HeaderSize)
710 if ( Info.LabelSetType == LS_MXF_SMPTE )
711 m_Writer = new h__Writer(DefaultSMPTEDict());
713 m_Writer = new h__Writer(DefaultInteropDict());
715 m_Writer->m_Info = Info;
717 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
719 if ( ASDCP_SUCCESS(result) )
720 result = m_Writer->SetSourceStream(ADesc);
722 if ( ASDCP_FAILURE(result) )
728 // Writes a frame of essence to the MXF file. If the optional AESEncContext
729 // argument is present, the essence is encrypted prior to writing.
730 // Fails if the file is not open, is finalized, or an operating system
733 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
735 if ( m_Writer.empty() )
738 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
741 // Closes the MXF file, writing the index and other closing information.
743 ASDCP::PCM::MXFWriter::Finalize()
745 if ( m_Writer.empty() )
748 return m_Writer->Finalize();
752 // end AS_DCP_PCM.cpp