release me!
[asdcplib.git] / src / AS_DCP_PCM.cpp
1 /*
2 Copyright (c) 2004-2011, 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 ASDCP::Result_t
378 ASDCP::PCM::MXFReader::Close() const
379 {
380   if ( m_Reader && m_Reader->m_File.IsOpen() )
381     {
382       m_Reader->Close();
383       return RESULT_OK;
384     }
385
386   return RESULT_INIT;
387 }
388
389
390 //------------------------------------------------------------------------------------------
391
392 //
393 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
394 {
395   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
396   h__Writer();
397
398 public:
399   AudioDescriptor m_ADesc;
400   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
401   
402   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
403     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
404   }
405
406   ~h__Writer(){}
407
408   Result_t OpenWrite(const char*, ui32_t HeaderSize);
409   Result_t SetSourceStream(const AudioDescriptor&);
410   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
411   Result_t Finalize();
412 };
413
414
415
416 // Open the file for writing. The file must not exist. Returns error if
417 // the operation cannot be completed.
418 ASDCP::Result_t
419 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
420 {
421   if ( ! m_State.Test_BEGIN() )
422     return RESULT_STATE;
423
424   Result_t result = m_File.OpenWrite(filename);
425
426   if ( ASDCP_SUCCESS(result) )
427     {
428       m_HeaderSize = HeaderSize;
429       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
430       result = m_State.Goto_INIT();
431     }
432
433   return result;
434 }
435
436
437 // Automatically sets the MXF file's metadata from the WAV parser info.
438 ASDCP::Result_t
439 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
440 {
441   if ( ! m_State.Test_INIT() )
442     return RESULT_STATE;
443
444   if ( ADesc.EditRate != EditRate_24
445        && ADesc.EditRate != EditRate_25
446        && ADesc.EditRate != EditRate_30
447        && ADesc.EditRate != EditRate_48
448        && ADesc.EditRate != EditRate_50
449        && ADesc.EditRate != EditRate_60
450        && ADesc.EditRate != EditRate_23_98 )
451     {
452       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
453                              ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
454       return RESULT_RAW_FORMAT;
455     }
456
457   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
458     {
459       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
460                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
461       return RESULT_RAW_FORMAT;
462     }
463
464   assert(m_Dict);
465   m_ADesc = ADesc;
466
467   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
468   
469   if ( ASDCP_SUCCESS(result) )
470     {
471       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
472       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
473       result = m_State.Goto_READY();
474     }
475
476   if ( ASDCP_SUCCESS(result) )
477     {
478       ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
479       
480       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
481                               SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
482                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
483     }
484
485   return result;
486 }
487
488
489 //
490 //
491 ASDCP::Result_t
492 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
493                                              HMACContext* HMAC)
494 {
495   Result_t result = RESULT_OK;
496
497   if ( m_State.Test_READY() )
498     result = m_State.Goto_RUNNING(); // first time through
499
500   if ( ASDCP_SUCCESS(result) )
501     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
502
503   if ( ASDCP_SUCCESS(result) )
504     m_FramesWritten++;
505
506   return result;
507 }
508
509 // Closes the MXF file, writing the index and other closing information.
510 //
511 ASDCP::Result_t
512 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
513 {
514   if ( ! m_State.Test_RUNNING() )
515     return RESULT_STATE;
516
517   m_State.Goto_FINAL();
518
519   return WriteMXFFooter();
520 }
521
522
523 //------------------------------------------------------------------------------------------
524 //
525
526
527
528 ASDCP::PCM::MXFWriter::MXFWriter()
529 {
530 }
531
532 ASDCP::PCM::MXFWriter::~MXFWriter()
533 {
534 }
535
536
537 // Open the file for writing. The file must not exist. Returns error if
538 // the operation cannot be completed.
539 ASDCP::Result_t
540 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
541                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
542 {
543   if ( Info.LabelSetType == LS_MXF_SMPTE )
544     m_Writer = new h__Writer(DefaultSMPTEDict());
545   else
546     m_Writer = new h__Writer(DefaultInteropDict());
547
548   m_Writer->m_Info = Info;
549   
550   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
551
552   if ( ASDCP_SUCCESS(result) )
553     result = m_Writer->SetSourceStream(ADesc);
554
555   if ( ASDCP_FAILURE(result) )
556     m_Writer.release();
557
558   return result;
559 }
560
561 // Writes a frame of essence to the MXF file. If the optional AESEncContext
562 // argument is present, the essence is encrypted prior to writing.
563 // Fails if the file is not open, is finalized, or an operating system
564 // error occurs.
565 ASDCP::Result_t
566 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
567 {
568   if ( m_Writer.empty() )
569     return RESULT_INIT;
570
571   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
572 }
573
574 // Closes the MXF file, writing the index and other closing information.
575 ASDCP::Result_t
576 ASDCP::PCM::MXFWriter::Finalize()
577 {
578   if ( m_Writer.empty() )
579     return RESULT_INIT;
580
581   return m_Writer->Finalize();
582 }
583
584 //
585 // end AS_DCP_PCM.cpp
586 //
587