the big-pre-as-02-refactor
[asdcplib.git] / src / AS_02_PCM.cpp
1 /*
2   Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
3   All rights reserved.
4
5   Redistribution and use in source and binary forms, with or without
6   modification, are permitted provided that the following conditions
7   are met:
8   1. Redistributions of source code must retain the above copyright
9   notice, this list of conditions and the following disclaimer.
10   2. Redistributions in binary form must reproduce the above copyright
11   notice, this list of conditions and the following disclaimer in the
12   documentation and/or other materials provided with the distribution.
13   3. The name of the author may not be used to endorse or promote products
14   derived from this software without specific prior written permission.
15
16   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17   IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18   OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19   IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 
27 /*! \file    AS_02_PCM.cpp
28   \version $Id$       
29   \brief   AS-02 library, PCM essence reader and writer implementation
30 */
31
32 #include "AS_02_internal.h"
33
34 #include <map>
35 #include <iostream>
36 #include <iomanip>
37
38 //------------------------------------------------------------------------------------------
39
40 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
41 static std::string SOUND_DEF_LABEL = "Sound Track";
42
43 //this must be changed because the CBR_frame_size is only   
44 //
45 static ui32_t
46 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
47 {
48   ui32_t CBR_frame_size = 0;
49
50   if ( Info.EncryptedEssence )
51     {
52       CBR_frame_size =
53         //TODO: correct?
54         /*SMPTE_UL_LENGTH  
55           + MXF_BER_LENGTH
56           + */klv_cryptinfo_size
57         + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
58         + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
59     }
60   else
61     {
62       CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc);
63     }
64
65   return CBR_frame_size;
66 }
67
68
69 //------------------------------------------------------------------------------------------
70
71
72 class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
73 {
74   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
75   h__Reader();
76
77 public:
78   ASDCP::PCM::AudioDescriptor m_ADesc;
79
80   h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d) {}
81   virtual ~h__Reader() {}
82
83   ASDCP::Result_t    OpenRead(const char*);
84   ASDCP::Result_t    ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
85
86   
87   Result_t OpenMXFRead(const char* filename);
88   // positions file before reading
89   Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
90                          const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
91
92   // reads from current position
93   Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
94                           const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
95
96
97 };
98
99
100 //
101 //
102 ASDCP::Result_t
103 AS_02::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
104 {
105   Result_t result = OpenMXFRead(filename);
106
107   if( ASDCP_SUCCESS(result) )
108     {
109       InterchangeObject* Object;
110       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
111         {
112           assert(Object);
113           result = MD_to_PCM_ADesc((ASDCP::MXF::WaveAudioDescriptor*)Object, m_ADesc);
114         }
115     }
116
117   // check for sample/frame rate sanity
118   if ( ASDCP_SUCCESS(result)
119        && m_ADesc.EditRate != EditRate_24
120        && m_ADesc.EditRate != EditRate_25
121        && m_ADesc.EditRate != EditRate_30
122        && m_ADesc.EditRate != EditRate_48
123        && m_ADesc.EditRate != EditRate_50
124        && m_ADesc.EditRate != EditRate_60
125        && m_ADesc.EditRate != EditRate_23_98 )
126     {
127       DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
128                              m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
129
130       // oh, they gave us the audio sampling rate instead, assume 24/1
131       if ( m_ADesc.EditRate == SampleRate_48k )
132         {
133           DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
134           m_ADesc.EditRate = EditRate_24;
135         }
136       else
137         {
138           // or we just drop the hammer
139           return RESULT_FORMAT;
140         }
141     }
142
143   // TODO: test file for sane CBR index BytesPerEditUnit
144
145   return result;
146 }
147
148
149 //
150 //
151 ASDCP::Result_t
152 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
153                                             ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
154 {
155   if ( ! m_File.IsOpen() )
156     return RESULT_INIT;
157
158   assert(m_Dict);
159   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
160 }
161
162
163 AS_02::PCM::MXFReader::MXFReader()
164 {
165   m_Reader = new h__Reader(DefaultCompositeDict());
166 }
167
168
169 AS_02::PCM::MXFReader::~MXFReader()
170 {
171   if ( m_Reader && m_Reader->m_File.IsOpen() )
172     m_Reader->Close();
173 }
174
175 // Warning: direct manipulation of MXF structures can interfere
176 // with the normal operation of the wrapper.  Caveat emptor!
177 //
178 ASDCP::MXF::OP1aHeader&
179 AS_02::PCM::MXFReader::OP1aHeader()
180 {
181   if ( m_Reader.empty() )
182     {
183       assert(g_OP1aHeader);
184       return *g_OP1aHeader;
185     }
186
187   return m_Reader->m_HeaderPart;
188 }
189
190 // Warning: direct manipulation of MXF structures can interfere
191 // with the normal operation of the wrapper.  Caveat emptor!
192 //
193 AS_02::MXF::AS02IndexReader&
194 AS_02::PCM::MXFReader::AS02IndexReader()
195 {
196   if ( m_Reader.empty() )
197     {
198       assert(g_AS02IndexReader);
199       return *g_AS02IndexReader;
200     }
201
202   return m_Reader->m_IndexAccess;
203 }
204
205 // Warning: direct manipulation of MXF structures can interfere
206 // with the normal operation of the wrapper.  Caveat emptor!
207 //
208 ASDCP::MXF::RIP&
209 AS_02::PCM::MXFReader::RIP()
210 {
211   if ( m_Reader.empty() )
212     {
213       assert(g_RIP);
214       return *g_RIP;
215     }
216
217   return m_Reader->m_RIP;
218 }
219
220 // Open the file for reading. The file must exist. Returns error if the
221 // operation cannot be completed.
222 ASDCP::Result_t
223 AS_02::PCM::MXFReader::OpenRead(const char* filename) const
224 {
225   return m_Reader->OpenRead(filename);
226 }
227
228 // Reads a frame of essence from the MXF file. If the optional AESEncContext
229 // argument is present, the essence is decrypted after reading. If the MXF
230 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
231 // will contain the ciphertext frame data.
232 ASDCP::Result_t
233 AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
234                                  ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
235 {
236   if ( m_Reader && m_Reader->m_File.IsOpen() )
237     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
238
239   return RESULT_INIT;
240 }
241
242
243 // Fill the struct with the values from the file's header.
244 // Returns RESULT_INIT if the file is not open.
245 ASDCP::Result_t
246 AS_02::PCM::MXFReader::FillAudioDescriptor(ASDCP::PCM::AudioDescriptor& ADesc) const
247 {
248   if ( m_Reader && m_Reader->m_File.IsOpen() )
249     {
250       ADesc = m_Reader->m_ADesc;
251       return RESULT_OK;
252     }
253
254   return RESULT_INIT;
255 }
256
257 // Fill the struct with the values from the file's header.
258 // Returns RESULT_INIT if the file is not open.
259 ASDCP::Result_t
260 AS_02::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
261 {
262   if ( m_Reader && m_Reader->m_File.IsOpen() )
263     {
264       Info = m_Reader->m_Info;
265       return RESULT_OK;
266     }
267
268   return RESULT_INIT;
269 }
270
271 //
272 void
273 AS_02::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
274 {
275   if ( m_Reader && m_Reader->m_File.IsOpen() )
276     m_Reader->m_HeaderPart.Dump(stream);
277 }
278
279
280 //
281 void
282 AS_02::PCM::MXFReader::DumpIndex(FILE* stream) const
283 {
284   if ( m_Reader->m_File.IsOpen() )
285     m_Reader->m_IndexAccess.Dump(stream);
286 }
287
288
289 //------------------------------------------------------------------------------------------
290
291 //
292 class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02Writer
293 {
294   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
295   h__Writer();
296
297 public:
298   ASDCP::PCM::AudioDescriptor m_ADesc;
299   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
300   ui64_t                        m_KLV_start;
301
302   h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_KLV_start(0){
303     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
304
305   }
306
307   virtual ~h__Writer(){}
308
309   Result_t OpenWrite(const char*, ui32_t HeaderSize);
310   Result_t SetSourceStream(const ASDCP::PCM::AudioDescriptor&);
311   Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
312
313   Result_t Finalize();
314
315   //void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
316   // const std::string& TrackName, const UL& EssenceUL,
317   // const UL& DataDefinition, const std::string& PackageLabel);
318   //void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
319   // const std::string& TrackName, const UL& DataDefinition,
320   // const std::string& PackageLabel);
321   //void AddEssenceDescriptor(const UL& WrappingUL);
322   //Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
323
324   ////new method to create BodyPartition for essence and index
325   //Result_t CreateBodyPartPair();
326   ////new method to finalize BodyPartion(index)
327   //Result_t CompleteIndexBodyPart();
328
329   // reimplement these functions in AS_02_PCM to support modifications for AS-02
330   Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
331                            const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
332   Result_t WriteAS02Footer();
333
334 };
335
336 // Open the file for writing. The file must not exist. Returns error if
337 // the operation cannot be completed.
338 ASDCP::Result_t
339 AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
340 {
341   if ( ! m_State.Test_BEGIN() )
342     return RESULT_STATE;
343
344   Result_t result = m_File.OpenWrite(filename);
345
346   if ( ASDCP_SUCCESS(result) )
347     {
348       m_HeaderSize = HeaderSize;
349       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
350       result = m_State.Goto_INIT();
351     }
352
353   return result;
354 }
355
356
357 // Automatically sets the MXF file's metadata from the WAV parser info.
358 ASDCP::Result_t
359 AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::PCM::AudioDescriptor& ADesc)
360 {
361   if ( ! m_State.Test_INIT() )
362     return RESULT_STATE;
363
364   if ( ADesc.EditRate != EditRate_24
365        && ADesc.EditRate != EditRate_25
366        && ADesc.EditRate != EditRate_30
367        && ADesc.EditRate != EditRate_48
368        && ADesc.EditRate != EditRate_50
369        && ADesc.EditRate != EditRate_60
370        && ADesc.EditRate != EditRate_23_98 )
371     {
372       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
373                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
374       return RESULT_RAW_FORMAT;
375     }
376
377   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
378     {
379       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
380                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
381       return RESULT_RAW_FORMAT;
382     }
383
384   assert(m_Dict);
385   m_ADesc = ADesc;
386
387   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
388
389   if ( ASDCP_SUCCESS(result) )
390     {
391       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
392       //SMPTE 382M-2007: ByteNo. 15 - 02h - Wave Clip-Wrapped Element
393       m_EssenceUL[SMPTE_UL_LENGTH-2] = 2; // 02h - Wave Clip-Wrapped Element
394       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
395       result = m_State.Goto_READY();
396     }
397
398   if ( ASDCP_SUCCESS(result) )
399     {
400       ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
401
402       result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
403                                SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
404                                m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
405     }
406
407   return result;
408 }
409
410
411 //
412 //
413 ASDCP::Result_t
414 AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
415                                              HMACContext* HMAC)
416 {
417   Result_t result = RESULT_OK;
418
419   if ( m_State.Test_READY() )
420     result = m_State.Goto_RUNNING(); // first time through
421
422   if ( ASDCP_SUCCESS(result) )
423     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
424
425   if ( ASDCP_SUCCESS(result) )
426     m_FramesWritten++;
427
428   return result;
429 }
430
431 // Closes the MXF file, writing the index and other closing information.
432 //
433 ASDCP::Result_t
434 AS_02::PCM::MXFWriter::h__Writer::Finalize()
435 {
436   if ( ! m_State.Test_RUNNING() )
437     return RESULT_STATE;
438
439   m_State.Goto_FINAL();
440
441   return WriteAS02Footer();
442 }
443
444
445 //------------------------------------------------------------------------------------------
446 //
447
448
449
450 AS_02::PCM::MXFWriter::MXFWriter()
451 {
452 }
453
454 AS_02::PCM::MXFWriter::~MXFWriter()
455 {
456 }
457
458 // Warning: direct manipulation of MXF structures can interfere
459 // with the normal operation of the wrapper.  Caveat emptor!
460 //
461 ASDCP::MXF::OP1aHeader&
462 AS_02::PCM::MXFWriter::OP1aHeader()
463 {
464   if ( m_Writer.empty() )
465     {
466       assert(g_OP1aHeader);
467       return *g_OP1aHeader;
468     }
469
470   return m_Writer->m_HeaderPart;
471 }
472
473 // Warning: direct manipulation of MXF structures can interfere
474 // with the normal operation of the wrapper.  Caveat emptor!
475 //
476 ASDCP::MXF::RIP&
477 AS_02::PCM::MXFWriter::RIP()
478 {
479   if ( m_Writer.empty() )
480     {
481       assert(g_RIP);
482       return *g_RIP;
483     }
484
485   return m_Writer->m_RIP;
486 }
487
488
489 // Open the file for writing. The file must not exist. Returns error if
490 // the operation cannot be completed.
491 ASDCP::Result_t
492 AS_02::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
493                                  const ASDCP::PCM::AudioDescriptor& ADesc, ui32_t HeaderSize)
494 {
495   m_Writer = new h__Writer(DefaultSMPTEDict());
496   m_Writer->m_Info = Info;
497
498   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
499
500   if ( ASDCP_SUCCESS(result) )
501     result = m_Writer->SetSourceStream(ADesc);
502
503   if ( ASDCP_FAILURE(result) )
504     m_Writer.release();
505
506   return result;
507 }
508
509 // Writes a frame of essence to the MXF file. If the optional AESEncContext
510 // argument is present, the essence is encrypted prior to writing.
511 // Fails if the file is not open, is finalized, or an operating system
512 // error occurs.
513 ASDCP::Result_t
514 AS_02::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
515 {
516   if ( m_Writer.empty() )
517     return RESULT_INIT;
518
519   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
520 }
521
522 // Closes the MXF file, writing the index and other closing information.
523 ASDCP::Result_t
524 AS_02::PCM::MXFWriter::Finalize()
525 {
526   if ( m_Writer.empty() )
527     return RESULT_INIT;
528
529   return m_Writer->Finalize();
530 }
531
532
533
534 //
535 // end AS_02_PCM.cpp
536 //
537