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);
159 if ( ASDCP_SUCCESS(result) )
161 result = MD_to_Atmos_ADesc(m_ADesc);
169 //------------------------------------------------------------------------------------------
171 ASDCP::ATMOS::MXFReader::MXFReader()
173 m_Reader = new h__Reader(DefaultSMPTEDict());
177 ASDCP::ATMOS::MXFReader::~MXFReader()
179 if ( m_Reader && m_Reader->m_File.IsOpen() )
183 // Warning: direct manipulation of MXF structures can interfere
184 // with the normal operation of the wrapper. Caveat emptor!
186 ASDCP::MXF::OP1aHeader&
187 ASDCP::ATMOS::MXFReader::OP1aHeader()
189 if ( m_Reader.empty() )
191 assert(g_OP1aHeader);
192 return *g_OP1aHeader;
195 return m_Reader->m_HeaderPart;
198 // Warning: direct manipulation of MXF structures can interfere
199 // with the normal operation of the wrapper. Caveat emptor!
201 ASDCP::MXF::OPAtomIndexFooter&
202 ASDCP::ATMOS::MXFReader::OPAtomIndexFooter()
204 if ( m_Reader.empty() )
206 assert(g_OPAtomIndexFooter);
207 return *g_OPAtomIndexFooter;
210 return m_Reader->m_IndexAccess;
213 // Warning: direct manipulation of MXF structures can interfere
214 // with the normal operation of the wrapper. Caveat emptor!
217 ASDCP::ATMOS::MXFReader::RIP()
219 if ( m_Reader.empty() )
225 return m_Reader->m_RIP;
228 // Open the file for reading. The file must exist. Returns error if the
229 // operation cannot be completed.
231 ASDCP::ATMOS::MXFReader::OpenRead(const char* filename) const
233 return m_Reader->OpenRead(filename);
238 ASDCP::ATMOS::MXFReader::ReadFrame(ui32_t FrameNum, DCData::FrameBuffer& FrameBuf,
239 AESDecContext* Ctx, HMACContext* HMAC) const
241 if ( m_Reader && m_Reader->m_File.IsOpen() )
242 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
248 ASDCP::ATMOS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
250 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
254 // Fill the struct with the values from the file's header.
255 // Returns RESULT_INIT if the file is not open.
257 ASDCP::ATMOS::MXFReader::FillAtmosDescriptor(AtmosDescriptor& ADesc) const
259 if ( m_Reader && m_Reader->m_File.IsOpen() )
261 ADesc = m_Reader->m_ADesc;
269 // Fill the struct with the values from the file's header.
270 // Returns RESULT_INIT if the file is not open.
272 ASDCP::ATMOS::MXFReader::FillWriterInfo(WriterInfo& Info) const
274 if ( m_Reader && m_Reader->m_File.IsOpen() )
276 Info = m_Reader->m_Info;
285 ASDCP::ATMOS::MXFReader::DumpHeaderMetadata(FILE* stream) const
287 if ( m_Reader->m_File.IsOpen() )
288 m_Reader->m_HeaderPart.Dump(stream);
293 ASDCP::ATMOS::MXFReader::DumpIndex(FILE* stream) const
295 if ( m_Reader->m_File.IsOpen() )
296 m_Reader->m_IndexAccess.Dump(stream);
301 ASDCP::ATMOS::MXFReader::Close() const
303 if ( m_Reader && m_Reader->m_File.IsOpen() )
313 //------------------------------------------------------------------------------------------
316 class ASDCP::ATMOS::MXFWriter::h__Writer : public DCData::h__Writer
318 MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
320 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
324 AtmosDescriptor m_ADesc;
326 h__Writer(const Dictionary& d) : DCData::h__Writer(d),
327 m_EssenceSubDescriptor(NULL), m_ADesc() {}
329 virtual ~h__Writer(){}
331 Result_t OpenWrite(const char*, ui32_t HeaderSize, const AtmosDescriptor& ADesc);
332 Result_t Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc);
337 ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc)
339 ASDCP_TEST_NULL(m_EssenceDescriptor);
340 ASDCP_TEST_NULL(m_EssenceSubDescriptor);
341 MXF::DolbyAtmosSubDescriptor * ADescObj = m_EssenceSubDescriptor;
342 ADescObj->MaxChannelCount = ADesc.MaxChannelCount;
343 ADescObj->MaxObjectCount = ADesc.MaxObjectCount;
344 ADescObj->AtmosID.Set(ADesc.AtmosID);
345 ADescObj->AtmosVersion = ADesc.AtmosVersion;
346 ADescObj->FirstFrame = ADesc.FirstFrame;
352 ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc)
355 m_EssenceSubDescriptor = new DolbyAtmosSubDescriptor(m_Dict);
356 DCData::SubDescriptorList_t subDescriptors;
357 subDescriptors.push_back(m_EssenceSubDescriptor);
358 Result_t result = DCData::h__Writer::OpenWrite(filename, HeaderSize, subDescriptors);
359 if ( ASDCP_FAILURE(result) )
360 delete m_EssenceSubDescriptor;
362 if ( ASDCP_SUCCESS(result) )
365 memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH);
366 result = Atmos_ADesc_to_MD(m_ADesc);
375 //------------------------------------------------------------------------------------------
377 ASDCP::ATMOS::MXFWriter::MXFWriter()
381 ASDCP::ATMOS::MXFWriter::~MXFWriter()
385 // Warning: direct manipulation of MXF structures can interfere
386 // with the normal operation of the wrapper. Caveat emptor!
388 ASDCP::MXF::OP1aHeader&
389 ASDCP::ATMOS::MXFWriter::OP1aHeader()
391 if ( m_Writer.empty() )
393 assert(g_OP1aHeader);
394 return *g_OP1aHeader;
397 return m_Writer->m_HeaderPart;
400 // Warning: direct manipulation of MXF structures can interfere
401 // with the normal operation of the wrapper. Caveat emptor!
403 ASDCP::MXF::OPAtomIndexFooter&
404 ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter()
406 if ( m_Writer.empty() )
408 assert(g_OPAtomIndexFooter);
409 return *g_OPAtomIndexFooter;
412 return m_Writer->m_FooterPart;
415 // Warning: direct manipulation of MXF structures can interfere
416 // with the normal operation of the wrapper. Caveat emptor!
419 ASDCP::ATMOS::MXFWriter::RIP()
421 if ( m_Writer.empty() )
427 return m_Writer->m_RIP;
430 // Open the file for writing. The file must not exist. Returns error if
431 // the operation cannot be completed.
433 ASDCP::ATMOS::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
434 const AtmosDescriptor& ADesc, ui32_t HeaderSize)
436 if ( Info.LabelSetType != LS_MXF_SMPTE )
438 DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n");
439 return RESULT_FORMAT;
442 m_Writer = new h__Writer(DefaultSMPTEDict());
443 m_Writer->m_Info = Info;
445 Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc);
447 if ( ASDCP_SUCCESS(result) )
448 result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL,
451 if ( ASDCP_FAILURE(result) )
457 // Writes a frame of essence to the MXF file. If the optional AESEncContext
458 // argument is present, the essence is encrypted prior to writing.
459 // Fails if the file is not open, is finalized, or an operating system
462 ASDCP::ATMOS::MXFWriter::WriteFrame(const DCData::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
464 if ( m_Writer.empty() )
467 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
470 // Closes the MXF file, writing the index and other closing information.
472 ASDCP::ATMOS::MXFWriter::Finalize()
474 if ( m_Writer.empty() )
477 return m_Writer->Finalize();
482 // end AS_DCP_ATMOS.cpp