format string fixes
[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: %d/%d\n\
82  AudioSamplingRate: %d/%d\n\
83             Locked: %u\n\
84       ChannelCount: %u\n\
85   QuantizationBits: %u\n\
86         BlockAlign: %u\n\
87             AvgBps: %u\n\
88      LinkedTrackID: %u\n\
89  ContainerDuration: %u\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();
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     Kumu::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   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
316
317
318   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
319   
320   h__Writer(){
321     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
322   }
323
324   ~h__Writer(){}
325
326   Result_t OpenWrite(const char*, ui32_t HeaderSize);
327   Result_t SetSourceStream(const AudioDescriptor&);
328   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
329   Result_t Finalize();
330 };
331
332
333
334 // Open the file for writing. The file must not exist. Returns error if
335 // the operation cannot be completed.
336 ASDCP::Result_t
337 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
338 {
339   if ( ! m_State.Test_BEGIN() )
340     return RESULT_STATE;
341
342   Result_t result = m_File.OpenWrite(filename);
343
344   if ( ASDCP_SUCCESS(result) )
345     {
346       m_HeaderSize = HeaderSize;
347       m_EssenceDescriptor = new WaveAudioDescriptor;
348       result = m_State.Goto_INIT();
349     }
350
351   return result;
352 }
353
354
355 // Automatically sets the MXF file's metadata from the WAV parser info.
356 ASDCP::Result_t
357 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
358 {
359   if ( ! m_State.Test_INIT() )
360     return RESULT_STATE;
361
362   if ( ADesc.SampleRate != EditRate_24
363        && ADesc.SampleRate != EditRate_48
364        && ADesc.SampleRate != EditRate_23_98 )
365     {
366       DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
367                              ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
368       return RESULT_RAW_FORMAT;
369     }
370
371   if ( ADesc.AudioSamplingRate != SampleRate_48k )
372     {
373       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %d/%d\n",
374                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
375       return RESULT_RAW_FORMAT;
376     }
377
378   m_ADesc = ADesc;
379   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
380   
381   if ( ASDCP_SUCCESS(result) )
382       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
383                               SOUND_DEF_LABEL,   UL(Dict::ul(MDD_SoundDataDef)),
384                               m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
385
386   if ( ASDCP_SUCCESS(result) )
387     {
388       memcpy(m_EssenceUL, Dict::ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
389       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
390       result = m_State.Goto_READY();
391     }
392
393   return result;
394 }
395
396
397 //
398 //
399 ASDCP::Result_t
400 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
401                                              HMACContext* HMAC)
402 {
403   Result_t result = RESULT_OK;
404
405   if ( m_State.Test_READY() )
406     result = m_State.Goto_RUNNING(); // first time through
407
408   if ( ASDCP_SUCCESS(result) )
409     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
410
411   if ( ASDCP_SUCCESS(result) )
412     m_FramesWritten++;
413
414   return result;
415 }
416
417 // Closes the MXF file, writing the index and other closing information.
418 //
419 ASDCP::Result_t
420 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
421 {
422   if ( ! m_State.Test_RUNNING() )
423     return RESULT_STATE;
424
425   m_State.Goto_FINAL();
426
427   return WriteMXFFooter();
428 }
429
430
431 //------------------------------------------------------------------------------------------
432 //
433
434
435
436 ASDCP::PCM::MXFWriter::MXFWriter()
437 {
438 }
439
440 ASDCP::PCM::MXFWriter::~MXFWriter()
441 {
442 }
443
444
445 // Open the file for writing. The file must not exist. Returns error if
446 // the operation cannot be completed.
447 ASDCP::Result_t
448 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
449                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
450 {
451   m_Writer = new h__Writer;
452   
453   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
454
455   if ( ASDCP_SUCCESS(result) )
456     {
457       m_Writer->m_Info = Info;
458       result = m_Writer->SetSourceStream(ADesc);
459     }
460
461   if ( ASDCP_FAILURE(result) )
462     m_Writer.release();
463
464   return result;
465 }
466
467 // Writes a frame of essence to the MXF file. If the optional AESEncContext
468 // argument is present, the essence is encrypted prior to writing.
469 // Fails if the file is not open, is finalized, or an operating system
470 // error occurs.
471 ASDCP::Result_t
472 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
473 {
474   if ( m_Writer.empty() )
475     return RESULT_INIT;
476
477   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
478 }
479
480 // Closes the MXF file, writing the index and other closing information.
481 ASDCP::Result_t
482 ASDCP::PCM::MXFWriter::Finalize()
483 {
484   if ( m_Writer.empty() )
485     return RESULT_INIT;
486
487   return m_Writer->Finalize();
488 }
489
490 //
491 // end AS_DCP_PCM.cpp
492 //
493