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