added aiff reader
[asdcplib.git] / src / AS_DCP_PCM.cpp
1 /*
2 Copyright (c) 2004-2006, 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
35 //------------------------------------------------------------------------------------------
36
37 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M frame wrapping of wave audio";
38 static std::string SOUND_DEF_LABEL = "Sound Track";
39
40 //
41 Result_t
42 PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, MXF::WaveAudioDescriptor* ADescObj)
43 {
44   ASDCP_TEST_NULL(ADescObj);
45   ADescObj->SampleRate = ADesc.SampleRate;
46   ADescObj->AudioSamplingRate = ADesc.AudioSamplingRate;
47   ADescObj->Locked = ADesc.Locked;
48   ADescObj->ChannelCount = ADesc.ChannelCount;
49   ADescObj->QuantizationBits = ADesc.QuantizationBits;
50   ADescObj->BlockAlign = ADesc.BlockAlign;
51   ADescObj->AvgBps = ADesc.AvgBps;
52   ADescObj->LinkedTrackID = ADesc.LinkedTrackID;
53   ADescObj->ContainerDuration = ADesc.ContainerDuration;
54   return RESULT_OK;
55 }
56
57 //
58 ASDCP::Result_t
59 MD_to_PCM_ADesc(MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc)
60 {
61   ASDCP_TEST_NULL(ADescObj);
62   ADesc.SampleRate = ADescObj->SampleRate;
63   ADesc.AudioSamplingRate = ADescObj->AudioSamplingRate;
64   ADesc.Locked = ADescObj->Locked;
65   ADesc.ChannelCount = ADescObj->ChannelCount;
66   ADesc.QuantizationBits = ADescObj->QuantizationBits;
67   ADesc.BlockAlign = ADescObj->BlockAlign;
68   ADesc.AvgBps = ADescObj->AvgBps;
69   ADesc.LinkedTrackID = ADescObj->LinkedTrackID;
70   ADesc.ContainerDuration = ADescObj->ContainerDuration;
71   return RESULT_OK;
72 }
73
74 void
75 ASDCP::PCM::AudioDescriptorDump(const AudioDescriptor& ADesc, FILE* stream)
76 {
77   if ( stream == 0 )
78     stream = stderr;
79
80   fprintf(stream, "\
81         SampleRate: %lu/%lu\n\
82  AudioSamplingRate: %lu/%lu\n\
83             Locked: %lu\n\
84       ChannelCount: %lu\n\
85   QuantizationBits: %lu\n\
86         BlockAlign: %lu\n\
87             AvgBps: %lu\n\
88      LinkedTrackID: %lu\n\
89  ContainerDuration: %lu\n",
90           ADesc.SampleRate.Numerator ,ADesc.SampleRate.Denominator,
91           ADesc.AudioSamplingRate.Numerator ,ADesc.AudioSamplingRate.Denominator,
92           ADesc.Locked,
93           ADesc.ChannelCount,
94           ADesc.QuantizationBits,
95           ADesc.BlockAlign,
96           ADesc.AvgBps,
97           ADesc.LinkedTrackID,
98           ADesc.ContainerDuration
99           );
100 }
101
102
103 //
104 //
105 static ui32_t
106 calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
107 {
108   ui32_t CBR_frame_size = 0;
109
110   if ( Info.EncryptedEssence )
111     {
112       CBR_frame_size =
113         SMPTE_UL_LENGTH
114         + MXF_BER_LENGTH
115         + klv_cryptinfo_size
116         + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
117         + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
118     }
119   else
120     {
121       CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc) + SMPTE_UL_LENGTH + MXF_BER_LENGTH;
122     }
123
124   return CBR_frame_size;
125 }
126
127
128 //------------------------------------------------------------------------------------------
129
130
131 class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
132 {
133   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
134
135 public:
136   AudioDescriptor m_ADesc;
137
138   h__Reader() {}
139   ~h__Reader() {}
140   Result_t    OpenRead(const char*);
141   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
142 };
143
144
145 //
146 //
147 ASDCP::Result_t
148 ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
149 {
150   Result_t result = OpenMXFRead(filename);
151
152   if( ASDCP_SUCCESS(result) )
153     {
154       InterchangeObject* Object;
155       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
156         {
157           assert(Object);
158           result = MD_to_PCM_ADesc((MXF::WaveAudioDescriptor*)Object, m_ADesc);
159         }
160     }
161
162   // check for sample/frame rate sanity
163   if ( m_ADesc.SampleRate != EditRate_24
164        && m_ADesc.SampleRate != EditRate_48
165        && m_ADesc.SampleRate != EditRate_23_98 )
166     {
167       DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
168                              m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
169
170       // oh, they gave us the audio sampling rate instead, assume 24/1
171       if ( m_ADesc.SampleRate == SampleRate_48k )
172         {
173           DefaultLogSink().Warn("adjusting SampleRate to 24/1\n"); 
174           m_ADesc.SampleRate.Numerator = 24;
175           m_ADesc.SampleRate.Denominator = 1;
176         }
177       else
178         {
179           // or we just drop the hammer
180           return RESULT_FORMAT;
181         }
182     }
183
184   if( ASDCP_SUCCESS(result) )
185     result = InitMXFIndex();
186
187   if( ASDCP_SUCCESS(result) )
188     result = InitInfo(m_Info);
189
190   // TODO: test file for sane CBR index BytesPerEditUnit
191
192   return result;
193 }
194
195
196 //
197 //
198 ASDCP::Result_t
199 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
200                                             AESDecContext* Ctx, HMACContext* HMAC)
201 {
202   if ( ! m_File.IsOpen() )
203     return RESULT_INIT;
204
205   return ReadEKLVPacket(FrameNum, FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
206 }
207
208 //------------------------------------------------------------------------------------------
209
210
211 //
212 void
213 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
214 {
215   if ( stream == 0 )
216     stream = stderr;
217
218   fprintf(stream, "Frame: %06lu, %7lu bytes\n",
219           m_FrameNumber, m_Size);
220
221   if ( dump_len )
222     hexdump(m_Data, dump_len, stream);
223 }
224
225 //------------------------------------------------------------------------------------------
226
227 ASDCP::PCM::MXFReader::MXFReader()
228 {
229   m_Reader = new h__Reader;
230 }
231
232
233 ASDCP::PCM::MXFReader::~MXFReader()
234 {
235   if ( m_Reader && m_Reader->m_File.IsOpen() )
236     m_Reader->Close();
237 }
238
239 // Open the file for reading. The file must exist. Returns error if the
240 // operation cannot be completed.
241 ASDCP::Result_t
242 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
243 {
244   return m_Reader->OpenRead(filename);
245 }
246
247 // Reads a frame of essence from the MXF file. If the optional AESEncContext
248 // argument is present, the essence is decrypted after reading. If the MXF
249 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
250 // will contain the ciphertext frame data.
251 ASDCP::Result_t
252 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
253                                  AESDecContext* Ctx, HMACContext* HMAC) const
254 {
255   if ( m_Reader && m_Reader->m_File.IsOpen() )
256     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
257
258   return RESULT_INIT;
259 }
260
261
262 // Fill the struct with the values from the file's header.
263 // Returns RESULT_INIT if the file is not open.
264 ASDCP::Result_t
265 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
266 {
267   if ( m_Reader && m_Reader->m_File.IsOpen() )
268     {
269       ADesc = m_Reader->m_ADesc;
270       return RESULT_OK;
271     }
272
273   return RESULT_INIT;
274 }
275
276 // Fill the struct with the values from the file's header.
277 // Returns RESULT_INIT if the file is not open.
278 ASDCP::Result_t
279 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
280 {
281   if ( m_Reader && m_Reader->m_File.IsOpen() )
282     {
283       Info = m_Reader->m_Info;
284       return RESULT_OK;
285     }
286
287   return RESULT_INIT;
288 }
289
290 //
291 void
292 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
293 {
294   if ( m_Reader && m_Reader->m_File.IsOpen() )
295     m_Reader->m_HeaderPart.Dump(stream);
296 }
297
298
299 //
300 void
301 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
302 {
303   if ( m_Reader->m_File.IsOpen() )
304     m_Reader->m_FooterPart.Dump(stream);
305 }
306
307
308 //------------------------------------------------------------------------------------------
309
310 //
311 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
312 {
313 public:
314   AudioDescriptor m_ADesc;
315
316   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
317   
318   h__Writer(){}
319   ~h__Writer(){}
320
321   Result_t OpenWrite(const char*, ui32_t HeaderSize);
322   Result_t SetSourceStream(const AudioDescriptor&);
323   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
324   Result_t Finalize();
325 };
326
327
328
329 // Open the file for writing. The file must not exist. Returns error if
330 // the operation cannot be completed.
331 ASDCP::Result_t
332 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
333 {
334   if ( ! m_State.Test_BEGIN() )
335     return RESULT_STATE;
336
337   Result_t result = m_File.OpenWrite(filename);
338
339   if ( ASDCP_SUCCESS(result) )
340     {
341       m_HeaderSize = HeaderSize;
342       m_EssenceDescriptor = new WaveAudioDescriptor;
343       result = m_State.Goto_INIT();
344     }
345
346   return result;
347 }
348
349
350 // Automatically sets the MXF file's metadata from the WAV parser info.
351 ASDCP::Result_t
352 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
353 {
354   if ( ! m_State.Test_INIT() )
355     return RESULT_STATE;
356
357   if ( ADesc.SampleRate != EditRate_24
358        && ADesc.SampleRate != EditRate_48
359        && ADesc.SampleRate != EditRate_23_98 )
360     {
361       DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %lu/%lu\n",
362                              ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
363       return RESULT_RAW_FORMAT;
364     }
365
366   if ( ADesc.AudioSamplingRate != SampleRate_48k )
367     {
368       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %lu/%lu\n",
369                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
370       return RESULT_RAW_FORMAT;
371     }
372
373   m_ADesc = ADesc;
374   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
375   
376   if ( ASDCP_SUCCESS(result) )
377       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
378                               SOUND_DEF_LABEL,   UL(Dict::ul(MDD_SoundDataDef)),
379                               m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
380
381   if ( ASDCP_SUCCESS(result) )
382     result = m_State.Goto_READY();
383
384   return result;
385 }
386
387
388 //
389 //
390 ASDCP::Result_t
391 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
392                                              HMACContext* HMAC)
393 {
394   Result_t result = RESULT_OK;
395
396   if ( m_State.Test_READY() )
397     result = m_State.Goto_RUNNING(); // first time through
398
399   if ( ASDCP_SUCCESS(result) )
400     result = WriteEKLVPacket(FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
401
402   if ( ASDCP_SUCCESS(result) )
403     m_FramesWritten++;
404
405   return result;
406 }
407
408 // Closes the MXF file, writing the index and other closing information.
409 //
410 ASDCP::Result_t
411 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
412 {
413   if ( ! m_State.Test_RUNNING() )
414     return RESULT_STATE;
415
416   m_State.Goto_FINAL();
417
418   return WriteMXFFooter();
419 }
420
421
422 //------------------------------------------------------------------------------------------
423 //
424
425
426
427 ASDCP::PCM::MXFWriter::MXFWriter()
428 {
429 }
430
431 ASDCP::PCM::MXFWriter::~MXFWriter()
432 {
433 }
434
435
436 // Open the file for writing. The file must not exist. Returns error if
437 // the operation cannot be completed.
438 ASDCP::Result_t
439 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
440                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
441 {
442   m_Writer = new h__Writer;
443   
444   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
445
446   if ( ASDCP_SUCCESS(result) )
447     {
448       m_Writer->m_Info = Info;
449       result = m_Writer->SetSourceStream(ADesc);
450     }
451
452   if ( ASDCP_FAILURE(result) )
453     m_Writer.release();
454
455   return result;
456 }
457
458 // Writes a frame of essence to the MXF file. If the optional AESEncContext
459 // argument is present, the essence is encrypted prior to writing.
460 // Fails if the file is not open, is finalized, or an operating system
461 // error occurs.
462 ASDCP::Result_t
463 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
464 {
465   if ( m_Writer.empty() )
466     return RESULT_INIT;
467
468   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
469 }
470
471 // Closes the MXF file, writing the index and other closing information.
472 ASDCP::Result_t
473 ASDCP::PCM::MXFWriter::Finalize()
474 {
475   if ( m_Writer.empty() )
476     return RESULT_INIT;
477
478   return m_Writer->Finalize();
479 }
480
481 //
482 // end AS_DCP_PCM.cpp
483 //
484