version bump
[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.SampleRate;
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.SampleRate = 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.SampleRate.Numerator << "/" << ADesc.SampleRate.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         SampleRate: %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.SampleRate.Numerator ,ADesc.SampleRate.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.SampleRate != EditRate_24
229        && m_ADesc.SampleRate != EditRate_48
230        && m_ADesc.SampleRate != EditRate_23_98 )
231     {
232       DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
233                              m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
234
235       // oh, they gave us the audio sampling rate instead, assume 24/1
236       if ( m_ADesc.SampleRate == SampleRate_48k )
237         {
238           DefaultLogSink().Warn("adjusting SampleRate to 24/1\n"); 
239           m_ADesc.SampleRate.Numerator = 24;
240           m_ADesc.SampleRate.Denominator = 1;
241         }
242       else
243         {
244           // or we just drop the hammer
245           return RESULT_FORMAT;
246         }
247     }
248
249   if( ASDCP_SUCCESS(result) )
250     result = InitMXFIndex();
251
252   if( ASDCP_SUCCESS(result) )
253     result = InitInfo();
254
255   // TODO: test file for sane CBR index BytesPerEditUnit
256
257   return result;
258 }
259
260
261 //
262 //
263 ASDCP::Result_t
264 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
265                                             AESDecContext* Ctx, HMACContext* HMAC)
266 {
267   if ( ! m_File.IsOpen() )
268     return RESULT_INIT;
269
270   assert(m_Dict);
271   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
272 }
273
274 //------------------------------------------------------------------------------------------
275
276
277 //
278 void
279 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
280 {
281   if ( stream == 0 )
282     stream = stderr;
283
284   fprintf(stream, "Frame: %06u, %7u bytes\n",
285           m_FrameNumber, m_Size);
286
287   if ( dump_len )
288     Kumu::hexdump(m_Data, dump_len, stream);
289 }
290
291 //------------------------------------------------------------------------------------------
292
293 ASDCP::PCM::MXFReader::MXFReader()
294 {
295   m_Reader = new h__Reader(DefaultCompositeDict());
296 }
297
298
299 ASDCP::PCM::MXFReader::~MXFReader()
300 {
301   if ( m_Reader && m_Reader->m_File.IsOpen() )
302     m_Reader->Close();
303 }
304
305 // Open the file for reading. The file must exist. Returns error if the
306 // operation cannot be completed.
307 ASDCP::Result_t
308 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
309 {
310   return m_Reader->OpenRead(filename);
311 }
312
313 // Reads a frame of essence from the MXF file. If the optional AESEncContext
314 // argument is present, the essence is decrypted after reading. If the MXF
315 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
316 // will contain the ciphertext frame data.
317 ASDCP::Result_t
318 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
319                                  AESDecContext* Ctx, HMACContext* HMAC) const
320 {
321   if ( m_Reader && m_Reader->m_File.IsOpen() )
322     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
323
324   return RESULT_INIT;
325 }
326
327
328 // Fill the struct with the values from the file's header.
329 // Returns RESULT_INIT if the file is not open.
330 ASDCP::Result_t
331 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
332 {
333   if ( m_Reader && m_Reader->m_File.IsOpen() )
334     {
335       ADesc = m_Reader->m_ADesc;
336       return RESULT_OK;
337     }
338
339   return RESULT_INIT;
340 }
341
342 // Fill the struct with the values from the file's header.
343 // Returns RESULT_INIT if the file is not open.
344 ASDCP::Result_t
345 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
346 {
347   if ( m_Reader && m_Reader->m_File.IsOpen() )
348     {
349       Info = m_Reader->m_Info;
350       return RESULT_OK;
351     }
352
353   return RESULT_INIT;
354 }
355
356 //
357 void
358 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
359 {
360   if ( m_Reader && m_Reader->m_File.IsOpen() )
361     m_Reader->m_HeaderPart.Dump(stream);
362 }
363
364
365 //
366 void
367 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
368 {
369   if ( m_Reader->m_File.IsOpen() )
370     m_Reader->m_FooterPart.Dump(stream);
371 }
372
373
374 //------------------------------------------------------------------------------------------
375
376 //
377 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
378 {
379   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
380   h__Writer();
381
382 public:
383   AudioDescriptor m_ADesc;
384   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
385   
386   h__Writer(const Dictionary& d) : ASDCP::h__Writer(d) {
387     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
388   }
389
390   ~h__Writer(){}
391
392   Result_t OpenWrite(const char*, ui32_t HeaderSize);
393   Result_t SetSourceStream(const AudioDescriptor&);
394   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
395   Result_t Finalize();
396 };
397
398
399
400 // Open the file for writing. The file must not exist. Returns error if
401 // the operation cannot be completed.
402 ASDCP::Result_t
403 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
404 {
405   if ( ! m_State.Test_BEGIN() )
406     return RESULT_STATE;
407
408   Result_t result = m_File.OpenWrite(filename);
409
410   if ( ASDCP_SUCCESS(result) )
411     {
412       m_HeaderSize = HeaderSize;
413       m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
414       result = m_State.Goto_INIT();
415     }
416
417   return result;
418 }
419
420
421 // Automatically sets the MXF file's metadata from the WAV parser info.
422 ASDCP::Result_t
423 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
424 {
425   if ( ! m_State.Test_INIT() )
426     return RESULT_STATE;
427
428   if ( ADesc.SampleRate != EditRate_24
429        && ADesc.SampleRate != EditRate_48
430        && ADesc.SampleRate != EditRate_23_98 )
431     {
432       DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
433                              ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
434       return RESULT_RAW_FORMAT;
435     }
436
437   if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
438     {
439       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
440                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
441       return RESULT_RAW_FORMAT;
442     }
443
444   assert(m_Dict);
445   m_ADesc = ADesc;
446   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
447   
448   if ( ASDCP_SUCCESS(result) )
449       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
450                               SOUND_DEF_LABEL,   UL(m_Dict->ul(MDD_SoundDataDef)),
451                               m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
452
453   if ( ASDCP_SUCCESS(result) )
454     {
455       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
456       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
457       result = m_State.Goto_READY();
458     }
459
460   return result;
461 }
462
463
464 //
465 //
466 ASDCP::Result_t
467 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
468                                              HMACContext* HMAC)
469 {
470   Result_t result = RESULT_OK;
471
472   if ( m_State.Test_READY() )
473     result = m_State.Goto_RUNNING(); // first time through
474
475   if ( ASDCP_SUCCESS(result) )
476     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
477
478   if ( ASDCP_SUCCESS(result) )
479     m_FramesWritten++;
480
481   return result;
482 }
483
484 // Closes the MXF file, writing the index and other closing information.
485 //
486 ASDCP::Result_t
487 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
488 {
489   if ( ! m_State.Test_RUNNING() )
490     return RESULT_STATE;
491
492   m_State.Goto_FINAL();
493
494   return WriteMXFFooter();
495 }
496
497
498 //------------------------------------------------------------------------------------------
499 //
500
501
502
503 ASDCP::PCM::MXFWriter::MXFWriter()
504 {
505 }
506
507 ASDCP::PCM::MXFWriter::~MXFWriter()
508 {
509 }
510
511
512 // Open the file for writing. The file must not exist. Returns error if
513 // the operation cannot be completed.
514 ASDCP::Result_t
515 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
516                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
517 {
518   if ( Info.LabelSetType == LS_MXF_SMPTE )
519     m_Writer = new h__Writer(DefaultSMPTEDict());
520   else
521     m_Writer = new h__Writer(DefaultInteropDict());
522
523   m_Writer->m_Info = Info;
524   
525   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
526
527   if ( ASDCP_SUCCESS(result) )
528     result = m_Writer->SetSourceStream(ADesc);
529
530   if ( ASDCP_FAILURE(result) )
531     m_Writer.release();
532
533   return result;
534 }
535
536 // Writes a frame of essence to the MXF file. If the optional AESEncContext
537 // argument is present, the essence is encrypted prior to writing.
538 // Fails if the file is not open, is finalized, or an operating system
539 // error occurs.
540 ASDCP::Result_t
541 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
542 {
543   if ( m_Writer.empty() )
544     return RESULT_INIT;
545
546   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
547 }
548
549 // Closes the MXF file, writing the index and other closing information.
550 ASDCP::Result_t
551 ASDCP::PCM::MXFWriter::Finalize()
552 {
553   if ( m_Writer.empty() )
554     return RESULT_INIT;
555
556   return m_Writer->Finalize();
557 }
558
559 //
560 // end AS_DCP_PCM.cpp
561 //
562