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