512bcdb0b9cd365972ca892624ab356084aa98ef
[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   ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
148
149   if ( ! m_File.IsOpen() )
150     {
151       return RESULT_INIT;
152     }
153
154   if ( FrameNum > m_ContainerDuration )
155     {
156       return RESULT_RANGE;
157     }
158
159   assert(m_ClipEssenceBegin);
160   Result_t result = RESULT_OK;
161
162   if( KM_SUCCESS(result) )
163     {
164       if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor),
165                                                      reinterpret_cast<InterchangeObject**>(&wave_descriptor))) )
166         {
167           if ( wave_descriptor == 0 )
168             {
169               DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
170               return RESULT_AS02_FORMAT;
171             }
172         }
173     }
174
175   ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign );
176
177   if ( m_File.Tell() != position )
178     {
179       result = m_File.Seek(position);
180     }
181
182   if ( KM_SUCCESS(result) )
183     {
184       result = m_File.Read(FrameBuf.Data(), m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign);
185     }
186
187   if ( KM_SUCCESS(result) )
188     {
189       FrameBuf.Size(m_SamplesPerFrame * wave_descriptor->ChannelCount * wave_descriptor->BlockAlign);
190     }
191
192   return result;
193 }
194
195
196 //------------------------------------------------------------------------------------------
197 //
198
199
200 AS_02::PCM::MXFReader::MXFReader()
201 {
202   m_Reader = new h__Reader(DefaultCompositeDict());
203 }
204
205 AS_02::PCM::MXFReader::~MXFReader()
206 {
207 }
208
209 // Warning: direct manipulation of MXF structures can interfere
210 // with the normal operation of the wrapper.  Caveat emptor!
211 //
212 ASDCP::MXF::OP1aHeader&
213 AS_02::PCM::MXFReader::OP1aHeader()
214 {
215   if ( m_Reader.empty() )
216     {
217       assert(g_OP1aHeader);
218       return *g_OP1aHeader;
219     }
220
221   return m_Reader->m_HeaderPart;
222 }
223
224 // Warning: direct manipulation of MXF structures can interfere
225 // with the normal operation of the wrapper.  Caveat emptor!
226 //
227 AS_02::MXF::AS02IndexReader&
228 AS_02::PCM::MXFReader::AS02IndexReader()
229 {
230   if ( m_Reader.empty() )
231     {
232       assert(g_AS02IndexReader);
233       return *g_AS02IndexReader;
234     }
235
236   return m_Reader->m_IndexAccess;
237 }
238
239 // Warning: direct manipulation of MXF structures can interfere
240 // with the normal operation of the wrapper.  Caveat emptor!
241 //
242 ASDCP::MXF::RIP&
243 AS_02::PCM::MXFReader::RIP()
244 {
245   if ( m_Reader.empty() )
246     {
247       assert(g_RIP);
248       return *g_RIP;
249     }
250
251   return m_Reader->m_RIP;
252 }
253
254 // Open the file for reading. The file must exist. Returns error if the
255 // operation cannot be completed.
256 ASDCP::Result_t
257 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
258 {
259   return m_Reader->OpenRead(filename, edit_rate);
260 }
261
262 //
263 Result_t
264 AS_02::PCM::MXFReader::Close() const
265 {
266   if ( m_Reader && m_Reader->m_File.IsOpen() )
267     {
268       m_Reader->Close();
269       return RESULT_OK;
270     }
271
272   return RESULT_INIT;
273 }
274
275 // Reads a frame of essence from the MXF file. If the optional AESEncContext
276 // argument is present, the essence is decrypted after reading. If the MXF
277 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
278 // will contain the ciphertext frame data.
279 ASDCP::Result_t
280 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
281                                  ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
282 {
283   if ( m_Reader && m_Reader->m_File.IsOpen() )
284     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
285
286   return RESULT_INIT;
287 }
288
289
290 // Fill the struct with the values from the file's header.
291 // Returns RESULT_INIT if the file is not open.
292 ASDCP::Result_t
293 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
294 {
295   if ( m_Reader && m_Reader->m_File.IsOpen() )
296     {
297       Info = m_Reader->m_Info;
298       return RESULT_OK;
299     }
300
301   return RESULT_INIT;
302 }
303
304 //
305 void
306 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
307 {
308   if ( m_Reader && m_Reader->m_File.IsOpen() )
309     m_Reader->m_HeaderPart.Dump(stream);
310 }
311
312
313 //
314 void
315 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
316 {
317   if ( m_Reader->m_File.IsOpen() )
318     m_Reader->m_IndexAccess.Dump(stream);
319 }
320
321
322 //------------------------------------------------------------------------------------------
323
324 //
325 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02Writer
326 {
327   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
328   h__Writer();
329
330 public:
331   ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
332   byte_t m_EssenceUL[SMPTE_UL_LENGTH];
333   ui32_t m_BytesPerFrame;
334   ui32_t m_SamplesPerFrame;
335
336   h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(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   if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_WaveAudioDescriptor)) )
359     {
360       DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
361       essence_descriptor->Dump();
362       return RESULT_AS02_FORMAT;
363     }
364
365   m_WaveAudioDescriptor = reinterpret_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
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_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
421       m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
422       m_WaveAudioDescriptor->ContainerDuration = 0;
423
424       result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
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), m_BytesPerFrame);
427     }
428
429   return result;
430 }
431
432
433 //
434 //
435 ASDCP::Result_t
436 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
437                                              HMACContext* HMAC)
438 {
439   if ( frame_buf.Size() == 0 )
440     {
441       DefaultLogSink().Error("The frame buffer size is zero.\n");
442       return RESULT_PARAM;
443     }
444
445   if ( frame_buf.Size() % m_BytesPerFrame != 0 )
446     {
447       DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
448       return RESULT_AS02_FORMAT;
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++;
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(m_BytesPerFrame);
487
488   if ( KM_SUCCESS(result) )
489     {
490       m_FramesWritten = m_FramesWritten * m_SamplesPerFrame;
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
542 // Open the file for writing. The file must not exist. Returns error if
543 // the operation cannot be completed.
544 ASDCP::Result_t
545 AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
546                                  ASDCP::MXF::FileDescriptor* essence_descriptor,
547                                  ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
548                                  const ASDCP::Rational& edit_rate, ui32_t header_size)
549 {
550   if ( essence_descriptor == 0 )
551     {
552       DefaultLogSink().Error("Essence descriptor object required.\n");
553       return RESULT_PARAM;
554     }
555
556   if ( Info.EncryptedEssence )
557     {
558       DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
559       return Kumu::RESULT_NOTIMPL;
560     }
561
562   m_Writer = new h__Writer(DefaultSMPTEDict());
563   m_Writer->m_Info = Info;
564
565   Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
566
567   if ( KM_SUCCESS(result) )
568     result = m_Writer->SetSourceStream(edit_rate);
569
570   if ( ASDCP_FAILURE(result) )
571     m_Writer.release();
572
573   return result;
574 }
575
576 // Writes a frame of essence to the MXF file. If the optional AESEncContext
577 // argument is present, the essence is encrypted prior to writing.
578 // Fails if the file is not open, is finalized, or an operating system
579 // error occurs.
580 ASDCP::Result_t
581 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
582 {
583   if ( m_Writer.empty() )
584     return RESULT_INIT;
585
586   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
587 }
588
589 // Closes the MXF file, writing the index and other closing information.
590 ASDCP::Result_t
591 AS_02::PCM::MXFWriter::Finalize()
592 {
593   if ( m_Writer.empty() )
594     return RESULT_INIT;
595
596   return m_Writer->Finalize();
597 }
598
599
600
601 //
602 // end AS_02_PCM.cpp
603 //
604