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