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