Add call to parent constructor.
[asdcplib-cth.git] / src / AS_02_PCM.cpp
1 /*
2 Copyright (c) 2011-2015, 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 */
29
30 /*! \file    AS_02_PCM.cpp
31   \version $Id: AS_02_PCM.cpp,v 1.16 2015/10/07 16:41:23 jhurst Exp $
32   \brief   AS-02 library, PCM essence reader and writer implementation
33 */
34
35 #include "AS_02_internal.h"
36
37 #include <map>
38 #include <iostream>
39 #include <iomanip>
40
41 //------------------------------------------------------------------------------------------
42
43 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
44 static std::string SOUND_DEF_LABEL = "Sound Track";
45
46
47 //------------------------------------------------------------------------------------------
48
49
50 class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
51 {
52   ui64_t m_ClipEssenceBegin, m_ClipSize;
53   ui32_t m_ClipDurationFrames, m_BytesPerFrame;
54
55   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
56   h__Reader();
57
58 public:
59   h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0), m_ClipSize(0),
60                                    m_ClipDurationFrames(0) {}
61   virtual ~h__Reader() {}
62
63   ASDCP::Result_t    OpenRead(const std::string&, const ASDCP::Rational& edit_rate);
64   ASDCP::Result_t    ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
65 };
66
67 // TODO: This will ignore any body partitions past the first
68 //
69 //
70 ASDCP::Result_t
71 AS_02::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
72 {
73   ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
74   IndexTableSegment::IndexEntry tmp_entry;
75   Result_t result = OpenMXFRead(filename.c_str());
76
77   if( KM_SUCCESS(result) )
78     {
79       InterchangeObject* tmp_obj = 0;
80
81       if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &tmp_obj)) )
82         {
83           wave_descriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(tmp_obj);
84         }
85     }
86
87   if ( wave_descriptor == 0 )
88     {
89       DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
90       result = RESULT_AS02_FORMAT;
91     }
92
93   if ( KM_SUCCESS(result) )
94     result = m_IndexAccess.Lookup(0, tmp_entry);
95
96   if ( KM_SUCCESS(result) )
97     result = m_File.Seek(tmp_entry.StreamOffset);
98
99   if ( KM_SUCCESS(result) )
100     {
101       assert(wave_descriptor);
102       KLReader reader;
103       result = reader.ReadKLFromFile(m_File);
104
105       if ( KM_SUCCESS(result) )
106         {
107           if ( ! UL(reader.Key()).MatchIgnoreStream(m_Dict->ul(MDD_WAVEssenceClip)) )
108             {
109               const MDDEntry *entry = m_Dict->FindUL(reader.Key());
110
111               if ( entry == 0 )
112                 {
113                   char buf[64];
114                   DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", UL(reader.Key()).EncodeString(buf, 64));
115                 }
116               else
117                 {
118                   DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", entry->name);
119                 }
120
121               return RESULT_AS02_FORMAT;
122             }
123
124           if ( wave_descriptor->BlockAlign == 0 )
125             {
126               DefaultLogSink().Error("EssenceDescriptor has corrupt BlockAlign value, unable to continue.\n");
127               return RESULT_AS02_FORMAT;
128             }
129
130           if ( reader.Length() % wave_descriptor->BlockAlign != 0 )
131             {
132               DefaultLogSink().Error("Clip length is not an even multiple of BlockAlign, unable to continue.\n");
133               return RESULT_AS02_FORMAT;
134             }
135
136           m_ClipEssenceBegin = m_File.Tell();
137           m_ClipSize = reader.Length();
138           m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, edit_rate);
139           m_ClipDurationFrames = m_ClipSize / m_BytesPerFrame;
140
141           if ( m_ClipSize % m_BytesPerFrame > 0 )
142             {
143               ++m_ClipDurationFrames; // there is a partial frame at the end
144             }
145         }
146     }
147
148   return result;
149 }
150
151 //
152 ASDCP::Result_t
153 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
154                                             ASDCP::AESDecContext*, ASDCP::HMACContext*)
155 {
156   if ( ! m_File.IsOpen() )
157     {
158       return RESULT_INIT;
159     }
160
161   if ( ! ( FrameNum < m_ClipDurationFrames ) )
162     {
163       return RESULT_RANGE;
164     }
165
166   assert(m_ClipEssenceBegin);
167   ui64_t offset = FrameNum * m_BytesPerFrame;
168   ui64_t position = m_ClipEssenceBegin + offset;
169   Result_t result = RESULT_OK;
170
171   if ( m_File.Tell() != position )
172     {
173       result = m_File.Seek(position);
174     }
175
176   if ( KM_SUCCESS(result) )
177     {
178       ui64_t remainder = m_ClipSize - offset;
179       ui32_t read_size = ( remainder < m_BytesPerFrame ) ? remainder : m_BytesPerFrame;
180       result = m_File.Read(FrameBuf.Data(), read_size);
181
182       if ( KM_SUCCESS(result) )
183         {
184           FrameBuf.Size(read_size);
185
186           if ( read_size < FrameBuf.Capacity() )
187             {
188               memset(FrameBuf.Data() + FrameBuf.Size(), 0, FrameBuf.Capacity() - FrameBuf.Size());
189             }
190         }
191     }
192
193   return result;
194 }
195
196
197 //------------------------------------------------------------------------------------------
198 //
199
200
201 AS_02::PCM::MXFReader::MXFReader()
202 {
203   m_Reader = new h__Reader(DefaultCompositeDict());
204 }
205
206 AS_02::PCM::MXFReader::~MXFReader()
207 {
208 }
209
210 // Warning: direct manipulation of MXF structures can interfere
211 // with the normal operation of the wrapper.  Caveat emptor!
212 //
213 ASDCP::MXF::OP1aHeader&
214 AS_02::PCM::MXFReader::OP1aHeader()
215 {
216   if ( m_Reader.empty() )
217     {
218       assert(g_OP1aHeader);
219       return *g_OP1aHeader;
220     }
221
222   return m_Reader->m_HeaderPart;
223 }
224
225 // Warning: direct manipulation of MXF structures can interfere
226 // with the normal operation of the wrapper.  Caveat emptor!
227 //
228 AS_02::MXF::AS02IndexReader&
229 AS_02::PCM::MXFReader::AS02IndexReader()
230 {
231   if ( m_Reader.empty() )
232     {
233       assert(g_AS02IndexReader);
234       return *g_AS02IndexReader;
235     }
236
237   return m_Reader->m_IndexAccess;
238 }
239
240 // Warning: direct manipulation of MXF structures can interfere
241 // with the normal operation of the wrapper.  Caveat emptor!
242 //
243 ASDCP::MXF::RIP&
244 AS_02::PCM::MXFReader::RIP()
245 {
246   if ( m_Reader.empty() )
247     {
248       assert(g_RIP);
249       return *g_RIP;
250     }
251
252   return m_Reader->m_RIP;
253 }
254
255 // Open the file for reading. The file must exist. Returns error if the
256 // operation cannot be completed.
257 ASDCP::Result_t
258 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
259 {
260   return m_Reader->OpenRead(filename, edit_rate);
261 }
262
263 //
264 Result_t
265 AS_02::PCM::MXFReader::Close() const
266 {
267   if ( m_Reader && m_Reader->m_File.IsOpen() )
268     {
269       m_Reader->Close();
270       return RESULT_OK;
271     }
272
273   return RESULT_INIT;
274 }
275
276 // Reads a frame of essence from the MXF file. If the optional AESEncContext
277 // argument is present, the essence is decrypted after reading. If the MXF
278 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
279 // will contain the ciphertext frame data.
280 ASDCP::Result_t
281 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
282                                  ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
283 {
284   if ( m_Reader && m_Reader->m_File.IsOpen() )
285     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
286
287   return RESULT_INIT;
288 }
289
290
291 // Fill the struct with the values from the file's header.
292 // Returns RESULT_INIT if the file is not open.
293 ASDCP::Result_t
294 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
295 {
296   if ( m_Reader && m_Reader->m_File.IsOpen() )
297     {
298       Info = m_Reader->m_Info;
299       return RESULT_OK;
300     }
301
302   return RESULT_INIT;
303 }
304
305 //
306 void
307 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
308 {
309   if ( m_Reader && m_Reader->m_File.IsOpen() )
310     m_Reader->m_HeaderPart.Dump(stream);
311 }
312
313
314 //
315 void
316 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
317 {
318   if ( m_Reader->m_File.IsOpen() )
319     m_Reader->m_IndexAccess.Dump(stream);
320 }
321
322
323 //------------------------------------------------------------------------------------------
324
325 //
326 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
327 {
328   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
329   h__Writer();
330
331 public:
332   ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
333   byte_t m_EssenceUL[SMPTE_UL_LENGTH];
334   ui32_t m_BytesPerSample;
335
336   h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerSample(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   m_WaveAudioDescriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
359
360   if ( m_WaveAudioDescriptor == 0 )
361     {
362       DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
363       essence_descriptor->Dump();
364       return RESULT_AS02_FORMAT;
365     }
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_BytesPerSample = AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor);
421       result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)),
422                                SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
423                                m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate));
424
425       if ( KM_SUCCESS(result) )
426         {
427           this->m_IndexWriter.SetEditRate(m_WaveAudioDescriptor->AudioSamplingRate,
428                                           AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
429         }
430     }
431
432   return result;
433 }
434
435
436 //
437 //
438 ASDCP::Result_t
439 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
440                                              HMACContext* HMAC)
441 {
442   if ( frame_buf.Size() == 0 )
443     {
444       DefaultLogSink().Error("The frame buffer size is zero.\n");
445       return RESULT_PARAM;
446     }
447
448   Result_t result = RESULT_OK;
449
450   if ( m_State.Test_READY() )
451     {
452       result = m_State.Goto_RUNNING(); // first time through
453     }
454
455   if ( KM_SUCCESS(result) && ! HasOpenClip() )
456     {
457       result = StartClip(m_EssenceUL, Ctx, HMAC);
458     }
459
460   if ( KM_SUCCESS(result) )
461     {
462       result = WriteClipBlock(frame_buf);
463     }
464
465   if ( KM_SUCCESS(result) )
466     {
467       m_FramesWritten += frame_buf.Size() / m_BytesPerSample;
468     }
469
470   return result;
471 }
472
473 // Closes the MXF file, writing the index and other closing information.
474 //
475 ASDCP::Result_t
476 AS_02::PCM::MXFWriter::h__Writer::Finalize()
477 {
478   if ( ! m_State.Test_RUNNING() )
479     return RESULT_STATE;
480
481   m_State.Goto_FINAL();
482
483   Result_t result = FinalizeClip(AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
484
485   if ( KM_SUCCESS(result) )
486     {
487       m_WaveAudioDescriptor->ContainerDuration = m_IndexWriter.m_Duration = m_FramesWritten;
488       WriteAS02Footer();
489     }
490
491   return result;
492 }
493
494
495 //------------------------------------------------------------------------------------------
496 //
497
498
499
500 AS_02::PCM::MXFWriter::MXFWriter()
501 {
502 }
503
504 AS_02::PCM::MXFWriter::~MXFWriter()
505 {
506 }
507
508 // Warning: direct manipulation of MXF structures can interfere
509 // with the normal operation of the wrapper.  Caveat emptor!
510 //
511 ASDCP::MXF::OP1aHeader&
512 AS_02::PCM::MXFWriter::OP1aHeader()
513 {
514   if ( m_Writer.empty() )
515     {
516       assert(g_OP1aHeader);
517       return *g_OP1aHeader;
518     }
519
520   return m_Writer->m_HeaderPart;
521 }
522
523 // Warning: direct manipulation of MXF structures can interfere
524 // with the normal operation of the wrapper.  Caveat emptor!
525 //
526 ASDCP::MXF::RIP&
527 AS_02::PCM::MXFWriter::RIP()
528 {
529   if ( m_Writer.empty() )
530     {
531       assert(g_RIP);
532       return *g_RIP;
533     }
534
535   return m_Writer->m_RIP;
536 }
537
538 // Open the file for writing. The file must not exist. Returns error if
539 // the operation cannot be completed.
540 ASDCP::Result_t
541 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
542                                  ASDCP::MXF::FileDescriptor* essence_descriptor,
543                                  ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
544                                  const ASDCP::Rational& edit_rate, ui32_t header_size)
545 {
546   if ( essence_descriptor == 0 )
547     {
548       DefaultLogSink().Error("Essence descriptor object required.\n");
549       return RESULT_PARAM;
550     }
551
552   if ( Info.EncryptedEssence )
553     {
554       DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
555       return Kumu::RESULT_NOTIMPL;
556     }
557
558   m_Writer = new h__Writer(DefaultSMPTEDict());
559   m_Writer->m_Info = Info;
560
561   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
562
563   if ( KM_SUCCESS(result) )
564     result = m_Writer->SetSourceStream(edit_rate);
565
566   if ( ASDCP_FAILURE(result) )
567     m_Writer.release();
568
569   return result;
570 }
571
572 // Writes a frame of essence to the MXF file. If the optional AESEncContext
573 // argument is present, the essence is encrypted prior to writing.
574 // Fails if the file is not open, is finalized, or an operating system
575 // error occurs.
576 ASDCP::Result_t
577 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
578 {
579   if ( m_Writer.empty() )
580     return RESULT_INIT;
581
582   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
583 }
584
585 // Closes the MXF file, writing the index and other closing information.
586 ASDCP::Result_t
587 AS_02::PCM::MXFWriter::Finalize()
588 {
589   if ( m_Writer.empty() )
590     return RESULT_INIT;
591
592   return m_Writer->Finalize();
593 }
594
595
596
597 //
598 // end AS_02_PCM.cpp
599 //