builds on Darwin
[asdcplib.git] / src / AS_DCP_MPEG2.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_MPEG2.cpp
28     \version $Id$       
29     \brief   AS-DCP library, MPEG2 essence reader and writer implementation
30 */
31
32 #include "AS_DCP_internal.h"
33 #include "MDD.h"
34
35
36 //------------------------------------------------------------------------------------------
37
38 //
39 ASDCP::Result_t
40 ASDCP::MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc)
41 {
42   ASDCP_TEST_NULL(VDescObj);
43   VDesc = *((MPEG2::VideoDescriptor*)VDescObj);
44   VDesc.FrameRate = 0;
45   return RESULT_OK;
46 }
47
48
49 //
50 void
51 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
52 {
53   if ( stream == 0 )
54     stream = stderr;
55
56   fprintf(stream, "\
57            SampleRate: %lu/%lu\n\
58           FrameLayout: %lu\n\
59           StoredWidth: %lu\n\
60          StoredHeight: %lu\n\
61           AspectRatio: %lu/%lu\n\
62        ComponentDepth: %lu\n\
63 HorizontalSubsampling: %lu\n\
64   VerticalSubsampling: %lu\n\
65           ColorSiting: %lu\n\
66      CodedContentType: %lu\n\
67              LowDelay: %lu\n\
68               BitRate: %lu\n\
69       ProfileAndLevel: %lu\n\
70     ContainerDuration: %lu\n",
71           VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
72           VDesc.FrameLayout,
73           VDesc.StoredWidth,
74           VDesc.StoredHeight,
75           VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
76           VDesc.ComponentDepth,
77           VDesc.HorizontalSubsampling,
78           VDesc.VerticalSubsampling,
79           VDesc.ColorSiting,
80           VDesc.CodedContentType,
81           VDesc.LowDelay,
82           VDesc.BitRate,
83           VDesc.ProfileAndLevel,
84           VDesc.ContainerDuration
85           );
86 }
87
88 //------------------------------------------------------------------------------------------
89 //
90 // hidden, internal implementation of MPEG2 reader
91
92 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
93 {
94   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
95
96 public:
97   VideoDescriptor m_VDesc;        // video parameter list
98
99   h__Reader() {}
100   ~h__Reader() {}
101   Result_t    OpenRead(const char*);
102   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
103   Result_t    ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
104   Result_t    FindFrameGOPStart(ui32_t, ui32_t&);
105 };
106
107
108 //
109 //
110 ASDCP::Result_t
111 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
112 {
113   Result_t result = OpenMXFRead(filename);
114
115   if( ASDCP_SUCCESS(result) )
116     {
117       InterchangeObject* Object;
118       if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
119         {
120           assert(Object);
121           result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
122         }
123     }
124
125   if( ASDCP_SUCCESS(result) )
126     result = InitMXFIndex();
127
128   if( ASDCP_SUCCESS(result) )
129     result = InitInfo(m_Info);
130
131   return result;
132 }
133
134
135 //
136 //
137 ASDCP::Result_t
138 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
139                                                       AESDecContext* Ctx, HMACContext* HMAC)
140 {
141   ui32_t KeyFrameNum;
142
143   Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
144
145   if ( ASDCP_SUCCESS(result) )
146     result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
147
148   return result;
149 }
150
151
152 //
153 //
154 ASDCP::Result_t
155 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
156 {
157   KeyFrameNum = 0;
158
159   if ( ! m_File.IsOpen() )
160     return RESULT_INIT;
161
162   // look up frame index node
163   IndexTableSegment::IndexEntry TmpEntry;
164
165   if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
166     {
167       DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
168       return RESULT_RANGE;
169     }
170
171   KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
172
173   return RESULT_OK;
174 }
175
176
177 //
178 //
179 ASDCP::Result_t
180 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
181                                               AESDecContext* Ctx, HMACContext* HMAC)
182 {
183   if ( ! m_File.IsOpen() )
184     return RESULT_INIT;
185
186   Result_t result = ReadEKLVPacket(FrameNum, FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC);
187
188   if ( ASDCP_FAILURE(result) )
189     return result;
190
191   IndexTableSegment::IndexEntry TmpEntry;
192   m_FooterPart.Lookup(FrameNum, TmpEntry);
193
194   switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
195     {
196     case 0:  FrameBuf.FrameType(FRAME_I); break;
197     case 2:  FrameBuf.FrameType(FRAME_P); break;
198     case 3:  FrameBuf.FrameType(FRAME_B); break;
199     default: FrameBuf.FrameType(FRAME_U);
200     }
201
202   FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
203   FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
204   FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
205
206   return RESULT_OK;
207 }
208
209 //------------------------------------------------------------------------------------------
210
211
212 //
213 void
214 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
215 {
216   if ( stream == 0 )
217     stream = stderr;
218
219   fprintf(stream, "Frame: %06lu, %c%-2hu, %7lu bytes",
220           m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
221
222   if ( m_GOPStart )
223     fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
224   
225   fputc('\n', stream);
226
227   if ( dump_len > 0 )
228     hexdump(m_Data, dump_len, stream);
229 }
230
231
232 //------------------------------------------------------------------------------------------
233
234 ASDCP::MPEG2::MXFReader::MXFReader()
235 {
236   m_Reader = new h__Reader;
237 }
238
239
240 ASDCP::MPEG2::MXFReader::~MXFReader()
241 {
242 }
243
244 // Open the file for reading. The file must exist. Returns error if the
245 // operation cannot be completed.
246 ASDCP::Result_t
247 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
248 {
249   return m_Reader->OpenRead(filename);
250 }
251
252 //
253 ASDCP::Result_t
254 ASDCP::MPEG2::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 //
265 ASDCP::Result_t
266 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
267                                            AESDecContext* Ctx, HMACContext* HMAC) const
268 {
269   if ( m_Reader && m_Reader->m_File.IsOpen() )
270     return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
271
272   return RESULT_INIT;
273 }
274
275
276 //
277 ASDCP::Result_t
278 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
279 {
280   if ( m_Reader && m_Reader->m_File.IsOpen() )
281     return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
282
283   return RESULT_INIT;
284 }
285
286
287 // Fill the struct with the values from the file's header.
288 // Returns RESULT_INIT if the file is not open.
289 ASDCP::Result_t
290 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
291 {
292   if ( m_Reader && m_Reader->m_File.IsOpen() )
293     {
294       VDesc = m_Reader->m_VDesc;
295       return RESULT_OK;
296     }
297
298   return RESULT_INIT;
299 }
300
301
302 // Fill the struct with the values from the file's header.
303 // Returns RESULT_INIT if the file is not open.
304 ASDCP::Result_t
305 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
306 {
307   if ( m_Reader && m_Reader->m_File.IsOpen() )
308     {
309       Info = m_Reader->m_Info;
310       return RESULT_OK;
311     }
312
313   return RESULT_INIT;
314 }
315
316 //
317 void
318 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
319 {
320   if ( m_Reader->m_File.IsOpen() )
321     m_Reader->m_HeaderPart.Dump(stream);
322 }
323
324
325 //
326 void
327 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
328 {
329   if ( m_Reader->m_File.IsOpen() )
330     m_Reader->m_FooterPart.Dump(stream);
331 }
332
333
334 //------------------------------------------------------------------------------------------
335 #if 0
336
337
338 //
339 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
340 {
341 public:
342   VideoDescriptor m_VDesc;
343   ui32_t          m_GOPOffset;
344
345   ASDCP_NO_COPY_CONSTRUCT(h__Writer);
346
347   h__Writer() : m_GOPOffset(0) {}
348   ~h__Writer(){}
349
350   Result_t OpenWrite(const char*, ui32_t HeaderSize);
351   Result_t SetSourceStream(const VideoDescriptor&);
352   Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
353   Result_t Finalize();
354 };
355
356
357 // Open the file for writing. The file must not exist. Returns error if
358 // the operation cannot be completed.
359 ASDCP::Result_t
360 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
361 {
362   if ( ! m_State.Test_BEGIN() )
363     return RESULT_STATE;
364
365   m_File = new MXFFile;
366
367   Result_t result = m_File->OpenWrite(filename);
368
369   if ( ASDCP_SUCCESS(result) )
370     {
371       m_EssenceDescriptor = new MDObject("MPEG2VideoDescriptor");
372       result = m_State.Goto_INIT();
373     }
374
375   return result;
376 }
377
378 // Automatically sets the MXF file's metadata from the MPEG stream.
379 ASDCP::Result_t
380 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
381 {
382   if ( ! m_State.Test_INIT() )
383     return RESULT_STATE;
384
385   m_VDesc = VDesc;
386   Result_t result = MPEG2_VDesc_to_MD(m_VDesc, *m_EssenceDescriptor);
387
388   if ( ASDCP_SUCCESS(result) )
389     result = WriteMXFHeader(ESS_MPEG2_VES, m_VDesc.EditRate, 24 /* TCFrameRate */);
390
391   if ( ASDCP_SUCCESS(result) )
392     result = m_State.Goto_READY();
393
394   return result;
395 }
396
397 // Writes a frame of essence to the MXF file. If the optional AESEncContext
398 // argument is present, the essence is encrypted prior to writing.
399 // Fails if the file is not open, is finalized, or an operating system
400 // error occurs.
401 //
402 ASDCP::Result_t
403 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
404                                                HMACContext* HMAC)
405 {
406   Result_t result = RESULT_OK;
407
408   if ( m_State.Test_READY() )
409     result = m_State.Goto_RUNNING(); // first time through, get the body location
410
411   ui64_t ThisOffset = m_StreamOffset;
412
413   if ( ASDCP_SUCCESS(result) )
414     result = WriteEKLVPacket(FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC);
415
416   if ( ASDCP_FAILURE(result) )
417     return result;
418
419   // create mxflib flags
420   int Flags = 0;
421
422   switch ( FrameBuf.FrameType() )
423     {
424     case FRAME_I: Flags = 0x00; break;
425     case FRAME_P: Flags = 0x22; break;
426     case FRAME_B: Flags = 0x33; break;
427     }
428
429   if ( FrameBuf.GOPStart() )
430     {
431       m_GOPOffset = 0;
432       Flags |= 0x40;
433
434       if ( FrameBuf.ClosedGOP() )
435         Flags |= 0x80;
436     }
437
438   // update the index manager
439   m_IndexMan->OfferEditUnit(0, m_FramesWritten, m_GOPOffset, Flags);
440   m_IndexMan->OfferTemporalOffset(m_FramesWritten, m_GOPOffset - FrameBuf.TemporalOffset());
441   m_IndexMan->OfferOffset(0, m_FramesWritten, ThisOffset);
442
443   m_FramesWritten++;
444   m_GOPOffset++;
445
446   return RESULT_OK;
447 }
448
449
450 // Closes the MXF file, writing the index and other closing information.
451 //
452 ASDCP::Result_t
453 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
454 {
455   if ( ! m_State.Test_RUNNING() )
456     return RESULT_STATE;
457
458   if ( ! m_File )
459     return RESULT_INIT;
460
461   m_State.Goto_FINAL();
462
463   return WriteMXFFooter(ESS_MPEG2_VES);
464 }
465
466
467 //------------------------------------------------------------------------------------------
468
469
470
471 ASDCP::MPEG2::MXFWriter::MXFWriter()
472 {
473 }
474
475 ASDCP::MPEG2::MXFWriter::~MXFWriter()
476 {
477 }
478
479
480 // Open the file for writing. The file must not exist. Returns error if
481 // the operation cannot be completed.
482 ASDCP::Result_t
483 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
484                                    const VideoDescriptor& VDesc, ui32_t HeaderSize)
485 {
486   m_Writer = new h__Writer;
487   
488   Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
489
490   if ( ASDCP_SUCCESS(result) )
491     {
492       m_Writer->m_Info = Info;
493       result = m_Writer->SetSourceStream(VDesc);
494     }
495
496   if ( ASDCP_FAILURE(result) )
497     m_Writer.release();
498
499   return result;
500 }
501
502
503 // Writes a frame of essence to the MXF file. If the optional AESEncContext
504 // argument is present, the essence is encrypted prior to writing.
505 // Fails if the file is not open, is finalized, or an operating system
506 // error occurs.
507 ASDCP::Result_t
508 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
509 {
510   if ( m_Writer.empty() )
511     return RESULT_INIT;
512
513   return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
514 }
515
516 // Closes the MXF file, writing the index and other closing information.
517 ASDCP::Result_t
518 ASDCP::MPEG2::MXFWriter::Finalize()
519 {
520   if ( m_Writer.empty() )
521     return RESULT_INIT;
522
523   return m_Writer->Finalize();
524 }
525 #endif
526
527 //
528 // end AS_DCP_MPEG2.cpp
529 //