63b55577f4bda85c67d73905e541ffbfdd7efb3d
[asdcplib.git] / src / AS_DCP_ATMOS.cpp
1 /*
2 Copyright (c) 2004-2013, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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.
15
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.
26 */
27 /*! \file    AS_DCP_ATMOS.cpp
28     \version $Id$
29     \brief   AS-DCP library, Dolby Atmos essence reader and writer implementation
30 */
31
32
33 #include <iostream>
34
35 #include "AS_DCP.h"
36 #include "AS_DCP_DCData_internal.h"
37 #include "AS_DCP_internal.h"
38
39 namespace ASDCP
40 {
41 namespace ATMOS
42 {
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 };
47 } // namespace ATMOS
48 } // namespace ASDCP
49
50 //
51 std::ostream&
52 ASDCP::ATMOS::operator << (std::ostream& strm, const AtmosDescriptor& ADesc)
53 {
54   char str_buf[40];
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;
63   return strm;
64 }
65
66 //
67 void
68 ASDCP::ATMOS::AtmosDescriptorDump(const AtmosDescriptor& ADesc, FILE* stream)
69 {
70   char str_buf[40];
71   char atmosID_buf[40];
72   if ( stream == 0 )
73     stream = stderr;
74
75   fprintf(stream, "\
76           EditRate: %d/%d\n\
77    ContainerDuration: %u\n\
78    DataEssenceCoding: %s\n\
79         AtmosVersion: %u\n\
80      MaxChannelCount: %u\n\
81       MaxObjectCount: %u\n\
82              AtmosID: %s\n\
83            FirsFrame: %u\n",
84           ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
85           ADesc.ContainerDuration,
86           UL(ADesc.DataEssenceCoding).EncodeString(str_buf, 40),
87           ADesc.AtmosVersion,
88           ADesc.MaxChannelCount,
89           ADesc.MaxObjectCount,
90           UUID(ADesc.AtmosID).EncodeString(atmosID_buf, 40),
91           ADesc.FirstFrame);
92 }
93
94 //
95 bool
96 ASDCP::ATMOS::IsDolbyAtmos(const char* filename)
97 {
98     // TODO
99     // For now use an atmos extension
100     bool result = (0 == (std::string("atmos").compare(Kumu::PathGetExtension(std::string(filename)))));
101     return result;
102 }
103
104
105 //------------------------------------------------------------------------------------------
106
107
108 class ASDCP::ATMOS::MXFReader::h__Reader : public ASDCP::DCData::h__Reader
109 {
110   MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
111
112   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
113   h__Reader();
114
115  public:
116   AtmosDescriptor m_ADesc;
117
118   h__Reader(const Dictionary& d) : DCData::h__Reader(d),  m_EssenceSubDescriptor(NULL),
119                                    m_ADesc() {}
120   virtual ~h__Reader() {}
121   Result_t    OpenRead(const char*);
122   Result_t    MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc);
123 };
124
125 ASDCP::Result_t
126 ASDCP::ATMOS::MXFReader::h__Reader::MD_to_Atmos_ADesc(ATMOS::AtmosDescriptor& ADesc)
127 {
128   ASDCP_TEST_NULL(m_EssenceSubDescriptor);
129   Result_t result = MD_to_DCData_DDesc(ADesc);
130   if( ASDCP_SUCCESS(result) )
131   {
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;
138   }
139   return result;
140 }
141
142 //
143 //
144 ASDCP::Result_t
145 ASDCP::ATMOS::MXFReader::h__Reader::OpenRead(const char* filename)
146 {
147   Result_t result = DCData::h__Reader::OpenRead(filename);
148
149   if( ASDCP_SUCCESS(result) )
150   {
151
152     if (NULL == m_EssenceSubDescriptor)
153     {
154       InterchangeObject* iObj = NULL;
155       result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(DolbyAtmosSubDescriptor), &iObj);
156       m_EssenceSubDescriptor = static_cast<MXF::DolbyAtmosSubDescriptor*>(iObj);
157     }
158
159     if ( ASDCP_SUCCESS(result) )
160     {
161       result = MD_to_Atmos_ADesc(m_ADesc);
162     }
163   }
164
165   return result;
166 }
167
168
169 //------------------------------------------------------------------------------------------
170
171 ASDCP::ATMOS::MXFReader::MXFReader()
172 {
173   m_Reader = new h__Reader(DefaultSMPTEDict());
174 }
175
176
177 ASDCP::ATMOS::MXFReader::~MXFReader()
178 {
179   if ( m_Reader && m_Reader->m_File.IsOpen() )
180     m_Reader->Close();
181 }
182
183 // Warning: direct manipulation of MXF structures can interfere
184 // with the normal operation of the wrapper.  Caveat emptor!
185 //
186 ASDCP::MXF::OP1aHeader&
187 ASDCP::ATMOS::MXFReader::OP1aHeader()
188 {
189   if ( m_Reader.empty() )
190   {
191     assert(g_OP1aHeader);
192     return *g_OP1aHeader;
193   }
194
195   return m_Reader->m_HeaderPart;
196 }
197
198 // Warning: direct manipulation of MXF structures can interfere
199 // with the normal operation of the wrapper.  Caveat emptor!
200 //
201 ASDCP::MXF::OPAtomIndexFooter&
202 ASDCP::ATMOS::MXFReader::OPAtomIndexFooter()
203 {
204   if ( m_Reader.empty() )
205   {
206     assert(g_OPAtomIndexFooter);
207     return *g_OPAtomIndexFooter;
208   }
209
210   return m_Reader->m_IndexAccess;
211 }
212
213 // Warning: direct manipulation of MXF structures can interfere
214 // with the normal operation of the wrapper.  Caveat emptor!
215 //
216 ASDCP::MXF::RIP&
217 ASDCP::ATMOS::MXFReader::RIP()
218 {
219   if ( m_Reader.empty() )
220     {
221       assert(g_RIP);
222       return *g_RIP;
223     }
224
225   return m_Reader->m_RIP;
226 }
227
228 // Open the file for reading. The file must exist. Returns error if the
229 // operation cannot be completed.
230 ASDCP::Result_t
231 ASDCP::ATMOS::MXFReader::OpenRead(const char* filename) const
232 {
233   return m_Reader->OpenRead(filename);
234 }
235
236 //
237 ASDCP::Result_t
238 ASDCP::ATMOS::MXFReader::ReadFrame(ui32_t FrameNum, DCData::FrameBuffer& FrameBuf,
239                                     AESDecContext* Ctx, HMACContext* HMAC) const
240 {
241   if ( m_Reader && m_Reader->m_File.IsOpen() )
242     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
243
244   return RESULT_INIT;
245 }
246
247 ASDCP::Result_t
248 ASDCP::ATMOS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
249 {
250     return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
251 }
252
253
254 // Fill the struct with the values from the file's header.
255 // Returns RESULT_INIT if the file is not open.
256 ASDCP::Result_t
257 ASDCP::ATMOS::MXFReader::FillAtmosDescriptor(AtmosDescriptor& ADesc) const
258 {
259   if ( m_Reader && m_Reader->m_File.IsOpen() )
260   {
261     ADesc = m_Reader->m_ADesc;
262     return RESULT_OK;
263   }
264
265   return RESULT_INIT;
266 }
267
268
269 // Fill the struct with the values from the file's header.
270 // Returns RESULT_INIT if the file is not open.
271 ASDCP::Result_t
272 ASDCP::ATMOS::MXFReader::FillWriterInfo(WriterInfo& Info) const
273 {
274   if ( m_Reader && m_Reader->m_File.IsOpen() )
275   {
276     Info = m_Reader->m_Info;
277     return RESULT_OK;
278   }
279
280   return RESULT_INIT;
281 }
282
283 //
284 void
285 ASDCP::ATMOS::MXFReader::DumpHeaderMetadata(FILE* stream) const
286 {
287   if ( m_Reader->m_File.IsOpen() )
288     m_Reader->m_HeaderPart.Dump(stream);
289 }
290
291 //
292 void
293 ASDCP::ATMOS::MXFReader::DumpIndex(FILE* stream) const
294 {
295   if ( m_Reader->m_File.IsOpen() )
296     m_Reader->m_IndexAccess.Dump(stream);
297 }
298
299 //
300 ASDCP::Result_t
301 ASDCP::ATMOS::MXFReader::Close() const
302 {
303   if ( m_Reader && m_Reader->m_File.IsOpen() )
304     {
305       m_Reader->Close();
306       return RESULT_OK;
307     }
308
309   return RESULT_INIT;
310 }
311
312
313 //------------------------------------------------------------------------------------------
314
315 //
316 class ASDCP::ATMOS::MXFWriter::h__Writer : public DCData::h__Writer
317 {
318   MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
319
320   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
321   h__Writer();
322
323  public:
324   AtmosDescriptor m_ADesc;
325
326   h__Writer(const Dictionary& d) : DCData::h__Writer(d),
327       m_EssenceSubDescriptor(NULL), m_ADesc() {}
328
329   virtual ~h__Writer(){}
330
331   Result_t OpenWrite(const char*, ui32_t HeaderSize, const AtmosDescriptor& ADesc);
332   Result_t Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc);
333 };
334
335 //
336 ASDCP::Result_t
337 ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc)
338 {
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;
347   return RESULT_OK;
348 }
349
350 //
351 ASDCP::Result_t
352 ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc)
353 {
354
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;
361
362   if ( ASDCP_SUCCESS(result) )
363   {
364       m_ADesc = ADesc;
365       memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH);
366       result = Atmos_ADesc_to_MD(m_ADesc);
367   }
368
369   return result;
370 }
371
372
373
374
375 //------------------------------------------------------------------------------------------
376
377 ASDCP::ATMOS::MXFWriter::MXFWriter()
378 {
379 }
380
381 ASDCP::ATMOS::MXFWriter::~MXFWriter()
382 {
383 }
384
385 // Warning: direct manipulation of MXF structures can interfere
386 // with the normal operation of the wrapper.  Caveat emptor!
387 //
388 ASDCP::MXF::OP1aHeader&
389 ASDCP::ATMOS::MXFWriter::OP1aHeader()
390 {
391   if ( m_Writer.empty() )
392   {
393     assert(g_OP1aHeader);
394     return *g_OP1aHeader;
395     }
396
397   return m_Writer->m_HeaderPart;
398 }
399
400 // Warning: direct manipulation of MXF structures can interfere
401 // with the normal operation of the wrapper.  Caveat emptor!
402 //
403 ASDCP::MXF::OPAtomIndexFooter&
404 ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter()
405 {
406   if ( m_Writer.empty() )
407   {
408     assert(g_OPAtomIndexFooter);
409     return *g_OPAtomIndexFooter;
410   }
411
412   return m_Writer->m_FooterPart;
413 }
414
415 // Warning: direct manipulation of MXF structures can interfere
416 // with the normal operation of the wrapper.  Caveat emptor!
417 //
418 ASDCP::MXF::RIP&
419 ASDCP::ATMOS::MXFWriter::RIP()
420 {
421   if ( m_Writer.empty() )
422     {
423       assert(g_RIP);
424       return *g_RIP;
425     }
426
427   return m_Writer->m_RIP;
428 }
429
430 // Open the file for writing. The file must not exist. Returns error if
431 // the operation cannot be completed.
432 ASDCP::Result_t
433 ASDCP::ATMOS::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
434                                        const AtmosDescriptor& ADesc, ui32_t HeaderSize)
435 {
436   if ( Info.LabelSetType != LS_MXF_SMPTE )
437   {
438     DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n");
439     return RESULT_FORMAT;
440   }
441
442   m_Writer = new h__Writer(DefaultSMPTEDict());
443   m_Writer->m_Info = Info;
444
445   Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc);
446
447   if ( ASDCP_SUCCESS(result) )
448       result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL,
449                                          ATMOS_DEF_LABEL);
450
451   if ( ASDCP_FAILURE(result) )
452     m_Writer.release();
453
454   return result;
455 }
456
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
460 // error occurs.
461 ASDCP::Result_t
462 ASDCP::ATMOS::MXFWriter::WriteFrame(const DCData::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
463 {
464   if ( m_Writer.empty() )
465     return RESULT_INIT;
466
467   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
468 }
469
470 // Closes the MXF file, writing the index and other closing information.
471 ASDCP::Result_t
472 ASDCP::ATMOS::MXFWriter::Finalize()
473 {
474   if ( m_Writer.empty() )
475     return RESULT_INIT;
476
477   return m_Writer->Finalize();
478 }
479
480
481 //
482 // end AS_DCP_ATMOS.cpp
483 //
484
485