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