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