release!
[asdcplib.git] / src / AS_DCP_PCM.cpp
1 /*
2 Copyright (c) 2004-2010, 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
454   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
455   
456   if ( ASDCP_SUCCESS(result) )
457     {
458       memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
459       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
460       result = m_State.Goto_READY();
461     }
462
463   if ( ASDCP_SUCCESS(result) )
464     {
465       ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
466       
467       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
468                               SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
469                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
470     }
471
472   return result;
473 }
474
475
476 //
477 //
478 ASDCP::Result_t
479 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
480                                              HMACContext* HMAC)
481 {
482   Result_t result = RESULT_OK;
483
484   if ( m_State.Test_READY() )
485     result = m_State.Goto_RUNNING(); // first time through
486
487   if ( ASDCP_SUCCESS(result) )
488     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
489
490   if ( ASDCP_SUCCESS(result) )
491     m_FramesWritten++;
492
493   return result;
494 }
495
496 // Closes the MXF file, writing the index and other closing information.
497 //
498 ASDCP::Result_t
499 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
500 {
501   if ( ! m_State.Test_RUNNING() )
502     return RESULT_STATE;
503
504   m_State.Goto_FINAL();
505
506   return WriteMXFFooter();
507 }
508
509
510 //------------------------------------------------------------------------------------------
511 //
512
513
514
515 ASDCP::PCM::MXFWriter::MXFWriter()
516 {
517 }
518
519 ASDCP::PCM::MXFWriter::~MXFWriter()
520 {
521 }
522
523
524 // Open the file for writing. The file must not exist. Returns error if
525 // the operation cannot be completed.
526 ASDCP::Result_t
527 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
528                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
529 {
530   if ( Info.LabelSetType == LS_MXF_SMPTE )
531     m_Writer = new h__Writer(DefaultSMPTEDict());
532   else
533     m_Writer = new h__Writer(DefaultInteropDict());
534
535   m_Writer->m_Info = Info;
536   
537   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
538
539   if ( ASDCP_SUCCESS(result) )
540     result = m_Writer->SetSourceStream(ADesc);
541
542   if ( ASDCP_FAILURE(result) )
543     m_Writer.release();
544
545   return result;
546 }
547
548 // Writes a frame of essence to the MXF file. If the optional AESEncContext
549 // argument is present, the essence is encrypted prior to writing.
550 // Fails if the file is not open, is finalized, or an operating system
551 // error occurs.
552 ASDCP::Result_t
553 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
554 {
555   if ( m_Writer.empty() )
556     return RESULT_INIT;
557
558   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
559 }
560
561 // Closes the MXF file, writing the index and other closing information.
562 ASDCP::Result_t
563 ASDCP::PCM::MXFWriter::Finalize()
564 {
565   if ( m_Writer.empty() )
566     return RESULT_INIT;
567
568   return m_Writer->Finalize();
569 }
570
571 //
572 // end AS_DCP_PCM.cpp
573 //
574