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