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