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(AtmosSMPTEDict());
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);
436 ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc)
438 ASDCP_TEST_NULL(m_EssenceDescriptor);
439 ASDCP_TEST_NULL(m_EssenceSubDescriptor);
440 MXF::DolbyAtmosSubDescriptor * ADescObj = m_EssenceSubDescriptor;
441 ADescObj->MaxChannelCount = ADesc.MaxChannelCount;
442 ADescObj->MaxObjectCount = ADesc.MaxObjectCount;
443 ADescObj->AtmosID.Set(ADesc.AtmosID);
444 ADescObj->AtmosVersion = ADesc.AtmosVersion;
445 ADescObj->FirstFrame = ADesc.FirstFrame;
451 ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc)
453 if ( ! m_State.Test_BEGIN() )
456 Result_t result = m_File.OpenWrite(filename);
458 if ( ASDCP_SUCCESS(result) )
460 m_HeaderSize = HeaderSize;
461 m_EssenceDescriptor = new MXF::PrivateDCDataDescriptor(m_Dict);
462 m_EssenceSubDescriptor = new DolbyAtmosSubDescriptor(m_Dict);
463 SubDescriptorList_t subDescriptors;
464 subDescriptors.push_back(m_EssenceSubDescriptor);
466 SubDescriptorList_t::const_iterator sDObj;
467 SubDescriptorList_t::const_iterator lastDescriptor = subDescriptors.end();
468 for (sDObj = subDescriptors.begin(); sDObj != lastDescriptor; ++sDObj)
470 m_EssenceSubDescriptorList.push_back(*sDObj);
471 GenRandomValue((*sDObj)->InstanceUID);
472 m_EssenceDescriptor->SubDescriptors.push_back((*sDObj)->InstanceUID);
474 result = m_State.Goto_INIT();
477 if ( ASDCP_FAILURE(result) )
478 delete m_EssenceSubDescriptor;
480 if ( ASDCP_SUCCESS(result) )
483 memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH);
484 result = Atmos_ADesc_to_MD(m_ADesc);
492 ASDCP::ATMOS::MXFWriter::h__Writer::SetSourceStream(ASDCP::DCData::DCDataDescriptor const& DDesc,
493 const byte_t * essenceCoding,
494 const std::string& packageLabel,
495 const std::string& defLabel)
497 if ( ! m_State.Test_INIT() )
500 if ( DDesc.EditRate != EditRate_24
501 && DDesc.EditRate != EditRate_25
502 && DDesc.EditRate != EditRate_30
503 && DDesc.EditRate != EditRate_48
504 && DDesc.EditRate != EditRate_50
505 && DDesc.EditRate != EditRate_60
506 && DDesc.EditRate != EditRate_96
507 && DDesc.EditRate != EditRate_100
508 && DDesc.EditRate != EditRate_120
509 && DDesc.EditRate != EditRate_192
510 && DDesc.EditRate != EditRate_200
511 && DDesc.EditRate != EditRate_240 )
513 DefaultLogSink().Error("DCDataDescriptor.EditRate is not a supported value: %d/%d\n",
514 DDesc.EditRate.Numerator, DDesc.EditRate.Denominator);
515 return RESULT_RAW_FORMAT;
520 if (NULL != essenceCoding)
521 memcpy(m_DDesc.DataEssenceCoding, essenceCoding, SMPTE_UL_LENGTH);
522 Result_t result = DCData_DDesc_to_MD(m_DDesc);
524 if ( ASDCP_SUCCESS(result) )
526 memcpy(m_EssenceUL, m_Dict->ul(MDD_PrivateDCDataEssence), SMPTE_UL_LENGTH);
527 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
528 result = m_State.Goto_READY();
531 if ( ASDCP_SUCCESS(result) )
533 ui32_t TCFrameRate = m_DDesc.EditRate.Numerator;
535 result = WriteASDCPHeader(packageLabel, UL(m_Dict->ul(MDD_PrivateDCDataWrappingFrame)),
536 defLabel, UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)),
537 m_DDesc.EditRate, TCFrameRate);
545 ASDCP::ATMOS::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf,
546 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
548 Result_t result = RESULT_OK;
550 if ( m_State.Test_READY() )
551 result = m_State.Goto_RUNNING(); // first time through
553 ui64_t StreamOffset = m_StreamOffset;
555 if ( ASDCP_SUCCESS(result) )
556 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
558 if ( ASDCP_SUCCESS(result) )
560 IndexTableSegment::IndexEntry Entry;
561 Entry.StreamOffset = StreamOffset;
562 m_FooterPart.PushIndexEntry(Entry);
568 // Closes the MXF file, writing the index and other closing information.
571 ASDCP::ATMOS::MXFWriter::h__Writer::Finalize()
573 if ( ! m_State.Test_RUNNING() )
576 m_State.Goto_FINAL();
578 return WriteASDCPFooter();
583 //------------------------------------------------------------------------------------------
585 ASDCP::ATMOS::MXFWriter::MXFWriter()
589 ASDCP::ATMOS::MXFWriter::~MXFWriter()
593 // Warning: direct manipulation of MXF structures can interfere
594 // with the normal operation of the wrapper. Caveat emptor!
596 ASDCP::MXF::OP1aHeader&
597 ASDCP::ATMOS::MXFWriter::OP1aHeader()
599 if ( m_Writer.empty() )
601 assert(g_OP1aHeader);
602 return *g_OP1aHeader;
605 return m_Writer->m_HeaderPart;
608 // Warning: direct manipulation of MXF structures can interfere
609 // with the normal operation of the wrapper. Caveat emptor!
611 ASDCP::MXF::OPAtomIndexFooter&
612 ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter()
614 if ( m_Writer.empty() )
616 assert(g_OPAtomIndexFooter);
617 return *g_OPAtomIndexFooter;
620 return m_Writer->m_FooterPart;
623 // Warning: direct manipulation of MXF structures can interfere
624 // with the normal operation of the wrapper. Caveat emptor!
627 ASDCP::ATMOS::MXFWriter::RIP()
629 if ( m_Writer.empty() )
635 return m_Writer->m_RIP;
638 // Open the file for writing. The file must not exist. Returns error if
639 // the operation cannot be completed.
641 ASDCP::ATMOS::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
642 const AtmosDescriptor& ADesc, ui32_t HeaderSize)
644 if ( Info.LabelSetType != LS_MXF_SMPTE )
646 DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n");
647 return RESULT_FORMAT;
650 m_Writer = new h__Writer(AtmosSMPTEDict());
651 m_Writer->m_Info = Info;
653 Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc);
655 if ( ASDCP_SUCCESS(result) )
656 result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL,
659 if ( ASDCP_FAILURE(result) )
665 // Writes a frame of essence to the MXF file. If the optional AESEncContext
666 // argument is present, the essence is encrypted prior to writing.
667 // Fails if the file is not open, is finalized, or an operating system
670 ASDCP::ATMOS::MXFWriter::WriteFrame(const DCData::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
672 if ( m_Writer.empty() )
675 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
678 // Closes the MXF file, writing the index and other closing information.
680 ASDCP::ATMOS::MXFWriter::Finalize()
682 if ( m_Writer.empty() )
685 return m_Writer->Finalize();
690 // end AS_DCP_ATMOS.cpp