o Added preliminary support for timed-text wrapping for AS-02. This
[asdcplib.git] / src / AS_02_PCM.cpp
1 /*
2 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10 1. Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16    derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /*! \file    AS_02_PCM.cpp
29   \version $Id$       
30   \brief   AS-02 library, PCM essence reader and writer implementation
31 */
32
33 #include "AS_02_internal.h"
34
35 #include <map>
36 #include <iostream>
37 #include <iomanip>
38
39 //------------------------------------------------------------------------------------------
40
41 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
42 static std::string SOUND_DEF_LABEL = "Sound Track";
43
44
45 //------------------------------------------------------------------------------------------
46
47
48 class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
49 {
50   ui64_t m_ClipEssenceBegin;
51   ui64_t m_SamplesPerFrame;
52   ui32_t m_ContainerDuration;
53
54   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
55   h__Reader();
56
57 public:
58   h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0),
59                                    m_SamplesPerFrame(0), m_ContainerDuration(0) {}
60   virtual ~h__Reader() {}
61
62   ASDCP::Result_t    OpenRead(const std::string&, const ASDCP::Rational& edit_rate);
63   ASDCP::Result_t    ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
64 };
65
66 // TODO: This will ignore any body partitions past the first
67 //
68 //
69 ASDCP::Result_t
70 AS_02::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
71 {
72   ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
73   IndexTableSegment::IndexEntry tmp_entry;
74   Result_t result = OpenMXFRead(filename.c_str());
75
76   if( KM_SUCCESS(result) )
77     {
78       if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor),
79                                                      reinterpret_cast<InterchangeObject**>(&wave_descriptor))) )
80         {
81           if ( wave_descriptor == 0 )
82             {
83               DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
84               return RESULT_AS02_FORMAT;
85             }
86         }
87     }
88
89   if ( KM_SUCCESS(result) )
90     result = m_IndexAccess.Lookup(0, tmp_entry);
91
92   if ( KM_SUCCESS(result) )
93     result = m_File.Seek(tmp_entry.StreamOffset);
94
95   if ( KM_SUCCESS(result) )
96     {
97       assert(wave_descriptor);
98       KLReader reader;
99       result = reader.ReadKLFromFile(m_File);
100
101       if ( KM_SUCCESS(result) )
102         {
103           if ( ! UL(reader.Key()).MatchIgnoreStream(m_Dict->ul(MDD_WAVEssenceClip)) )
104             {
105               const MDDEntry *entry = m_Dict->FindUL(reader.Key());
106
107               if ( entry == 0 )
108                 {
109                   char buf[64];
110                   DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", UL(reader.Key()).EncodeString(buf, 64));
111                 }
112               else
113                 {
114                   DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", entry->name);
115                 }
116               
117               return RESULT_AS02_FORMAT;
118             }
119
120           if ( wave_descriptor->BlockAlign == 0 )
121             {
122               DefaultLogSink().Error("EssenceDescriptor has corrupt BlockAlign value, unable to continue.\n");
123               return RESULT_AS02_FORMAT;
124             }
125
126           if ( reader.Length() % wave_descriptor->BlockAlign != 0 )
127             {
128               DefaultLogSink().Error("Clip length is not an even multiple of BlockAlign, unable to continue.\n");
129               return RESULT_AS02_FORMAT;
130             }
131
132           m_ClipEssenceBegin = m_File.Tell();
133           m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*wave_descriptor, edit_rate);
134           m_ContainerDuration = static_cast<ui32_t>(8ULL * reader.Length() /
135                                                     (m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->QuantizationBits));
136         }
137     }
138
139   return result;
140 }
141
142 //
143 ASDCP::Result_t
144 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
145                                             ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
146 {
147   ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
148
149   if ( ! m_File.IsOpen() )
150     {
151       return RESULT_INIT;
152     }
153
154   if ( FrameNum > m_ContainerDuration )
155     {
156       return RESULT_RANGE;
157     }
158
159   assert(m_ClipEssenceBegin);
160   Result_t result = RESULT_OK;
161
162   if( KM_SUCCESS(result) )
163     {
164       if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor),
165                                                      reinterpret_cast<InterchangeObject**>(&wave_descriptor))) )
166         {
167           if ( wave_descriptor == 0 )
168             {
169               DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
170               return RESULT_AS02_FORMAT;
171             }
172         }
173     }
174
175   ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign );
176
177   if ( m_File.Tell() != position )
178     {
179       result = m_File.Seek(position);
180     }
181
182   if ( KM_SUCCESS(result) )
183     {
184       result = m_File.Read(FrameBuf.Data(), m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign);
185     }
186
187   if ( KM_SUCCESS(result) )
188     {
189       FrameBuf.Size(m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign);
190     }
191
192   return result;
193 }
194
195
196 //------------------------------------------------------------------------------------------
197 //
198
199
200 AS_02::PCM::MXFReader::MXFReader()
201 {
202   m_Reader = new h__Reader(DefaultCompositeDict());
203 }
204
205 AS_02::PCM::MXFReader::~MXFReader()
206 {
207 }
208
209 // Warning: direct manipulation of MXF structures can interfere
210 // with the normal operation of the wrapper.  Caveat emptor!
211 //
212 ASDCP::MXF::OP1aHeader&
213 AS_02::PCM::MXFReader::OP1aHeader()
214 {
215   if ( m_Reader.empty() )
216     {
217       assert(g_OP1aHeader);
218       return *g_OP1aHeader;
219     }
220
221   return m_Reader->m_HeaderPart;
222 }
223
224 // Warning: direct manipulation of MXF structures can interfere
225 // with the normal operation of the wrapper.  Caveat emptor!
226 //
227 AS_02::MXF::AS02IndexReader&
228 AS_02::PCM::MXFReader::AS02IndexReader()
229 {
230   if ( m_Reader.empty() )
231     {
232       assert(g_AS02IndexReader);
233       return *g_AS02IndexReader;
234     }
235
236   return m_Reader->m_IndexAccess;
237 }
238
239 // Warning: direct manipulation of MXF structures can interfere
240 // with the normal operation of the wrapper.  Caveat emptor!
241 //
242 ASDCP::MXF::RIP&
243 AS_02::PCM::MXFReader::RIP()
244 {
245   if ( m_Reader.empty() )
246     {
247       assert(g_RIP);
248       return *g_RIP;
249     }
250
251   return m_Reader->m_RIP;
252 }
253
254 // Open the file for reading. The file must exist. Returns error if the
255 // operation cannot be completed.
256 ASDCP::Result_t
257 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
258 {
259   return m_Reader->OpenRead(filename, edit_rate);
260 }
261
262 //
263 Result_t
264 AS_02::PCM::MXFReader::Close() const
265 {
266   if ( m_Reader && m_Reader->m_File.IsOpen() )
267     {
268       m_Reader->Close();
269       return RESULT_OK;
270     }
271
272   return RESULT_INIT;
273 }
274
275 // Reads a frame of essence from the MXF file. If the optional AESEncContext
276 // argument is present, the essence is decrypted after reading. If the MXF
277 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
278 // will contain the ciphertext frame data.
279 ASDCP::Result_t
280 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
281                                  ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
282 {
283   if ( m_Reader && m_Reader->m_File.IsOpen() )
284     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
285
286   return RESULT_INIT;
287 }
288
289
290 // Fill the struct with the values from the file's header.
291 // Returns RESULT_INIT if the file is not open.
292 ASDCP::Result_t
293 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
294 {
295   if ( m_Reader && m_Reader->m_File.IsOpen() )
296     {
297       Info = m_Reader->m_Info;
298       return RESULT_OK;
299     }
300
301   return RESULT_INIT;
302 }
303
304 //
305 void
306 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
307 {
308   if ( m_Reader && m_Reader->m_File.IsOpen() )
309     m_Reader->m_HeaderPart.Dump(stream);
310 }
311
312
313 //
314 void
315 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
316 {
317   if ( m_Reader->m_File.IsOpen() )
318     m_Reader->m_IndexAccess.Dump(stream);
319 }
320
321
322 //------------------------------------------------------------------------------------------
323
324 //
325 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02Writer
326 {
327   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
328   h__Writer();
329
330 public:
331   ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
332   byte_t m_EssenceUL[SMPTE_UL_LENGTH];
333   ui32_t m_BytesPerFrame;
334   ui32_t m_SamplesPerFrame;
335
336   h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
337   {
338     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
339   }
340
341   virtual ~h__Writer(){}
342
343   Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
344                      ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
345   Result_t SetSourceStream(const ASDCP::Rational&);
346   Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
347   Result_t Finalize();
348 };
349
350 // Open the file for writing. The file must not exist. Returns error if
351 // the operation cannot be completed.
352 ASDCP::Result_t
353 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
354                                             ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
355 {
356   assert(essence_descriptor);
357
358   if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_WaveAudioDescriptor)) )
359     {
360       DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
361       essence_descriptor->Dump();
362       return RESULT_AS02_FORMAT;
363     }
364
365   m_WaveAudioDescriptor = reinterpret_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
366
367   if ( ! m_State.Test_BEGIN() )
368     {
369       return RESULT_STATE;
370     }
371
372   Result_t result = m_File.OpenWrite(filename.c_str());
373
374   if ( KM_SUCCESS(result) )
375     {
376       m_HeaderSize = header_size;
377       m_EssenceDescriptor = essence_descriptor;
378       m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
379
380       ASDCP::MXF::InterchangeObject_list_t::iterator i;
381       for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
382         {
383           if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
384                && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
385                && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
386             {
387               DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
388               (*i)->Dump();
389             }
390
391           m_EssenceSubDescriptorList.push_back(*i);
392           GenRandomValue((*i)->InstanceUID);
393           m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
394           *i = 0; // parent will only free the ones we don't keep
395         }
396
397       result = m_State.Goto_INIT();
398     }
399
400   return result;
401 }
402
403
404 // Automatically sets the MXF file's metadata from the WAV parser info.
405 ASDCP::Result_t
406 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
407 {
408   if ( ! m_State.Test_INIT() )
409     {
410       return RESULT_STATE;
411     }
412
413   memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
414   m_EssenceUL[15] = 1; // set the stream identifier
415   Result_t result = m_State.Goto_READY();
416
417   if ( KM_SUCCESS(result) )
418     {
419       assert(m_WaveAudioDescriptor);
420       m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
421       m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
422       m_WaveAudioDescriptor->ContainerDuration = 0;
423
424       result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)),
425                                SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
426                                m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate), m_BytesPerFrame);
427     }
428
429   return result;
430 }
431
432
433 //
434 //
435 ASDCP::Result_t
436 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
437                                              HMACContext* HMAC)
438 {
439   if ( frame_buf.Size() == 0 )
440     {
441       DefaultLogSink().Error("The frame buffer size is zero.\n");
442       return RESULT_PARAM;
443     }
444
445   if ( frame_buf.Size() % m_BytesPerFrame != 0 )
446     {
447       DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
448       return RESULT_AS02_FORMAT;
449     }
450
451   Result_t result = RESULT_OK;
452
453   if ( m_State.Test_READY() )
454     {
455       result = m_State.Goto_RUNNING(); // first time through
456     }
457
458   if ( KM_SUCCESS(result) && ! HasOpenClip() )
459     {
460       result = StartClip(m_EssenceUL, Ctx, HMAC);
461     }
462
463   if ( KM_SUCCESS(result) )
464     {
465       result = WriteClipBlock(frame_buf);
466     }
467
468   if ( KM_SUCCESS(result) )
469     {
470       m_FramesWritten++;
471     }
472
473   return result;
474 }
475
476 // Closes the MXF file, writing the index and other closing information.
477 //
478 ASDCP::Result_t
479 AS_02::PCM::MXFWriter::h__Writer::Finalize()
480 {
481   if ( ! m_State.Test_RUNNING() )
482     return RESULT_STATE;
483
484   m_State.Goto_FINAL();
485
486   Result_t result = FinalizeClip(m_BytesPerFrame);
487
488   if ( KM_SUCCESS(result) )
489     {
490       m_FramesWritten = m_FramesWritten * m_SamplesPerFrame;
491       m_IndexWriter.ThisPartition = m_File.Tell();
492       m_IndexWriter.WriteToFile(m_File);
493       m_RIP.PairArray.push_back(RIP::Pair(0, m_IndexWriter.ThisPartition));
494       WriteAS02Footer();
495     }
496
497   return result;
498 }
499
500
501 //------------------------------------------------------------------------------------------
502 //
503
504
505
506 AS_02::PCM::MXFWriter::MXFWriter()
507 {
508 }
509
510 AS_02::PCM::MXFWriter::~MXFWriter()
511 {
512 }
513
514 // Warning: direct manipulation of MXF structures can interfere
515 // with the normal operation of the wrapper.  Caveat emptor!
516 //
517 ASDCP::MXF::OP1aHeader&
518 AS_02::PCM::MXFWriter::OP1aHeader()
519 {
520   if ( m_Writer.empty() )
521     {
522       assert(g_OP1aHeader);
523       return *g_OP1aHeader;
524     }
525
526   return m_Writer->m_HeaderPart;
527 }
528
529 // Warning: direct manipulation of MXF structures can interfere
530 // with the normal operation of the wrapper.  Caveat emptor!
531 //
532 ASDCP::MXF::RIP&
533 AS_02::PCM::MXFWriter::RIP()
534 {
535   if ( m_Writer.empty() )
536     {
537       assert(g_RIP);
538       return *g_RIP;
539     }
540
541   return m_Writer->m_RIP;
542 }
543
544
545 // Open the file for writing. The file must not exist. Returns error if
546 // the operation cannot be completed.
547 ASDCP::Result_t
548 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
549                                  ASDCP::MXF::FileDescriptor* essence_descriptor,
550                                  ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
551                                  const ASDCP::Rational& edit_rate, ui32_t header_size)
552 {
553   if ( essence_descriptor == 0 )
554     {
555       DefaultLogSink().Error("Essence descriptor object required.\n");
556       return RESULT_PARAM;
557     }
558
559   if ( Info.EncryptedEssence )
560     {
561       DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
562       return Kumu::RESULT_NOTIMPL;
563     }
564
565   m_Writer = new h__Writer(DefaultSMPTEDict());
566   m_Writer->m_Info = Info;
567
568   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
569
570   if ( KM_SUCCESS(result) )
571     result = m_Writer->SetSourceStream(edit_rate);
572
573   if ( ASDCP_FAILURE(result) )
574     m_Writer.release();
575
576   return result;
577 }
578
579 // Writes a frame of essence to the MXF file. If the optional AESEncContext
580 // argument is present, the essence is encrypted prior to writing.
581 // Fails if the file is not open, is finalized, or an operating system
582 // error occurs.
583 ASDCP::Result_t
584 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
585 {
586   if ( m_Writer.empty() )
587     return RESULT_INIT;
588
589   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
590 }
591
592 // Closes the MXF file, writing the index and other closing information.
593 ASDCP::Result_t
594 AS_02::PCM::MXFWriter::Finalize()
595 {
596   if ( m_Writer.empty() )
597     return RESULT_INIT;
598
599   return m_Writer->Finalize();
600 }
601
602
603
604 //
605 // end AS_02_PCM.cpp
606 //
607