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_ATMOS.cpp
29 \brief AS-DCP library, Dolby Atmos essence reader and writer implementation
36 #include "AS_DCP_internal.h"
42 static std::string ATMOS_PACKAGE_LABEL = "File Package: SMPTE-GC frame wrapping of Dolby ATMOS data";
43 static std::string ATMOS_DEF_LABEL = "Dolby ATMOS Data Track";
44 static byte_t ATMOS_ESSENCE_CODING[SMPTE_UL_LENGTH] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05,
45 0x0e, 0x09, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00 };
51 ASDCP::ATMOS::operator << (std::ostream& strm, const AtmosDescriptor& ADesc)
54 strm << " EditRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
55 strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
56 strm << " DataEssenceCoding: " << UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40) << std::endl;
57 strm << " AtmosVersion: " << (unsigned) ADesc.AtmosVersion << std::endl;
58 strm << " MaxChannelCount: " << (unsigned) ADesc.MaxChannelCount << std::endl;
59 strm << " MaxObjectCount: " << (unsigned) ADesc.MaxObjectCount << std::endl;
60 strm << " AtmosID: " << UUID(ADesc.AtmosID).EncodeString(str_buf, 40) << std::endl;
61 strm << " FirstFrame: " << (unsigned) ADesc.FirstFrame << std::endl;
67 ASDCP::ATMOS::AtmosDescriptorDump(const AtmosDescriptor& ADesc, FILE* stream)
76 ContainerDuration: %u\n\
77 DataEssenceCoding: %s\n\
79 MaxChannelCount: %u\n\
83 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
84 ADesc.ContainerDuration,
85 UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40),
87 ADesc.MaxChannelCount,
89 UUID(ADesc.AtmosID).EncodeString(atmosID_buf, 40),
95 ASDCP::ATMOS::IsDolbyAtmos(const std::string& filename)
98 // For now use an atmos extension
99 bool result = ( 0 == (std::string("atmos").compare(Kumu::PathGetExtension(filename))) );
104 //------------------------------------------------------------------------------------------
106 typedef std::list<MXF::InterchangeObject*> SubDescriptorList_t;
108 class ASDCP::ATMOS::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
110 MXF::PrivateDCDataDescriptor* m_EssenceDescriptor;
111 MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
113 KM_NO_COPY_CONSTRUCT(h__Reader);
117 ASDCP::DCData::DCDataDescriptor m_DDesc;
118 AtmosDescriptor m_ADesc;
120 h__Reader(const Dictionary& d) :
121 ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0) {}
122 virtual ~h__Reader() {}
123 Result_t OpenRead(const std::string&);
124 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
125 Result_t MD_to_DCData_DDesc(ASDCP::DCData::DCDataDescriptor& DDesc);
126 Result_t MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc);
130 ASDCP::ATMOS::MXFReader::h__Reader::MD_to_DCData_DDesc(ASDCP::DCData::DCDataDescriptor& DDesc)
132 ASDCP_TEST_NULL(m_EssenceDescriptor);
133 MXF::PrivateDCDataDescriptor* DDescObj = m_EssenceDescriptor;
134 DDesc.EditRate = DDescObj->SampleRate;
135 assert(DDescObj->ContainerDuration <= 0xFFFFFFFFL);
136 DDesc.ContainerDuration = static_cast<ui32_t>(DDescObj->ContainerDuration);
137 memcpy(DDesc.DataEssenceCoding, DDescObj->DataEssenceCoding.Value(), SMPTE_UL_LENGTH);
142 ASDCP::ATMOS::MXFReader::h__Reader::MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc)
144 ASDCP_TEST_NULL(m_EssenceSubDescriptor);
145 Result_t result = MD_to_DCData_DDesc(ADesc);
146 if( ASDCP_SUCCESS(result) )
148 MXF::DolbyAtmosSubDescriptor* ADescObj = m_EssenceSubDescriptor;
149 ADesc.MaxChannelCount = ADescObj->MaxChannelCount;
150 ADesc.MaxObjectCount = ADescObj->MaxObjectCount;
151 ::memcpy(ADesc.AtmosID, ADescObj->AtmosID.Value(), UUIDlen);
152 ADesc.AtmosVersion = ADescObj->AtmosVersion;
153 ADesc.FirstFrame = ADescObj->FirstFrame;
161 ASDCP::ATMOS::MXFReader::h__Reader::OpenRead(const std::string& filename)
163 Result_t result = OpenMXFRead(filename);
164 m_EssenceDescriptor = 0;
166 if ( KM_SUCCESS(result) )
168 InterchangeObject* iObj = 0;
169 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(PrivateDCDataDescriptor), &iObj);
171 if ( KM_SUCCESS(result) )
173 m_EssenceDescriptor = static_cast<MXF::PrivateDCDataDescriptor*>(iObj);
177 if ( m_EssenceDescriptor == 0 )
179 DefaultLogSink().Error("DCDataDescriptor object not found in Atmos file.\n");
180 result = RESULT_FORMAT;
183 if ( KM_SUCCESS(result) )
185 result = MD_to_DCData_DDesc(m_DDesc);
188 // check for sample/frame rate sanity
189 if ( ASDCP_SUCCESS(result)
190 && m_DDesc.EditRate != EditRate_24
191 && m_DDesc.EditRate != EditRate_25
192 && m_DDesc.EditRate != EditRate_30
193 && m_DDesc.EditRate != EditRate_48
194 && m_DDesc.EditRate != EditRate_50
195 && m_DDesc.EditRate != EditRate_60
196 && m_DDesc.EditRate != EditRate_96
197 && m_DDesc.EditRate != EditRate_100
198 && m_DDesc.EditRate != EditRate_120
199 && m_DDesc.EditRate != EditRate_192
200 && m_DDesc.EditRate != EditRate_200
201 && m_DDesc.EditRate != EditRate_240 )
203 DefaultLogSink().Error("DC Data file EditRate is not a supported value: %d/%d\n", // lu
204 m_DDesc.EditRate.Numerator, m_DDesc.EditRate.Denominator);
206 return RESULT_FORMAT;
209 if( ASDCP_SUCCESS(result) )
212 if (NULL == m_EssenceSubDescriptor)
214 InterchangeObject* iObj = NULL;
215 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor), &iObj);
216 m_EssenceSubDescriptor = static_cast<MXF::DolbyAtmosSubDescriptor*>(iObj);
220 DefaultLogSink().Error("DolbyAtmosSubDescriptor object not found.\n");
221 return RESULT_FORMAT;
225 if ( ASDCP_SUCCESS(result) )
227 result = MD_to_Atmos_ADesc(m_ADesc);
236 ASDCP::ATMOS::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
237 AESDecContext* Ctx, HMACContext* HMAC)
239 if ( ! m_File.IsOpen() )
243 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_PrivateDCDataEssence), Ctx, HMAC);
247 //------------------------------------------------------------------------------------------
249 ASDCP::ATMOS::MXFReader::MXFReader()
251 m_Reader = new h__Reader(DefaultSMPTEDict());
255 ASDCP::ATMOS::MXFReader::~MXFReader()
257 if ( m_Reader && m_Reader->m_File.IsOpen() )
261 // Warning: direct manipulation of MXF structures can interfere
262 // with the normal operation of the wrapper. Caveat emptor!
264 ASDCP::MXF::OP1aHeader&
265 ASDCP::ATMOS::MXFReader::OP1aHeader()
267 if ( m_Reader.empty() )
269 assert(g_OP1aHeader);
270 return *g_OP1aHeader;
273 return m_Reader->m_HeaderPart;
276 // Warning: direct manipulation of MXF structures can interfere
277 // with the normal operation of the wrapper. Caveat emptor!
279 ASDCP::MXF::OPAtomIndexFooter&
280 ASDCP::ATMOS::MXFReader::OPAtomIndexFooter()
282 if ( m_Reader.empty() )
284 assert(g_OPAtomIndexFooter);
285 return *g_OPAtomIndexFooter;
288 return m_Reader->m_IndexAccess;
291 // Warning: direct manipulation of MXF structures can interfere
292 // with the normal operation of the wrapper. Caveat emptor!
295 ASDCP::ATMOS::MXFReader::RIP()
297 if ( m_Reader.empty() )
303 return m_Reader->m_RIP;
306 // Open the file for reading. The file must exist. Returns error if the
307 // operation cannot be completed.
309 ASDCP::ATMOS::MXFReader::OpenRead(const std::string& filename) const
311 return m_Reader->OpenRead(filename);
316 ASDCP::ATMOS::MXFReader::ReadFrame(ui32_t FrameNum, DCData::FrameBuffer& FrameBuf,
317 AESDecContext* Ctx, HMACContext* HMAC) const
319 if ( m_Reader && m_Reader->m_File.IsOpen() )
320 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
326 ASDCP::ATMOS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
328 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
332 // Fill the struct with the values from the file's header.
333 // Returns RESULT_INIT if the file is not open.
335 ASDCP::ATMOS::MXFReader::FillAtmosDescriptor(AtmosDescriptor& ADesc) const
337 if ( m_Reader && m_Reader->m_File.IsOpen() )
339 ADesc = m_Reader->m_ADesc;
347 // Fill the struct with the values from the file's header.
348 // Returns RESULT_INIT if the file is not open.
350 ASDCP::ATMOS::MXFReader::FillWriterInfo(WriterInfo& Info) const
352 if ( m_Reader && m_Reader->m_File.IsOpen() )
354 Info = m_Reader->m_Info;
363 ASDCP::ATMOS::MXFReader::DumpHeaderMetadata(FILE* stream) const
365 if ( m_Reader->m_File.IsOpen() )
366 m_Reader->m_HeaderPart.Dump(stream);
371 ASDCP::ATMOS::MXFReader::DumpIndex(FILE* stream) const
373 if ( m_Reader->m_File.IsOpen() )
374 m_Reader->m_IndexAccess.Dump(stream);
379 ASDCP::ATMOS::MXFReader::Close() const
381 if ( m_Reader && m_Reader->m_File.IsOpen() )
391 //------------------------------------------------------------------------------------------
394 class ASDCP::ATMOS::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
396 ASDCP::DCData::DCDataDescriptor m_DDesc;
397 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
398 MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
400 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
404 AtmosDescriptor m_ADesc;
406 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d),
407 m_EssenceSubDescriptor(0), m_ADesc() {
408 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
411 virtual ~h__Writer(){}
413 Result_t OpenWrite(const std::string&, ui32_t HeaderSize, const AtmosDescriptor& ADesc);
414 Result_t SetSourceStream(const DCData::DCDataDescriptor&, const byte_t*, const std::string&, const std::string&);
415 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
417 Result_t DCData_DDesc_to_MD(ASDCP::DCData::DCDataDescriptor& DDesc);
418 Result_t Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc);
423 ASDCP::ATMOS::MXFWriter::h__Writer::DCData_DDesc_to_MD(ASDCP::DCData::DCDataDescriptor& DDesc)
425 ASDCP_TEST_NULL(m_EssenceDescriptor);
426 MXF::PrivateDCDataDescriptor* DDescObj = static_cast<MXF::PrivateDCDataDescriptor *>(m_EssenceDescriptor);
428 DDescObj->SampleRate = DDesc.EditRate;
429 DDescObj->ContainerDuration = DDesc.ContainerDuration;
430 DDescObj->DataEssenceCoding.Set(DDesc.DataEssenceCoding);
437 ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc)
439 ASDCP_TEST_NULL(m_EssenceDescriptor);
440 ASDCP_TEST_NULL(m_EssenceSubDescriptor);
441 MXF::DolbyAtmosSubDescriptor * ADescObj = m_EssenceSubDescriptor;
442 ADescObj->MaxChannelCount = ADesc.MaxChannelCount;
443 ADescObj->MaxObjectCount = ADesc.MaxObjectCount;
444 ADescObj->AtmosID.Set(ADesc.AtmosID);
445 ADescObj->AtmosVersion = ADesc.AtmosVersion;
446 ADescObj->FirstFrame = ADesc.FirstFrame;
452 ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc)
454 if ( ! m_State.Test_BEGIN() )
457 Result_t result = m_File.OpenWrite(filename);
459 if ( ASDCP_SUCCESS(result) )
461 m_HeaderSize = HeaderSize;
462 m_EssenceDescriptor = new MXF::PrivateDCDataDescriptor(m_Dict);
463 m_EssenceSubDescriptor = new DolbyAtmosSubDescriptor(m_Dict);
464 SubDescriptorList_t subDescriptors;
465 subDescriptors.push_back(m_EssenceSubDescriptor);
467 SubDescriptorList_t::const_iterator sDObj;
468 SubDescriptorList_t::const_iterator lastDescriptor = subDescriptors.end();
469 for (sDObj = subDescriptors.begin(); sDObj != lastDescriptor; ++sDObj)
471 m_EssenceSubDescriptorList.push_back(*sDObj);
472 GenRandomValue((*sDObj)->InstanceUID);
473 m_EssenceDescriptor->SubDescriptors.push_back((*sDObj)->InstanceUID);
475 result = m_State.Goto_INIT();
478 if ( ASDCP_FAILURE(result) )
479 delete m_EssenceSubDescriptor;
481 if ( ASDCP_SUCCESS(result) )
484 memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH);
485 result = Atmos_ADesc_to_MD(m_ADesc);
493 ASDCP::ATMOS::MXFWriter::h__Writer::SetSourceStream(ASDCP::DCData::DCDataDescriptor const& DDesc,
494 const byte_t * essenceCoding,
495 const std::string& packageLabel,
496 const std::string& defLabel)
498 if ( ! m_State.Test_INIT() )
501 if ( DDesc.EditRate != EditRate_24
502 && DDesc.EditRate != EditRate_25
503 && DDesc.EditRate != EditRate_30
504 && DDesc.EditRate != EditRate_48
505 && DDesc.EditRate != EditRate_50
506 && DDesc.EditRate != EditRate_60
507 && DDesc.EditRate != EditRate_96
508 && DDesc.EditRate != EditRate_100
509 && DDesc.EditRate != EditRate_120
510 && DDesc.EditRate != EditRate_192
511 && DDesc.EditRate != EditRate_200
512 && DDesc.EditRate != EditRate_240 )
514 DefaultLogSink().Error("DCDataDescriptor.EditRate is not a supported value: %d/%d\n",
515 DDesc.EditRate.Numerator, DDesc.EditRate.Denominator);
516 return RESULT_RAW_FORMAT;
521 if (NULL != essenceCoding)
522 memcpy(m_DDesc.DataEssenceCoding, essenceCoding, SMPTE_UL_LENGTH);
523 Result_t result = DCData_DDesc_to_MD(m_DDesc);
525 if ( ASDCP_SUCCESS(result) )
527 memcpy(m_EssenceUL, m_Dict->ul(MDD_PrivateDCDataEssence), SMPTE_UL_LENGTH);
528 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
529 result = m_State.Goto_READY();
532 if ( ASDCP_SUCCESS(result) )
534 ui32_t TCFrameRate = m_DDesc.EditRate.Numerator;
536 result = WriteASDCPHeader(packageLabel, UL(m_Dict->ul(MDD_PrivateDCDataWrappingFrame)),
537 defLabel, UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)),
538 m_DDesc.EditRate, TCFrameRate);
546 ASDCP::ATMOS::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf,
547 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
549 Result_t result = RESULT_OK;
551 if ( m_State.Test_READY() )
552 result = m_State.Goto_RUNNING(); // first time through
554 ui64_t StreamOffset = m_StreamOffset;
556 if ( ASDCP_SUCCESS(result) )
557 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
559 if ( ASDCP_SUCCESS(result) )
561 IndexTableSegment::IndexEntry Entry;
562 Entry.StreamOffset = StreamOffset;
563 m_FooterPart.PushIndexEntry(Entry);
569 // Closes the MXF file, writing the index and other closing information.
572 ASDCP::ATMOS::MXFWriter::h__Writer::Finalize()
574 if ( ! m_State.Test_RUNNING() )
577 m_State.Goto_FINAL();
579 return WriteASDCPFooter();
584 //------------------------------------------------------------------------------------------
586 ASDCP::ATMOS::MXFWriter::MXFWriter()
590 ASDCP::ATMOS::MXFWriter::~MXFWriter()
594 // Warning: direct manipulation of MXF structures can interfere
595 // with the normal operation of the wrapper. Caveat emptor!
597 ASDCP::MXF::OP1aHeader&
598 ASDCP::ATMOS::MXFWriter::OP1aHeader()
600 if ( m_Writer.empty() )
602 assert(g_OP1aHeader);
603 return *g_OP1aHeader;
606 return m_Writer->m_HeaderPart;
609 // Warning: direct manipulation of MXF structures can interfere
610 // with the normal operation of the wrapper. Caveat emptor!
612 ASDCP::MXF::OPAtomIndexFooter&
613 ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter()
615 if ( m_Writer.empty() )
617 assert(g_OPAtomIndexFooter);
618 return *g_OPAtomIndexFooter;
621 return m_Writer->m_FooterPart;
624 // Warning: direct manipulation of MXF structures can interfere
625 // with the normal operation of the wrapper. Caveat emptor!
628 ASDCP::ATMOS::MXFWriter::RIP()
630 if ( m_Writer.empty() )
636 return m_Writer->m_RIP;
639 // Open the file for writing. The file must not exist. Returns error if
640 // the operation cannot be completed.
642 ASDCP::ATMOS::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
643 const AtmosDescriptor& ADesc, ui32_t HeaderSize)
645 if ( Info.LabelSetType != LS_MXF_SMPTE )
647 DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n");
648 return RESULT_FORMAT;
651 m_Writer = new h__Writer(DefaultSMPTEDict());
652 m_Writer->m_Info = Info;
654 Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc);
656 if ( ASDCP_SUCCESS(result) )
657 result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL,
660 if ( ASDCP_FAILURE(result) )
666 // Writes a frame of essence to the MXF file. If the optional AESEncContext
667 // argument is present, the essence is encrypted prior to writing.
668 // Fails if the file is not open, is finalized, or an operating system
671 ASDCP::ATMOS::MXFWriter::WriteFrame(const DCData::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
673 if ( m_Writer.empty() )
676 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
679 // Closes the MXF file, writing the index and other closing information.
681 ASDCP::ATMOS::MXFWriter::Finalize()
683 if ( m_Writer.empty() )
686 return m_Writer->Finalize();
691 // end AS_DCP_ATMOS.cpp