added version info to libpyasdcp (same as other lib versions)
[asdcplib.git] / src / AS_DCP_PCM.cpp
1 /*
2 Copyright (c) 2004-2009, 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_DCP_PCM.cpp
28     \version $Id$       
29     \brief   AS-DCP library, PCM essence reader and writer implementation
30 */
31
32 #include "AS_DCP_internal.h"
33 #include <map>
34 #include <iostream>
35 #include <iomanip>
36
37 //------------------------------------------------------------------------------------------
38
39 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of wave audio";
40 static std::string SOUND_DEF_LABEL = "Sound Track";
41
42 static byte_t SNDFMT_CFG_1_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
43                                       0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x01, 0x00 };
44
45 static byte_t SNDFMT_CFG_2_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
46                                       0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x02, 0x00 };
47
48 static byte_t SNDFMT_CFG_3_UL[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0b,
49                                       0x04, 0x02, 0x02, 0x10, 0x03, 0x01, 0x03, 0x00 };
50
51 //
52 Result_t
53 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
54 {
55   ASDCP_TEST_NULL(ADescObj);
56   ADescObj->SampleRate = ADesc.EditRate;
57   ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
58   ADescObj->Locked = ADesc.Locked;
59   ADescObj->ChannelCount = ADesc.ChannelCount;
60   ADescObj->QuantizationBits = ADesc.QuantizationBits;
61   ADescObj->BlockAlign = ADesc.BlockAlign;
62   ADescObj->AvgBps = ADesc.AvgBps;
63   ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
64   ADescObj->ContainerDuration = ADesc.ContainerDuration;
65
66   ADescObj->ChannelAssignment.Reset();
67
68   switch ( ADesc.ChannelFormat )
69     {
70       case PCM::CF_CFG_1:
71         ADescObj->ChannelAssignment = UL(SNDFMT_CFG_1_UL);
72         break;
73
74       case PCM::CF_CFG_2:
75         ADescObj->ChannelAssignment = UL(SNDFMT_CFG_2_UL);
76         break;
77
78       case PCM::CF_CFG_3:
79         ADescObj->ChannelAssignment = UL(SNDFMT_CFG_3_UL);
80         break;
81     }
82
83   return RESULT_OK;
84 }
85
86 //
87 ASDCP::Result_t
88 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
89 {
90   ASDCP_TEST_NULL(ADescObj);
91   ADesc.EditRate = ADescObj->SampleRate;
92   ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
93   ADesc.Locked = ADescObj->Locked;
94   ADesc.ChannelCount = ADescObj->ChannelCount;
95   ADesc.QuantizationBits = ADescObj->QuantizationBits;
96   ADesc.BlockAlign = ADescObj->BlockAlign;
97   ADesc.AvgBps = ADescObj->AvgBps;
98   ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
99   assert(ADescObj->ContainerDuration <= 0xFFFFFFFFL);
100   ADesc.ContainerDuration = (ui32_t) ADescObj->ContainerDuration;
101
102   ADesc.ChannelFormat = PCM::CF_NONE;
103
104   if ( ADescObj->ChannelAssignment.HasValue() )
105     {
106       if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_1_UL) )
107         ADesc.ChannelFormat = PCM::CF_CFG_1;
108
109       else if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_2_UL) )
110         ADesc.ChannelFormat = PCM::CF_CFG_2;
111
112       else if ( ADescObj->ChannelAssignment == UL(SNDFMT_CFG_3_UL) )
113         ADesc.ChannelFormat = PCM::CF_CFG_3;
114     }
115
116   return RESULT_OK;
117 }
118
119 //
120 std::ostream&
121 ASDCP::PCM::operator << (std::ostream& strm, const AudioDescriptor& ADesc)
122 {
123   strm << "        SampleRate: " << ADesc.EditRate.Numerator << "/" << ADesc.EditRate.Denominator << std::endl;
124   strm << " AudioSamplingRate: " << ADesc.AudioSamplingRate.Numerator << "/" << ADesc.AudioSamplingRate.Denominator << std::endl;
125   strm << "            Locked: " << (unsigned) ADesc.Locked << std::endl;
126   strm << "      ChannelCount: " << (unsigned) ADesc.ChannelCount << std::endl;
127   strm << "  QuantizationBits: " << (unsigned) ADesc.QuantizationBits << std::endl;
128   strm << "        BlockAlign: " << (unsigned) ADesc.BlockAlign << std::endl;
129   strm << "            AvgBps: " << (unsigned) ADesc.AvgBps << std::endl;
130   strm << "     LinkedTrackID: " << (unsigned) ADesc.LinkedTrackID << std::endl;
131   strm << " ContainerDuration: " << (unsigned) ADesc.ContainerDuration << std::endl;
132
133   return strm;
134 }
135
136 //
137 void
138 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
139 {
140   if ( stream == 0 )
141     stream = stderr;
142
143   fprintf(stream, "\
144         EditRate: %d/%d\n\
145  AudioSamplingRate: %d/%d\n\
146             Locked: %u\n\
147       ChannelCount: %u\n\
148   QuantizationBits: %u\n\
149         BlockAlign: %u\n\
150             AvgBps: %u\n\
151      LinkedTrackID: %u\n\
152  ContainerDuration: %u\n",
153           ADesc.EditRate.Numerator, ADesc.EditRate.Denominator,
154           ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator,
155           ADesc.Locked,
156           ADesc.ChannelCount,
157           ADesc.QuantizationBits,
158           ADesc.BlockAlign,
159           ADesc.AvgBps,
160           ADesc.LinkedTrackID,
161           ADesc.ContainerDuration
162           );
163 }
164
165
166 //
167 //
168 static ui32_t
169 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
170 {
171   ui32_t CBR_frame_size = 0;
172
173   if ( Info.EncryptedEssence )
174     {
175       CBR_frame_size =
176         SMPTE_UL_LENGTH
177         + MXF_BER_LENGTH
178         + klv_cryptinfo_size
179         + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
180         + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
181     }
182   else
183     {
184       CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
185     }
186
187   return CBR_frame_size;
188 }
189
190
191 //------------------------------------------------------------------------------------------
192
193
194 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
195 {
196   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
197   h__Reader();
198
199 public:
200   AudioDescriptor m_ADesc;
201
202   h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
203   ~h__Reader() {}
204   Result_t    OpenRead(const char*);
205   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
206 };
207
208
209 //
210 //
211 ASDCP::Result_t
212 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
213 {
214   Result_t result = OpenMXFRead(filename);
215
216   if( ASDCP_SUCCESS(result) )
217     {
218       InterchangeObject* Object;
219       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
220         {
221           assert(Object);
222           result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
223         }
224     }
225
226   // check for sample/frame rate sanity
227   if ( ASDCP_SUCCESS(result)
228        && m_ADesc.EditRate != EditRate_24
229        && m_ADesc.EditRate != EditRate_25
230        && m_ADesc.EditRate != EditRate_30
231        && m_ADesc.EditRate != EditRate_48
232        && m_ADesc.EditRate != EditRate_50
233        && m_ADesc.EditRate != EditRate_60
234        && m_ADesc.EditRate != EditRate_23_98 )
235     {
236       DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
237                              m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
238
239       // oh, they gave us the audio sampling rate instead, assume 24/1
240       if ( m_ADesc.EditRate == SampleRate_48k )
241         {
242           DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
243           m_ADesc.EditRate = EditRate_24;
244         }
245       else
246         {
247           // or we just drop the hammer
248           return RESULT_FORMAT;
249         }
250     }
251
252   if( ASDCP_SUCCESS(result) )
253     result = InitMXFIndex();
254
255   if( ASDCP_SUCCESS(result) )
256     result = InitInfo();
257
258   // TODO: test file for sane CBR index BytesPerEditUnit
259
260   return result;
261 }
262
263
264 //
265 //
266 ASDCP::Result_t
267 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
268                                             AESDecContext* Ctx, HMACContext* HMAC)
269 {
270   if ( ! m_File.IsOpen() )
271     return RESULT_INIT;
272
273   assert(m_Dict);
274   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
275 }
276
277 //------------------------------------------------------------------------------------------
278
279
280 //
281 void
282 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
283 {
284   if ( stream == 0 )
285     stream = stderr;
286
287   fprintf(stream, "Frame: %06u, %7u bytes\n",
288           m_FrameNumber, m_Size);
289
290   if ( dump_len )
291     Kumu::hexdump(m_Data, dump_len, stream);
292 }
293
294 //------------------------------------------------------------------------------------------
295
296 ASDCP::PCM::MXFReader::MXFReader()
297 {
298   m_Reader = new h__Reader(DefaultCompositeDict());
299 }
300
301
302 ASDCP::PCM::MXFReader::~MXFReader()
303 {
304   if ( m_Reader && m_Reader->m_File.IsOpen() )
305     m_Reader->Close();
306 }
307
308 // Open the file for reading. The file must exist. Returns error if the
309 // operation cannot be completed.
310 ASDCP::Result_t
311 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
312 {
313   return m_Reader->OpenRead(filename);
314 }
315
316 // Reads a frame of essence from the MXF file. If the optional AESEncContext
317 // argument is present, the essence is decrypted after reading. If the MXF
318 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
319 // will contain the ciphertext frame data.
320 ASDCP::Result_t
321 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
322                                  AESDecContext* Ctx, HMACContext* HMAC) const
323 {
324   if ( m_Reader && m_Reader->m_File.IsOpen() )
325     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
326
327   return RESULT_INIT;
328 }
329
330
331 // Fill the struct with the values from the file's header.
332 // Returns RESULT_INIT if the file is not open.
333 ASDCP::Result_t
334 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
335 {
336   if ( m_Reader && m_Reader->m_File.IsOpen() )
337     {
338       ADesc = m_Reader->m_ADesc;
339       return RESULT_OK;
340     }
341
342   return RESULT_INIT;
343 }
344
345 // Fill the struct with the values from the file's header.
346 // Returns RESULT_INIT if the file is not open.
347 ASDCP::Result_t
348 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
349 {
350   if ( m_Reader && m_Reader->m_File.IsOpen() )
351     {
352       Info = m_Reader->m_Info;
353       return RESULT_OK;
354     }
355
356   return RESULT_INIT;
357 }
358
359 //
360 void
361 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
362 {
363   if ( m_Reader && m_Reader->m_File.IsOpen() )
364     m_Reader->m_HeaderPart.Dump(stream);
365 }
366
367
368 //
369 void
370 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
371 {
372   if ( m_Reader->m_File.IsOpen() )
373     m_Reader->m_FooterPart.Dump(stream);
374 }
375
376
377 //------------------------------------------------------------------------------------------
378
379 //
380 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
381 {
382   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
383   h__Writer();
384
385 public:
386   AudioDescriptor m_ADesc;
387   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
388   
389   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
390     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
391   }
392
393   ~h__Writer(){}
394
395   Result_t OpenWrite(const char*, ui32_t HeaderSize);
396   Result_t SetSourceStream(const AudioDescriptor&);
397   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
398   Result_t Finalize();
399 };
400
401
402
403 // Open the file for writing. The file must not exist. Returns error if
404 // the operation cannot be completed.
405 ASDCP::Result_t
406 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
407 {
408   if ( ! m_State.Test_BEGIN() )
409     return RESULT_STATE;
410
411   Result_t result = m_File.OpenWrite(filename);
412
413   if ( ASDCP_SUCCESS(result) )
414     {
415       m_HeaderSize = HeaderSize;
416       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
417       result = m_State.Goto_INIT();
418     }
419
420   return result;
421 }
422
423
424 // Automatically sets the MXF file's metadata from the WAV parser info.
425 ASDCP::Result_t
426 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
427 {
428   if ( ! m_State.Test_INIT() )
429     return RESULT_STATE;
430
431   if ( ADesc.EditRate != EditRate_24
432        && ADesc.EditRate != EditRate_25
433        && ADesc.EditRate != EditRate_30
434        && ADesc.EditRate != EditRate_48
435        && ADesc.EditRate != EditRate_50
436        && ADesc.EditRate != EditRate_60
437        && ADesc.EditRate != EditRate_23_98 )
438     {
439       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
440                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
441       return RESULT_RAW_FORMAT;
442     }
443
444   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
445     {
446       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
447                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
448       return RESULT_RAW_FORMAT;
449     }
450
451   assert(m_Dict);
452   m_ADesc = ADesc;
453   ui32_t TCFrameRate = ( ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
454
455   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
456   
457   if ( ASDCP_SUCCESS(result) )
458       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
459                               SOUND_DEF_LABEL,   UL(m_Dict->ul(MDD_SoundDataDef)),
460                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
461
462   if ( ASDCP_SUCCESS(result) )
463     {
464       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
465       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
466       result = m_State.Goto_READY();
467     }
468
469   return result;
470 }
471
472
473 //
474 //
475 ASDCP::Result_t
476 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
477                                              HMACContext* HMAC)
478 {
479   Result_t result = RESULT_OK;
480
481   if ( m_State.Test_READY() )
482     result = m_State.Goto_RUNNING(); // first time through
483
484   if ( ASDCP_SUCCESS(result) )
485     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
486
487   if ( ASDCP_SUCCESS(result) )
488     m_FramesWritten++;
489
490   return result;
491 }
492
493 // Closes the MXF file, writing the index and other closing information.
494 //
495 ASDCP::Result_t
496 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
497 {
498   if ( ! m_State.Test_RUNNING() )
499     return RESULT_STATE;
500
501   m_State.Goto_FINAL();
502
503   return WriteMXFFooter();
504 }
505
506
507 //------------------------------------------------------------------------------------------
508 //
509
510
511
512 ASDCP::PCM::MXFWriter::MXFWriter()
513 {
514 }
515
516 ASDCP::PCM::MXFWriter::~MXFWriter()
517 {
518 }
519
520
521 // Open the file for writing. The file must not exist. Returns error if
522 // the operation cannot be completed.
523 ASDCP::Result_t
524 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
525                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
526 {
527   if ( Info.LabelSetType == LS_MXF_SMPTE )
528     m_Writer = new h__Writer(DefaultSMPTEDict());
529   else
530     m_Writer = new h__Writer(DefaultInteropDict());
531
532   m_Writer->m_Info = Info;
533   
534   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
535
536   if ( ASDCP_SUCCESS(result) )
537     result = m_Writer->SetSourceStream(ADesc);
538
539   if ( ASDCP_FAILURE(result) )
540     m_Writer.release();
541
542   return result;
543 }
544
545 // Writes a frame of essence to the MXF file. If the optional AESEncContext
546 // argument is present, the essence is encrypted prior to writing.
547 // Fails if the file is not open, is finalized, or an operating system
548 // error occurs.
549 ASDCP::Result_t
550 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
551 {
552   if ( m_Writer.empty() )
553     return RESULT_INIT;
554
555   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
556 }
557
558 // Closes the MXF file, writing the index and other closing information.
559 ASDCP::Result_t
560 ASDCP::PCM::MXFWriter::Finalize()
561 {
562   if ( m_Writer.empty() )
563     return RESULT_INIT;
564
565   return m_Writer->Finalize();
566 }
567
568 //
569 // end AS_DCP_PCM.cpp
570 //
571