phdr
[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_BytesPerFrame;
53   ui32_t m_ContainerDuration;
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),
60                                    m_SamplesPerFrame(0), m_BytesPerFrame(0), m_ContainerDuration(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->FindUL(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_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*wave_descriptor, edit_rate);
138           m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, edit_rate);
139           m_ContainerDuration = static_cast<ui32_t>(reader.Length() / ( m_SamplesPerFrame * AS_02::MXF::CalcSampleSize(*wave_descriptor)));
140
141         }
142     }
143
144   return result;
145 }
146
147 //
148 ASDCP::Result_t
149 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
150                                             ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
151 {
152   if ( ! m_File.IsOpen() )
153     {
154       return RESULT_INIT;
155     }
156
157   if ( FrameNum > m_ContainerDuration )
158     {
159       return RESULT_RANGE;
160     }
161
162   assert(m_ClipEssenceBegin);
163   ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_BytesPerFrame );
164   Result_t result = RESULT_OK;
165
166   if ( m_File.Tell() != position )
167     {
168       result = m_File.Seek(position);
169     }
170
171   if ( KM_SUCCESS(result) )
172     {
173       result = m_File.Read(FrameBuf.Data(), m_BytesPerFrame);
174     }
175
176   if ( KM_SUCCESS(result) )
177     {
178       FrameBuf.Size(m_BytesPerFrame);
179     }
180
181   return result;
182 }
183
184
185 //------------------------------------------------------------------------------------------
186 //
187
188
189 AS_02::PCM::MXFReader::MXFReader()
190 {
191   m_Reader = new h__Reader(DefaultCompositeDict());
192 }
193
194 AS_02::PCM::MXFReader::~MXFReader()
195 {
196 }
197
198 // Warning: direct manipulation of MXF structures can interfere
199 // with the normal operation of the wrapper.  Caveat emptor!
200 //
201 ASDCP::MXF::OP1aHeader&
202 AS_02::PCM::MXFReader::OP1aHeader()
203 {
204   if ( m_Reader.empty() )
205     {
206       assert(g_OP1aHeader);
207       return *g_OP1aHeader;
208     }
209
210   return m_Reader->m_HeaderPart;
211 }
212
213 // Warning: direct manipulation of MXF structures can interfere
214 // with the normal operation of the wrapper.  Caveat emptor!
215 //
216 AS_02::MXF::AS02IndexReader&
217 AS_02::PCM::MXFReader::AS02IndexReader()
218 {
219   if ( m_Reader.empty() )
220     {
221       assert(g_AS02IndexReader);
222       return *g_AS02IndexReader;
223     }
224
225   return m_Reader->m_IndexAccess;
226 }
227
228 // Warning: direct manipulation of MXF structures can interfere
229 // with the normal operation of the wrapper.  Caveat emptor!
230 //
231 ASDCP::MXF::RIP&
232 AS_02::PCM::MXFReader::RIP()
233 {
234   if ( m_Reader.empty() )
235     {
236       assert(g_RIP);
237       return *g_RIP;
238     }
239
240   return m_Reader->m_RIP;
241 }
242
243 // Open the file for reading. The file must exist. Returns error if the
244 // operation cannot be completed.
245 ASDCP::Result_t
246 AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
247 {
248   return m_Reader->OpenRead(filename, edit_rate);
249 }
250
251 //
252 Result_t
253 AS_02::PCM::MXFReader::Close() const
254 {
255   if ( m_Reader && m_Reader->m_File.IsOpen() )
256     {
257       m_Reader->Close();
258       return RESULT_OK;
259     }
260
261   return RESULT_INIT;
262 }
263
264 // Reads a frame of essence from the MXF file. If the optional AESEncContext
265 // argument is present, the essence is decrypted after reading. If the MXF
266 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
267 // will contain the ciphertext frame data.
268 ASDCP::Result_t
269 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
270                                  ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
271 {
272   if ( m_Reader && m_Reader->m_File.IsOpen() )
273     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
274
275   return RESULT_INIT;
276 }
277
278
279 // Fill the struct with the values from the file's header.
280 // Returns RESULT_INIT if the file is not open.
281 ASDCP::Result_t
282 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
283 {
284   if ( m_Reader && m_Reader->m_File.IsOpen() )
285     {
286       Info = m_Reader->m_Info;
287       return RESULT_OK;
288     }
289
290   return RESULT_INIT;
291 }
292
293 //
294 void
295 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
296 {
297   if ( m_Reader && m_Reader->m_File.IsOpen() )
298     m_Reader->m_HeaderPart.Dump(stream);
299 }
300
301
302 //
303 void
304 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
305 {
306   if ( m_Reader->m_File.IsOpen() )
307     m_Reader->m_IndexAccess.Dump(stream);
308 }
309
310
311 //------------------------------------------------------------------------------------------
312
313 //
314 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
315 {
316   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
317   h__Writer();
318
319 public:
320   ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
321   byte_t m_EssenceUL[SMPTE_UL_LENGTH];
322   ui32_t m_BytesPerFrame;
323   ui32_t m_SamplesPerFrame;
324
325   h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
326   {
327     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
328   }
329
330   virtual ~h__Writer(){}
331
332   Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
333                      ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
334   Result_t SetSourceStream(const ASDCP::Rational&);
335   Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
336   Result_t Finalize();
337 };
338
339 // Open the file for writing. The file must not exist. Returns error if
340 // the operation cannot be completed.
341 ASDCP::Result_t
342 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
343                                             ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
344 {
345   assert(essence_descriptor);
346
347   m_WaveAudioDescriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
348
349   if ( m_WaveAudioDescriptor == 0 )
350     {
351       DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
352       essence_descriptor->Dump();
353       return RESULT_AS02_FORMAT;
354     }
355
356   if ( ! m_State.Test_BEGIN() )
357     {
358       return RESULT_STATE;
359     }
360
361   Result_t result = m_File.OpenWrite(filename.c_str());
362
363   if ( KM_SUCCESS(result) )
364     {
365       m_HeaderSize = header_size;
366       m_EssenceDescriptor = essence_descriptor;
367       m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
368
369       ASDCP::MXF::InterchangeObject_list_t::iterator i;
370       for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
371         {
372           if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
373                && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
374                && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
375             {
376               DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
377               (*i)->Dump();
378             }
379
380           m_EssenceSubDescriptorList.push_back(*i);
381           GenRandomValue((*i)->InstanceUID);
382           m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
383           *i = 0; // parent will only free the ones we don't keep
384         }
385
386       result = m_State.Goto_INIT();
387     }
388
389   return result;
390 }
391
392
393 // Automatically sets the MXF file's metadata from the WAV parser info.
394 ASDCP::Result_t
395 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
396 {
397   if ( ! m_State.Test_INIT() )
398     {
399       return RESULT_STATE;
400     }
401
402   memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
403   m_EssenceUL[15] = 1; // set the stream identifier
404   Result_t result = m_State.Goto_READY();
405
406   if ( KM_SUCCESS(result) )
407     {
408       assert(m_WaveAudioDescriptor);
409       m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
410       m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
411       m_WaveAudioDescriptor->ContainerDuration = 0;
412       assert(m_BytesPerFrame);
413       result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrappingClip)),
414                                SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
415                                m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate));
416
417       if ( KM_SUCCESS(result) )
418         {
419           this->m_IndexWriter.SetEditRate(m_WaveAudioDescriptor->AudioSamplingRate,
420                                           AS_02::MXF::CalcSampleSize(*m_WaveAudioDescriptor));
421         }
422     }
423
424   return result;
425 }
426
427
428 //
429 //
430 ASDCP::Result_t
431 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
432                                              HMACContext* HMAC)
433 {
434   if ( frame_buf.Size() == 0 )
435     {
436       DefaultLogSink().Error("The frame buffer size is zero.\n");
437       return RESULT_PARAM;
438     }
439
440   if ( frame_buf.Size() % m_BytesPerFrame != 0 )
441     {
442       DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
443       return RESULT_AS02_FORMAT;
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 += m_SamplesPerFrame;
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_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