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_ATMOS.cpp
29 \brief AS-DCP library, Dolby Atmos essence reader and writer implementation
36 #include "AS_DCP_DCData_internal.h"
37 #include "AS_DCP_internal.h"
43 static std::string ATMOS_PACKAGE_LABEL = "File Package: SMPTE-GC frame wrapping of Dolby ATMOS data";
44 static std::string ATMOS_DEF_LABEL = "Dolby ATMOS Data Track";
45 static byte_t ATMOS_ESSENCE_CODING[SMPTE_UL_LENGTH] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05,
46 0x0e, 0x09, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00 };
52 ASDCP::ATMOS::operator << (std::ostream& strm, const AtmosDescriptor& ADesc)
55 strm << " EditRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
56 strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
57 strm << " DataEssenceCoding: " << UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40) << std::endl;
58 strm << " AtmosVersion: " << (unsigned) ADesc.AtmosVersion << std::endl;
59 strm << " MaxChannelCount: " << (unsigned) ADesc.MaxChannelCount << std::endl;
60 strm << " MaxObjectCount: " << (unsigned) ADesc.MaxObjectCount << std::endl;
61 strm << " AtmosID: " << UUID(ADesc.AtmosID).EncodeString(str_buf, 40) << std::endl;
62 strm << " FirstFrame: " << (unsigned) ADesc.FirstFrame << std::endl;
68 ASDCP::ATMOS::AtmosDescriptorDump(const AtmosDescriptor& ADesc, FILE* stream)
77 ContainerDuration: %u\n\
78 DataEssenceCoding: %s\n\
80 MaxChannelCount: %u\n\
84 ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
85 ADesc.ContainerDuration,
86 UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40),
88 ADesc.MaxChannelCount,
90 UUID(ADesc.AtmosID).EncodeString(atmosID_buf, 40),
96 ASDCP::ATMOS::IsDolbyAtmos(const char* filename)
99 // For now use an atmos extension
100 bool result = (0 == (std::string("atmos").compare(Kumu::PathGetExtension(std::string(filename)))));
105 //------------------------------------------------------------------------------------------
108 class ASDCP::ATMOS::MXFReader::h__Reader : public ASDCP::DCData::h__Reader
110 MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
112 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
116 AtmosDescriptor m_ADesc;
118 h__Reader(const Dictionary& d) : DCData::h__Reader(d), m_EssenceSubDescriptor(NULL),
120 virtual ~h__Reader() {}
121 Result_t OpenRead(const char*);
122 Result_t MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc);
126 ASDCP::ATMOS::MXFReader::h__Reader::MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc)
128 ASDCP_TEST_NULL(m_EssenceSubDescriptor);
129 Result_t result = MD_to_DCData_DDesc(ADesc);
130 if( ASDCP_SUCCESS(result) )
132 MXF::DolbyAtmosSubDescriptor* ADescObj = m_EssenceSubDescriptor;
133 ADesc.MaxChannelCount = ADescObj->MaxChannelCount;
134 ADesc.MaxObjectCount = ADescObj->MaxObjectCount;
135 ::memcpy(ADesc.AtmosID, ADescObj->AtmosID.Value(), UUIDlen);
136 ADesc.AtmosVersion = ADescObj->AtmosVersion;
137 ADesc.FirstFrame = ADescObj->FirstFrame;
145 ASDCP::ATMOS::MXFReader::h__Reader::OpenRead(const char* filename)
147 Result_t result = DCData::h__Reader::OpenRead(filename);
149 if( ASDCP_SUCCESS(result) )
152 if (NULL == m_EssenceSubDescriptor)
154 InterchangeObject* iObj = NULL;
155 result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor), &iObj);
156 m_EssenceSubDescriptor = static_cast<MXF::DolbyAtmosSubDescriptor*>(iObj);
160 DefaultLogSink().Error("DolbyAtmosSubDescriptor object not found.\n");
161 return RESULT_FORMAT;
165 if ( ASDCP_SUCCESS(result) )
167 result = MD_to_Atmos_ADesc(m_ADesc);
175 //------------------------------------------------------------------------------------------
177 ASDCP::ATMOS::MXFReader::MXFReader()
179 m_Reader = new h__Reader(DefaultSMPTEDict());
183 ASDCP::ATMOS::MXFReader::~MXFReader()
185 if ( m_Reader && m_Reader->m_File.IsOpen() )
189 // Warning: direct manipulation of MXF structures can interfere
190 // with the normal operation of the wrapper. Caveat emptor!
192 ASDCP::MXF::OP1aHeader&
193 ASDCP::ATMOS::MXFReader::OP1aHeader()
195 if ( m_Reader.empty() )
197 assert(g_OP1aHeader);
198 return *g_OP1aHeader;
201 return m_Reader->m_HeaderPart;
204 // Warning: direct manipulation of MXF structures can interfere
205 // with the normal operation of the wrapper. Caveat emptor!
207 ASDCP::MXF::OPAtomIndexFooter&
208 ASDCP::ATMOS::MXFReader::OPAtomIndexFooter()
210 if ( m_Reader.empty() )
212 assert(g_OPAtomIndexFooter);
213 return *g_OPAtomIndexFooter;
216 return m_Reader->m_IndexAccess;
219 // Warning: direct manipulation of MXF structures can interfere
220 // with the normal operation of the wrapper. Caveat emptor!
223 ASDCP::ATMOS::MXFReader::RIP()
225 if ( m_Reader.empty() )
231 return m_Reader->m_RIP;
234 // Open the file for reading. The file must exist. Returns error if the
235 // operation cannot be completed.
237 ASDCP::ATMOS::MXFReader::OpenRead(const char* filename) const
239 return m_Reader->OpenRead(filename);
244 ASDCP::ATMOS::MXFReader::ReadFrame(ui32_t FrameNum, DCData::FrameBuffer& FrameBuf,
245 AESDecContext* Ctx, HMACContext* HMAC) const
247 if ( m_Reader && m_Reader->m_File.IsOpen() )
248 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
254 ASDCP::ATMOS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
256 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
260 // Fill the struct with the values from the file's header.
261 // Returns RESULT_INIT if the file is not open.
263 ASDCP::ATMOS::MXFReader::FillAtmosDescriptor(AtmosDescriptor& ADesc) const
265 if ( m_Reader && m_Reader->m_File.IsOpen() )
267 ADesc = m_Reader->m_ADesc;
275 // Fill the struct with the values from the file's header.
276 // Returns RESULT_INIT if the file is not open.
278 ASDCP::ATMOS::MXFReader::FillWriterInfo(WriterInfo& Info) const
280 if ( m_Reader && m_Reader->m_File.IsOpen() )
282 Info = m_Reader->m_Info;
291 ASDCP::ATMOS::MXFReader::DumpHeaderMetadata(FILE* stream) const
293 if ( m_Reader->m_File.IsOpen() )
294 m_Reader->m_HeaderPart.Dump(stream);
299 ASDCP::ATMOS::MXFReader::DumpIndex(FILE* stream) const
301 if ( m_Reader->m_File.IsOpen() )
302 m_Reader->m_IndexAccess.Dump(stream);
307 ASDCP::ATMOS::MXFReader::Close() const
309 if ( m_Reader && m_Reader->m_File.IsOpen() )
319 //------------------------------------------------------------------------------------------
322 class ASDCP::ATMOS::MXFWriter::h__Writer : public DCData::h__Writer
324 MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
326 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
330 AtmosDescriptor m_ADesc;
332 h__Writer(const Dictionary& d) : DCData::h__Writer(d),
333 m_EssenceSubDescriptor(NULL), m_ADesc() {}
335 virtual ~h__Writer(){}
337 Result_t OpenWrite(const char*, ui32_t HeaderSize, const AtmosDescriptor& ADesc);
338 Result_t Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc);
343 ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc)
345 ASDCP_TEST_NULL(m_EssenceDescriptor);
346 ASDCP_TEST_NULL(m_EssenceSubDescriptor);
347 MXF::DolbyAtmosSubDescriptor * ADescObj = m_EssenceSubDescriptor;
348 ADescObj->MaxChannelCount = ADesc.MaxChannelCount;
349 ADescObj->MaxObjectCount = ADesc.MaxObjectCount;
350 ADescObj->AtmosID.Set(ADesc.AtmosID);
351 ADescObj->AtmosVersion = ADesc.AtmosVersion;
352 ADescObj->FirstFrame = ADesc.FirstFrame;
358 ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc)
361 m_EssenceSubDescriptor = new DolbyAtmosSubDescriptor(m_Dict);
362 DCData::SubDescriptorList_t subDescriptors;
363 subDescriptors.push_back(m_EssenceSubDescriptor);
364 Result_t result = DCData::h__Writer::OpenWrite(filename, HeaderSize, subDescriptors);
365 if ( ASDCP_FAILURE(result) )
366 delete m_EssenceSubDescriptor;
368 if ( ASDCP_SUCCESS(result) )
371 memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH);
372 result = Atmos_ADesc_to_MD(m_ADesc);
381 //------------------------------------------------------------------------------------------
383 ASDCP::ATMOS::MXFWriter::MXFWriter()
387 ASDCP::ATMOS::MXFWriter::~MXFWriter()
391 // Warning: direct manipulation of MXF structures can interfere
392 // with the normal operation of the wrapper. Caveat emptor!
394 ASDCP::MXF::OP1aHeader&
395 ASDCP::ATMOS::MXFWriter::OP1aHeader()
397 if ( m_Writer.empty() )
399 assert(g_OP1aHeader);
400 return *g_OP1aHeader;
403 return m_Writer->m_HeaderPart;
406 // Warning: direct manipulation of MXF structures can interfere
407 // with the normal operation of the wrapper. Caveat emptor!
409 ASDCP::MXF::OPAtomIndexFooter&
410 ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter()
412 if ( m_Writer.empty() )
414 assert(g_OPAtomIndexFooter);
415 return *g_OPAtomIndexFooter;
418 return m_Writer->m_FooterPart;
421 // Warning: direct manipulation of MXF structures can interfere
422 // with the normal operation of the wrapper. Caveat emptor!
425 ASDCP::ATMOS::MXFWriter::RIP()
427 if ( m_Writer.empty() )
433 return m_Writer->m_RIP;
436 // Open the file for writing. The file must not exist. Returns error if
437 // the operation cannot be completed.
439 ASDCP::ATMOS::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
440 const AtmosDescriptor& ADesc, ui32_t HeaderSize)
442 if ( Info.LabelSetType != LS_MXF_SMPTE )
444 DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n");
445 return RESULT_FORMAT;
448 m_Writer = new h__Writer(DefaultSMPTEDict());
449 m_Writer->m_Info = Info;
451 Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc);
453 if ( ASDCP_SUCCESS(result) )
454 result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL,
457 if ( ASDCP_FAILURE(result) )
463 // Writes a frame of essence to the MXF file. If the optional AESEncContext
464 // argument is present, the essence is encrypted prior to writing.
465 // Fails if the file is not open, is finalized, or an operating system
468 ASDCP::ATMOS::MXFWriter::WriteFrame(const DCData::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
470 if ( m_Writer.empty() )
473 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
476 // Closes the MXF file, writing the index and other closing information.
478 ASDCP::ATMOS::MXFWriter::Finalize()
480 if ( m_Writer.empty() )
483 return m_Writer->Finalize();
488 // end AS_DCP_ATMOS.cpp