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