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