Added library names
[asdcplib.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$       
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->FindULAnyVersion(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* Ctx, ASDCP::HMACContext* HMAC)
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) const
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     {
311       m_Reader->m_HeaderPart.Dump(stream);
312     }
313 }
314
315 //
316 void
317 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
318 {
319   if ( m_Reader && m_Reader->m_File.IsOpen() )
320     {
321       m_Reader->m_IndexAccess.Dump(stream);
322     }
323 }
324
325
326 //------------------------------------------------------------------------------------------
327
328 //
329 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
330 {
331   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
332   h__Writer();
333
334 public:
335   ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
336   byte_t m_EssenceUL[SMPTE_UL_LENGTH];
337   ui32_t m_BytesPerSample;
338   
339   h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerSample(0)
340   {
341     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
342   }
343
344   virtual ~h__Writer(){}
345
346   Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
347                      ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
348   Result_t SetSourceStream(const ASDCP::Rational&);
349   Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
350   Result_t Finalize();
351 };
352
353 // Open the file for writing. The file must not exist. Returns error if
354 // the operation cannot be completed.
355 ASDCP::Result_t
356 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
357                                             ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
358 {
359   assert(essence_descriptor);
360
361   m_WaveAudioDescriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
362
363   if ( m_WaveAudioDescriptor == 0 )
364     {
365       DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
366       essence_descriptor->Dump();
367       return RESULT_AS02_FORMAT;
368     }
369
370   if ( ! m_State.Test_BEGIN() )
371     {
372       return RESULT_STATE;
373     }
374
375   Result_t result = m_File.OpenWrite(filename.c_str());
376
377   if ( KM_SUCCESS(result) )
378     {
379       m_HeaderSize = header_size;
380       m_EssenceDescriptor = essence_descriptor;
381       m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
382
383       ASDCP::MXF::InterchangeObject_list_t::iterator i;
384       for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
385         {
386           if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
387                && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
388                && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
389             {
390               DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
391               (*i)->Dump();
392             }
393
394           m_EssenceSubDescriptorList.push_back(*i);
395           GenRandomValue((*i)->InstanceUID);
396           m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
397           *i = 0; // parent will only free the ones we don't keep
398         }
399
400       result = m_State.Goto_INIT();
401     }
402
403   return result;
404 }
405
406
407 // Automatically sets the MXF file's metadata from the WAV parser info.
408 ASDCP::Result_t
409 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
410 {
411   if ( ! m_State.Test_INIT() )
412     {
413       return RESULT_STATE;
414     }
415
416   memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
417   m_EssenceUL[15] = 1; // set the stream identifier
418   Result_t result = m_State.Goto_READY();
419
420   if ( KM_SUCCESS(result) )
421     {
422       assert(m_WaveAudioDescriptor);
423       m_BytesPerSample = AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor);
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));
427
428       if ( KM_SUCCESS(result) )
429         {
430           this->m_IndexWriter.SetEditRate(m_WaveAudioDescriptor->AudioSamplingRate,
431                                           AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
432         }
433     }
434
435   return result;
436 }
437
438
439 //
440 //
441 ASDCP::Result_t
442 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
443                                              HMACContext* HMAC)
444 {
445   if ( frame_buf.Size() == 0 )
446     {
447       DefaultLogSink().Error("The frame buffer size is zero.\n");
448       return RESULT_PARAM;
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 += frame_buf.Size() / m_BytesPerSample;
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(AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
487
488   if ( KM_SUCCESS(result) )
489     {
490       m_WaveAudioDescriptor->ContainerDuration = m_IndexWriter.m_Duration = m_FramesWritten;
491       WriteAS02Footer();
492     }
493
494   return result;
495 }
496
497
498 //------------------------------------------------------------------------------------------
499 //
500
501
502
503 AS_02::PCM::MXFWriter::MXFWriter()
504 {
505 }
506
507 AS_02::PCM::MXFWriter::~MXFWriter()
508 {
509 }
510
511 // Warning: direct manipulation of MXF structures can interfere
512 // with the normal operation of the wrapper.  Caveat emptor!
513 //
514 ASDCP::MXF::OP1aHeader&
515 AS_02::PCM::MXFWriter::OP1aHeader()
516 {
517   if ( m_Writer.empty() )
518     {
519       assert(g_OP1aHeader);
520       return *g_OP1aHeader;
521     }
522
523   return m_Writer->m_HeaderPart;
524 }
525
526 // Warning: direct manipulation of MXF structures can interfere
527 // with the normal operation of the wrapper.  Caveat emptor!
528 //
529 ASDCP::MXF::RIP&
530 AS_02::PCM::MXFWriter::RIP()
531 {
532   if ( m_Writer.empty() )
533     {
534       assert(g_RIP);
535       return *g_RIP;
536     }
537
538   return m_Writer->m_RIP;
539 }
540
541 // Open the file for writing. The file must not exist. Returns error if
542 // the operation cannot be completed.
543 ASDCP::Result_t
544 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
545                                  ASDCP::MXF::FileDescriptor* essence_descriptor,
546                                  ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
547                                  const ASDCP::Rational& edit_rate, ui32_t header_size)
548 {
549   if ( essence_descriptor == 0 )
550     {
551       DefaultLogSink().Error("Essence descriptor object required.\n");
552       return RESULT_PARAM;
553     }
554
555   if ( Info.EncryptedEssence )
556     {
557       DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
558       return Kumu::RESULT_NOTIMPL;
559     }
560
561   m_Writer = new h__Writer(DefaultSMPTEDict());
562   m_Writer->m_Info = Info;
563
564   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
565
566   if ( KM_SUCCESS(result) )
567     result = m_Writer->SetSourceStream(edit_rate);
568
569   if ( ASDCP_FAILURE(result) )
570     m_Writer.release();
571
572   return result;
573 }
574
575 // Writes a frame of essence to the MXF file. If the optional AESEncContext
576 // argument is present, the essence is encrypted prior to writing.
577 // Fails if the file is not open, is finalized, or an operating system
578 // error occurs.
579 ASDCP::Result_t
580 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
581 {
582   if ( m_Writer.empty() )
583     return RESULT_INIT;
584
585   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
586 }
587
588 // Closes the MXF file, writing the index and other closing information.
589 ASDCP::Result_t
590 AS_02::PCM::MXFWriter::Finalize()
591 {
592   if ( m_Writer.empty() )
593     return RESULT_INIT;
594
595   return m_Writer->Finalize();
596 }
597
598
599
600 //
601 // end AS_02_PCM.cpp
602 //
603