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 )
200 DefaultLogSink().Error("DC Data file EditRate is not a supported value: %d/%d\n", // lu
201 m_DDesc.EditRate.Numerator, m_DDesc.EditRate.Denominator);
203 return RESULT_FORMAT;
206 if( ASDCP_SUCCESS(result) )
209 if (NULL == m_EssenceSubDescriptor)
211 InterchangeObject* iObj = NULL;
212 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor), &iObj);
213 m_EssenceSubDescriptor = static_cast<MXF::DolbyAtmosSubDescriptor*>(iObj);
217 DefaultLogSink().Error("DolbyAtmosSubDescriptor object not found.\n");
218 return RESULT_FORMAT;
222 if ( ASDCP_SUCCESS(result) )
224 result = MD_to_Atmos_ADesc(m_ADesc);
233 ASDCP::ATMOS::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
234 AESDecContext* Ctx, HMACContext* HMAC)
236 if ( ! m_File.IsOpen() )
240 return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_PrivateDCDataEssence), Ctx, HMAC);
244 //------------------------------------------------------------------------------------------
246 ASDCP::ATMOS::MXFReader::MXFReader()
248 m_Reader = new h__Reader(DefaultSMPTEDict());
252 ASDCP::ATMOS::MXFReader::~MXFReader()
254 if ( m_Reader && m_Reader->m_File.IsOpen() )
258 // Warning: direct manipulation of MXF structures can interfere
259 // with the normal operation of the wrapper. Caveat emptor!
261 ASDCP::MXF::OP1aHeader&
262 ASDCP::ATMOS::MXFReader::OP1aHeader()
264 if ( m_Reader.empty() )
266 assert(g_OP1aHeader);
267 return *g_OP1aHeader;
270 return m_Reader->m_HeaderPart;
273 // Warning: direct manipulation of MXF structures can interfere
274 // with the normal operation of the wrapper. Caveat emptor!
276 ASDCP::MXF::OPAtomIndexFooter&
277 ASDCP::ATMOS::MXFReader::OPAtomIndexFooter()
279 if ( m_Reader.empty() )
281 assert(g_OPAtomIndexFooter);
282 return *g_OPAtomIndexFooter;
285 return m_Reader->m_IndexAccess;
288 // Warning: direct manipulation of MXF structures can interfere
289 // with the normal operation of the wrapper. Caveat emptor!
292 ASDCP::ATMOS::MXFReader::RIP()
294 if ( m_Reader.empty() )
300 return m_Reader->m_RIP;
303 // Open the file for reading. The file must exist. Returns error if the
304 // operation cannot be completed.
306 ASDCP::ATMOS::MXFReader::OpenRead(const std::string& filename) const
308 return m_Reader->OpenRead(filename);
313 ASDCP::ATMOS::MXFReader::ReadFrame(ui32_t FrameNum, DCData::FrameBuffer& FrameBuf,
314 AESDecContext* Ctx, HMACContext* HMAC) const
316 if ( m_Reader && m_Reader->m_File.IsOpen() )
317 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
323 ASDCP::ATMOS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
325 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
329 // Fill the struct with the values from the file's header.
330 // Returns RESULT_INIT if the file is not open.
332 ASDCP::ATMOS::MXFReader::FillAtmosDescriptor(AtmosDescriptor& ADesc) const
334 if ( m_Reader && m_Reader->m_File.IsOpen() )
336 ADesc = m_Reader->m_ADesc;
344 // Fill the struct with the values from the file's header.
345 // Returns RESULT_INIT if the file is not open.
347 ASDCP::ATMOS::MXFReader::FillWriterInfo(WriterInfo& Info) const
349 if ( m_Reader && m_Reader->m_File.IsOpen() )
351 Info = m_Reader->m_Info;
360 ASDCP::ATMOS::MXFReader::DumpHeaderMetadata(FILE* stream) const
362 if ( m_Reader->m_File.IsOpen() )
363 m_Reader->m_HeaderPart.Dump(stream);
368 ASDCP::ATMOS::MXFReader::DumpIndex(FILE* stream) const
370 if ( m_Reader->m_File.IsOpen() )
371 m_Reader->m_IndexAccess.Dump(stream);
376 ASDCP::ATMOS::MXFReader::Close() const
378 if ( m_Reader && m_Reader->m_File.IsOpen() )
388 //------------------------------------------------------------------------------------------
391 class ASDCP::ATMOS::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
393 ASDCP::DCData::DCDataDescriptor m_DDesc;
394 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
395 MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
397 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
401 AtmosDescriptor m_ADesc;
403 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d),
404 m_EssenceSubDescriptor(0), m_ADesc() {
405 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
408 virtual ~h__Writer(){}
410 Result_t OpenWrite(const std::string&, ui32_t HeaderSize, const AtmosDescriptor& ADesc);
411 Result_t SetSourceStream(const DCData::DCDataDescriptor&, const byte_t*, const std::string&, const std::string&);
412 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
414 Result_t DCData_DDesc_to_MD(ASDCP::DCData::DCDataDescriptor& DDesc);
415 Result_t Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc);
420 ASDCP::ATMOS::MXFWriter::h__Writer::DCData_DDesc_to_MD(ASDCP::DCData::DCDataDescriptor& DDesc)
422 ASDCP_TEST_NULL(m_EssenceDescriptor);
423 MXF::PrivateDCDataDescriptor* DDescObj = static_cast<MXF::PrivateDCDataDescriptor *>(m_EssenceDescriptor);
425 DDescObj->SampleRate = DDesc.EditRate;
426 DDescObj->ContainerDuration = DDesc.ContainerDuration;
427 DDescObj->DataEssenceCoding.Set(DDesc.DataEssenceCoding);
434 ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc)
436 ASDCP_TEST_NULL(m_EssenceDescriptor);
437 ASDCP_TEST_NULL(m_EssenceSubDescriptor);
438 MXF::DolbyAtmosSubDescriptor * ADescObj = m_EssenceSubDescriptor;
439 ADescObj->MaxChannelCount = ADesc.MaxChannelCount;
440 ADescObj->MaxObjectCount = ADesc.MaxObjectCount;
441 ADescObj->AtmosID.Set(ADesc.AtmosID);
442 ADescObj->AtmosVersion = ADesc.AtmosVersion;
443 ADescObj->FirstFrame = ADesc.FirstFrame;
449 ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc)
451 if ( ! m_State.Test_BEGIN() )
454 Result_t result = m_File.OpenWrite(filename);
456 if ( ASDCP_SUCCESS(result) )
458 m_HeaderSize = HeaderSize;
459 m_EssenceDescriptor = new MXF::PrivateDCDataDescriptor(m_Dict);
460 m_EssenceSubDescriptor = new DolbyAtmosSubDescriptor(m_Dict);
461 SubDescriptorList_t subDescriptors;
462 subDescriptors.push_back(m_EssenceSubDescriptor);
464 SubDescriptorList_t::const_iterator sDObj;
465 SubDescriptorList_t::const_iterator lastDescriptor = subDescriptors.end();
466 for (sDObj = subDescriptors.begin(); sDObj != lastDescriptor; ++sDObj)
468 m_EssenceSubDescriptorList.push_back(*sDObj);
469 GenRandomValue((*sDObj)->InstanceUID);
470 m_EssenceDescriptor->SubDescriptors.push_back((*sDObj)->InstanceUID);
472 result = m_State.Goto_INIT();
475 if ( ASDCP_FAILURE(result) )
476 delete m_EssenceSubDescriptor;
478 if ( ASDCP_SUCCESS(result) )
481 memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH);
482 result = Atmos_ADesc_to_MD(m_ADesc);
490 ASDCP::ATMOS::MXFWriter::h__Writer::SetSourceStream(ASDCP::DCData::DCDataDescriptor const& DDesc,
491 const byte_t * essenceCoding,
492 const std::string& packageLabel,
493 const std::string& defLabel)
495 if ( ! m_State.Test_INIT() )
498 if ( DDesc.EditRate != EditRate_24
499 && DDesc.EditRate != EditRate_25
500 && DDesc.EditRate != EditRate_30
501 && DDesc.EditRate != EditRate_48
502 && DDesc.EditRate != EditRate_50
503 && DDesc.EditRate != EditRate_60
504 && DDesc.EditRate != EditRate_96
505 && DDesc.EditRate != EditRate_100
506 && DDesc.EditRate != EditRate_120 )
508 DefaultLogSink().Error("DCDataDescriptor.EditRate is not a supported value: %d/%d\n",
509 DDesc.EditRate.Numerator, DDesc.EditRate.Denominator);
510 return RESULT_RAW_FORMAT;
515 if (NULL != essenceCoding)
516 memcpy(m_DDesc.DataEssenceCoding, essenceCoding, SMPTE_UL_LENGTH);
517 Result_t result = DCData_DDesc_to_MD(m_DDesc);
519 if ( ASDCP_SUCCESS(result) )
521 memcpy(m_EssenceUL, m_Dict->ul(MDD_PrivateDCDataEssence), SMPTE_UL_LENGTH);
522 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
523 result = m_State.Goto_READY();
526 if ( ASDCP_SUCCESS(result) )
528 ui32_t TCFrameRate = m_DDesc.EditRate.Numerator;
530 result = WriteASDCPHeader(packageLabel, UL(m_Dict->ul(MDD_PrivateDCDataWrappingFrame)),
531 defLabel, UL(m_EssenceUL), UL(m_Dict->ul(MDD_DataDataDef)),
532 m_DDesc.EditRate, TCFrameRate);
540 ASDCP::ATMOS::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf,
541 ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
543 Result_t result = RESULT_OK;
545 if ( m_State.Test_READY() )
546 result = m_State.Goto_RUNNING(); // first time through
548 ui64_t StreamOffset = m_StreamOffset;
550 if ( ASDCP_SUCCESS(result) )
551 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
553 if ( ASDCP_SUCCESS(result) )
555 IndexTableSegment::IndexEntry Entry;
556 Entry.StreamOffset = StreamOffset;
557 m_FooterPart.PushIndexEntry(Entry);
563 // Closes the MXF file, writing the index and other closing information.
566 ASDCP::ATMOS::MXFWriter::h__Writer::Finalize()
568 if ( ! m_State.Test_RUNNING() )
571 m_State.Goto_FINAL();
573 return WriteASDCPFooter();
578 //------------------------------------------------------------------------------------------
580 ASDCP::ATMOS::MXFWriter::MXFWriter()
584 ASDCP::ATMOS::MXFWriter::~MXFWriter()
588 // Warning: direct manipulation of MXF structures can interfere
589 // with the normal operation of the wrapper. Caveat emptor!
591 ASDCP::MXF::OP1aHeader&
592 ASDCP::ATMOS::MXFWriter::OP1aHeader()
594 if ( m_Writer.empty() )
596 assert(g_OP1aHeader);
597 return *g_OP1aHeader;
600 return m_Writer->m_HeaderPart;
603 // Warning: direct manipulation of MXF structures can interfere
604 // with the normal operation of the wrapper. Caveat emptor!
606 ASDCP::MXF::OPAtomIndexFooter&
607 ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter()
609 if ( m_Writer.empty() )
611 assert(g_OPAtomIndexFooter);
612 return *g_OPAtomIndexFooter;
615 return m_Writer->m_FooterPart;
618 // Warning: direct manipulation of MXF structures can interfere
619 // with the normal operation of the wrapper. Caveat emptor!
622 ASDCP::ATMOS::MXFWriter::RIP()
624 if ( m_Writer.empty() )
630 return m_Writer->m_RIP;
633 // Open the file for writing. The file must not exist. Returns error if
634 // the operation cannot be completed.
636 ASDCP::ATMOS::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
637 const AtmosDescriptor& ADesc, ui32_t HeaderSize)
639 if ( Info.LabelSetType != LS_MXF_SMPTE )
641 DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n");
642 return RESULT_FORMAT;
645 m_Writer = new h__Writer(DefaultSMPTEDict());
646 m_Writer->m_Info = Info;
648 Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc);
650 if ( ASDCP_SUCCESS(result) )
651 result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL,
654 if ( ASDCP_FAILURE(result) )
660 // Writes a frame of essence to the MXF file. If the optional AESEncContext
661 // argument is present, the essence is encrypted prior to writing.
662 // Fails if the file is not open, is finalized, or an operating system
665 ASDCP::ATMOS::MXFWriter::WriteFrame(const DCData::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
667 if ( m_Writer.empty() )
670 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
673 // Closes the MXF file, writing the index and other closing information.
675 ASDCP::ATMOS::MXFWriter::Finalize()
677 if ( m_Writer.empty() )
680 return m_Writer->Finalize();
685 // end AS_DCP_ATMOS.cpp