separated PackageUID from Timed Text Asset ID
[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 ( ASDCP_SUCCESS(result)
164        && m_ADesc.SampleRate != EditRate_24
165        && m_ADesc.SampleRate != EditRate_48
166        && m_ADesc.SampleRate != EditRate_23_98 )
167     {
168       DefaultLogSink().Error("PCM file SampleRate is not 24/1, 48/1 or 24000/1001: %08x/%08x\n", // lu
169                              m_ADesc.SampleRate.Numerator, m_ADesc.SampleRate.Denominator);
170
171       // oh, they gave us the audio sampling rate instead, assume 24/1
172       if ( m_ADesc.SampleRate == SampleRate_48k )
173         {
174           DefaultLogSink().Warn("adjusting SampleRate to 24/1\n"); 
175           m_ADesc.SampleRate.Numerator = 24;
176           m_ADesc.SampleRate.Denominator = 1;
177         }
178       else
179         {
180           // or we just drop the hammer
181           return RESULT_FORMAT;
182         }
183     }
184
185   if( ASDCP_SUCCESS(result) )
186     result = InitMXFIndex();
187
188   if( ASDCP_SUCCESS(result) )
189     result = InitInfo();
190
191   // TODO: test file for sane CBR index BytesPerEditUnit
192
193   return result;
194 }
195
196
197 //
198 //
199 ASDCP::Result_t
200 ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
201                                             AESDecContext* Ctx, HMACContext* HMAC)
202 {
203   if ( ! m_File.IsOpen() )
204     return RESULT_INIT;
205
206   return ReadEKLVFrame(FrameNum, FrameBuf, Dict::ul(MDD_WAVEssence), Ctx, HMAC);
207 }
208
209 //------------------------------------------------------------------------------------------
210
211
212 //
213 void
214 ASDCP::PCM::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
215 {
216   if ( stream == 0 )
217     stream = stderr;
218
219   fprintf(stream, "Frame: %06u, %7u bytes\n",
220           m_FrameNumber, m_Size);
221
222   if ( dump_len )
223     Kumu::hexdump(m_Data, dump_len, stream);
224 }
225
226 //------------------------------------------------------------------------------------------
227
228 ASDCP::PCM::MXFReader::MXFReader()
229 {
230   m_Reader = new h__Reader;
231 }
232
233
234 ASDCP::PCM::MXFReader::~MXFReader()
235 {
236   if ( m_Reader && m_Reader->m_File.IsOpen() )
237     m_Reader->Close();
238 }
239
240 // Open the file for reading. The file must exist. Returns error if the
241 // operation cannot be completed.
242 ASDCP::Result_t
243 ASDCP::PCM::MXFReader::OpenRead(const char* filename) const
244 {
245   return m_Reader->OpenRead(filename);
246 }
247
248 // Reads a frame of essence from the MXF file. If the optional AESEncContext
249 // argument is present, the essence is decrypted after reading. If the MXF
250 // file is encrypted and the AESDecContext argument is NULL, the frame buffer
251 // will contain the ciphertext frame data.
252 ASDCP::Result_t
253 ASDCP::PCM::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
254                                  AESDecContext* Ctx, HMACContext* HMAC) const
255 {
256   if ( m_Reader && m_Reader->m_File.IsOpen() )
257     return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
258
259   return RESULT_INIT;
260 }
261
262
263 // Fill the struct with the values from the file's header.
264 // Returns RESULT_INIT if the file is not open.
265 ASDCP::Result_t
266 ASDCP::PCM::MXFReader::FillAudioDescriptor(AudioDescriptor& ADesc) const
267 {
268   if ( m_Reader && m_Reader->m_File.IsOpen() )
269     {
270       ADesc = m_Reader->m_ADesc;
271       return RESULT_OK;
272     }
273
274   return RESULT_INIT;
275 }
276
277 // Fill the struct with the values from the file's header.
278 // Returns RESULT_INIT if the file is not open.
279 ASDCP::Result_t
280 ASDCP::PCM::MXFReader::FillWriterInfo(WriterInfo& Info) const
281 {
282   if ( m_Reader && m_Reader->m_File.IsOpen() )
283     {
284       Info = m_Reader->m_Info;
285       return RESULT_OK;
286     }
287
288   return RESULT_INIT;
289 }
290
291 //
292 void
293 ASDCP::PCM::MXFReader::DumpHeaderMetadata(FILE* stream) const
294 {
295   if ( m_Reader && m_Reader->m_File.IsOpen() )
296     m_Reader->m_HeaderPart.Dump(stream);
297 }
298
299
300 //
301 void
302 ASDCP::PCM::MXFReader::DumpIndex(FILE* stream) const
303 {
304   if ( m_Reader->m_File.IsOpen() )
305     m_Reader->m_FooterPart.Dump(stream);
306 }
307
308
309 //------------------------------------------------------------------------------------------
310
311 //
312 class ASDCP::PCM::MXFWriter::h__Writer : public ASDCP::h__Writer
313 {
314 public:
315   AudioDescriptor m_ADesc;
316   byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
317
318
319   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
320   
321   h__Writer(){
322     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
323   }
324
325   ~h__Writer(){}
326
327   Result_t OpenWrite(const char*, ui32_t HeaderSize);
328   Result_t SetSourceStream(const AudioDescriptor&);
329   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
330   Result_t Finalize();
331 };
332
333
334
335 // Open the file for writing. The file must not exist. Returns error if
336 // the operation cannot be completed.
337 ASDCP::Result_t
338 ASDCP::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
339 {
340   if ( ! m_State.Test_BEGIN() )
341     return RESULT_STATE;
342
343   Result_t result = m_File.OpenWrite(filename);
344
345   if ( ASDCP_SUCCESS(result) )
346     {
347       m_HeaderSize = HeaderSize;
348       m_EssenceDescriptor = new WaveAudioDescriptor;
349       result = m_State.Goto_INIT();
350     }
351
352   return result;
353 }
354
355
356 // Automatically sets the MXF file's metadata from the WAV parser info.
357 ASDCP::Result_t
358 ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
359 {
360   if ( ! m_State.Test_INIT() )
361     return RESULT_STATE;
362
363   if ( ADesc.SampleRate != EditRate_24
364        && ADesc.SampleRate != EditRate_48
365        && ADesc.SampleRate != EditRate_23_98 )
366     {
367       DefaultLogSink().Error("AudioDescriptor.SampleRate is not 24/1, 48/1 or 24000/1001: %d/%d\n",
368                              ADesc.SampleRate.Numerator, ADesc.SampleRate.Denominator);
369       return RESULT_RAW_FORMAT;
370     }
371
372   if ( ADesc.AudioSamplingRate != SampleRate_48k )
373     {
374       DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1: %d/%d\n",
375                              ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
376       return RESULT_RAW_FORMAT;
377     }
378
379   m_ADesc = ADesc;
380   Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
381   
382   if ( ASDCP_SUCCESS(result) )
383       result = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(Dict::ul(MDD_WAVWrapping)),
384                               SOUND_DEF_LABEL,   UL(Dict::ul(MDD_SoundDataDef)),
385                               m_ADesc.SampleRate, 24 /* TCFrameRate */, calc_CBR_frame_size(m_Info, m_ADesc));
386
387   if ( ASDCP_SUCCESS(result) )
388     {
389       memcpy(m_EssenceUL, Dict::ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
390       m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
391       result = m_State.Goto_READY();
392     }
393
394   return result;
395 }
396
397
398 //
399 //
400 ASDCP::Result_t
401 ASDCP::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
402                                              HMACContext* HMAC)
403 {
404   Result_t result = RESULT_OK;
405
406   if ( m_State.Test_READY() )
407     result = m_State.Goto_RUNNING(); // first time through
408
409   if ( ASDCP_SUCCESS(result) )
410     result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
411
412   if ( ASDCP_SUCCESS(result) )
413     m_FramesWritten++;
414
415   return result;
416 }
417
418 // Closes the MXF file, writing the index and other closing information.
419 //
420 ASDCP::Result_t
421 ASDCP::PCM::MXFWriter::h__Writer::Finalize()
422 {
423   if ( ! m_State.Test_RUNNING() )
424     return RESULT_STATE;
425
426   m_State.Goto_FINAL();
427
428   return WriteMXFFooter();
429 }
430
431
432 //------------------------------------------------------------------------------------------
433 //
434
435
436
437 ASDCP::PCM::MXFWriter::MXFWriter()
438 {
439 }
440
441 ASDCP::PCM::MXFWriter::~MXFWriter()
442 {
443 }
444
445
446 // Open the file for writing. The file must not exist. Returns error if
447 // the operation cannot be completed.
448 ASDCP::Result_t
449 ASDCP::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
450                                  const AudioDescriptor& ADesc, ui32_t HeaderSize)
451 {
452   m_Writer = new h__Writer;
453   
454   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
455
456   if ( ASDCP_SUCCESS(result) )
457     {
458       m_Writer->m_Info = Info;
459       result = m_Writer->SetSourceStream(ADesc);
460     }
461
462   if ( ASDCP_FAILURE(result) )
463     m_Writer.release();
464
465   return result;
466 }
467
468 // Writes a frame of essence to the MXF file. If the optional AESEncContext
469 // argument is present, the essence is encrypted prior to writing.
470 // Fails if the file is not open, is finalized, or an operating system
471 // error occurs.
472 ASDCP::Result_t
473 ASDCP::PCM::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
474 {
475   if ( m_Writer.empty() )
476     return RESULT_INIT;
477
478   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
479 }
480
481 // Closes the MXF file, writing the index and other closing information.
482 ASDCP::Result_t
483 ASDCP::PCM::MXFWriter::Finalize()
484 {
485   if ( m_Writer.empty() )
486     return RESULT_INIT;
487
488   return m_Writer->Finalize();
489 }
490
491 //
492 // end AS_DCP_PCM.cpp
493 //
494