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