Fix a type-punning warning.
[asdcplib-cth.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: AS_DCP_ATMOS.cpp,v 1.4 2014/01/02 23:29:22 jhurst Exp $
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 std::string& filename)
97 {
98     // TODO
99     // For now use an atmos extension
100     bool result = ( 0 == (std::string("atmos").compare(Kumu::PathGetExtension(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 std::string&);
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 std::string& 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           if ( iObj == 0 )
159             {
160               DefaultLogSink().Error("DolbyAtmosSubDescriptor object not found.\n");
161               return RESULT_FORMAT;
162             }
163         }
164
165       if ( ASDCP_SUCCESS(result) )
166         {
167           result = MD_to_Atmos_ADesc(m_ADesc);
168         }
169     }
170
171   return result;
172 }
173
174
175 //------------------------------------------------------------------------------------------
176
177 ASDCP::ATMOS::MXFReader::MXFReader()
178 {
179   m_Reader = new h__Reader(DefaultSMPTEDict());
180 }
181
182
183 ASDCP::ATMOS::MXFReader::~MXFReader()
184 {
185   if ( m_Reader && m_Reader->m_File.IsOpen() )
186     m_Reader->Close();
187 }
188
189 // Warning: direct manipulation of MXF structures can interfere
190 // with the normal operation of the wrapper.  Caveat emptor!
191 //
192 ASDCP::MXF::OP1aHeader&
193 ASDCP::ATMOS::MXFReader::OP1aHeader()
194 {
195   if ( m_Reader.empty() )
196   {
197     assert(g_OP1aHeader);
198     return *g_OP1aHeader;
199   }
200
201   return m_Reader->m_HeaderPart;
202 }
203
204 // Warning: direct manipulation of MXF structures can interfere
205 // with the normal operation of the wrapper.  Caveat emptor!
206 //
207 ASDCP::MXF::OPAtomIndexFooter&
208 ASDCP::ATMOS::MXFReader::OPAtomIndexFooter()
209 {
210   if ( m_Reader.empty() )
211   {
212     assert(g_OPAtomIndexFooter);
213     return *g_OPAtomIndexFooter;
214   }
215
216   return m_Reader->m_IndexAccess;
217 }
218
219 // Warning: direct manipulation of MXF structures can interfere
220 // with the normal operation of the wrapper.  Caveat emptor!
221 //
222 ASDCP::MXF::RIP&
223 ASDCP::ATMOS::MXFReader::RIP()
224 {
225   if ( m_Reader.empty() )
226     {
227       assert(g_RIP);
228       return *g_RIP;
229     }
230
231   return m_Reader->m_RIP;
232 }
233
234 // Open the file for reading. The file must exist. Returns error if the
235 // operation cannot be completed.
236 ASDCP::Result_t
237 ASDCP::ATMOS::MXFReader::OpenRead(const std::string& filename) const
238 {
239   return m_Reader->OpenRead(filename);
240 }
241
242 //
243 ASDCP::Result_t
244 ASDCP::ATMOS::MXFReader::ReadFrame(ui32_t FrameNum, DCData::FrameBuffer& FrameBuf,
245                                     AESDecContext* Ctx, HMACContext* HMAC) const
246 {
247   if ( m_Reader && m_Reader->m_File.IsOpen() )
248     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
249
250   return RESULT_INIT;
251 }
252
253 ASDCP::Result_t
254 ASDCP::ATMOS::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
255 {
256     return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
257 }
258
259
260 // Fill the struct with the values from the file's header.
261 // Returns RESULT_INIT if the file is not open.
262 ASDCP::Result_t
263 ASDCP::ATMOS::MXFReader::FillAtmosDescriptor(AtmosDescriptor& ADesc) const
264 {
265   if ( m_Reader && m_Reader->m_File.IsOpen() )
266   {
267     ADesc = m_Reader->m_ADesc;
268     return RESULT_OK;
269   }
270
271   return RESULT_INIT;
272 }
273
274
275 // Fill the struct with the values from the file's header.
276 // Returns RESULT_INIT if the file is not open.
277 ASDCP::Result_t
278 ASDCP::ATMOS::MXFReader::FillWriterInfo(WriterInfo& Info) const
279 {
280   if ( m_Reader && m_Reader->m_File.IsOpen() )
281   {
282     Info = m_Reader->m_Info;
283     return RESULT_OK;
284   }
285
286   return RESULT_INIT;
287 }
288
289 //
290 void
291 ASDCP::ATMOS::MXFReader::DumpHeaderMetadata(FILE* stream) const
292 {
293   if ( m_Reader->m_File.IsOpen() )
294     m_Reader->m_HeaderPart.Dump(stream);
295 }
296
297 //
298 void
299 ASDCP::ATMOS::MXFReader::DumpIndex(FILE* stream) const
300 {
301   if ( m_Reader->m_File.IsOpen() )
302     m_Reader->m_IndexAccess.Dump(stream);
303 }
304
305 //
306 ASDCP::Result_t
307 ASDCP::ATMOS::MXFReader::Close() const
308 {
309   if ( m_Reader && m_Reader->m_File.IsOpen() )
310     {
311       m_Reader->Close();
312       return RESULT_OK;
313     }
314
315   return RESULT_INIT;
316 }
317
318
319 //------------------------------------------------------------------------------------------
320
321 //
322 class ASDCP::ATMOS::MXFWriter::h__Writer : public DCData::h__Writer
323 {
324   MXF::DolbyAtmosSubDescriptor* m_EssenceSubDescriptor;
325
326   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
327   h__Writer();
328
329  public:
330   AtmosDescriptor m_ADesc;
331
332   h__Writer(const Dictionary& d) : DCData::h__Writer(d),
333       m_EssenceSubDescriptor(NULL), m_ADesc() {}
334
335   virtual ~h__Writer(){}
336
337   Result_t OpenWrite(const std::string&, ui32_t HeaderSize, const AtmosDescriptor& ADesc);
338   Result_t Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc);
339 };
340
341 //
342 ASDCP::Result_t
343 ASDCP::ATMOS::MXFWriter::h__Writer::Atmos_ADesc_to_MD(const AtmosDescriptor& ADesc)
344 {
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;
353   return RESULT_OK;
354 }
355
356 //
357 ASDCP::Result_t
358 ASDCP::ATMOS::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize, const AtmosDescriptor& ADesc)
359 {
360
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;
367
368   if ( ASDCP_SUCCESS(result) )
369   {
370       m_ADesc = ADesc;
371       memcpy(m_ADesc.DataEssenceCoding, ATMOS_ESSENCE_CODING, SMPTE_UL_LENGTH);
372       result = Atmos_ADesc_to_MD(m_ADesc);
373   }
374
375   return result;
376 }
377
378
379
380
381 //------------------------------------------------------------------------------------------
382
383 ASDCP::ATMOS::MXFWriter::MXFWriter()
384 {
385 }
386
387 ASDCP::ATMOS::MXFWriter::~MXFWriter()
388 {
389 }
390
391 // Warning: direct manipulation of MXF structures can interfere
392 // with the normal operation of the wrapper.  Caveat emptor!
393 //
394 ASDCP::MXF::OP1aHeader&
395 ASDCP::ATMOS::MXFWriter::OP1aHeader()
396 {
397   if ( m_Writer.empty() )
398   {
399     assert(g_OP1aHeader);
400     return *g_OP1aHeader;
401     }
402
403   return m_Writer->m_HeaderPart;
404 }
405
406 // Warning: direct manipulation of MXF structures can interfere
407 // with the normal operation of the wrapper.  Caveat emptor!
408 //
409 ASDCP::MXF::OPAtomIndexFooter&
410 ASDCP::ATMOS::MXFWriter::OPAtomIndexFooter()
411 {
412   if ( m_Writer.empty() )
413   {
414     assert(g_OPAtomIndexFooter);
415     return *g_OPAtomIndexFooter;
416   }
417
418   return m_Writer->m_FooterPart;
419 }
420
421 // Warning: direct manipulation of MXF structures can interfere
422 // with the normal operation of the wrapper.  Caveat emptor!
423 //
424 ASDCP::MXF::RIP&
425 ASDCP::ATMOS::MXFWriter::RIP()
426 {
427   if ( m_Writer.empty() )
428     {
429       assert(g_RIP);
430       return *g_RIP;
431     }
432
433   return m_Writer->m_RIP;
434 }
435
436 // Open the file for writing. The file must not exist. Returns error if
437 // the operation cannot be completed.
438 ASDCP::Result_t
439 ASDCP::ATMOS::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
440                                        const AtmosDescriptor& ADesc, ui32_t HeaderSize)
441 {
442   if ( Info.LabelSetType != LS_MXF_SMPTE )
443   {
444     DefaultLogSink().Error("Atmos support requires LS_MXF_SMPTE\n");
445     return RESULT_FORMAT;
446   }
447
448   m_Writer = new h__Writer(DefaultSMPTEDict());
449   m_Writer->m_Info = Info;
450
451   Result_t result = m_Writer->OpenWrite(filename, HeaderSize, ADesc);
452
453   if ( ASDCP_SUCCESS(result) )
454       result = m_Writer->SetSourceStream(ADesc, ATMOS_ESSENCE_CODING, ATMOS_PACKAGE_LABEL,
455                                          ATMOS_DEF_LABEL);
456
457   if ( ASDCP_FAILURE(result) )
458     m_Writer.release();
459
460   return result;
461 }
462
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
466 // error occurs.
467 ASDCP::Result_t
468 ASDCP::ATMOS::MXFWriter::WriteFrame(const DCData::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
469 {
470   if ( m_Writer.empty() )
471     return RESULT_INIT;
472
473   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
474 }
475
476 // Closes the MXF file, writing the index and other closing information.
477 ASDCP::Result_t
478 ASDCP::ATMOS::MXFWriter::Finalize()
479 {
480   if ( m_Writer.empty() )
481     return RESULT_INIT;
482
483   return m_Writer->Finalize();
484 }
485
486
487 //
488 // end AS_DCP_ATMOS.cpp
489 //
490
491