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