2 Copyright (c) 2004-2012, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
27 /*! \file AS_DCP_MPEG2.cpp
28 \version $Id: AS_DCP_MPEG2.cpp,v 1.34 2012/02/07 18:54:24 jhurst Exp $
29 \brief AS-DCP library, MPEG2 essence reader and writer implementation
32 #include "AS_DCP_internal.h"
37 //------------------------------------------------------------------------------------------
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";
44 MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc)
46 ASDCP_TEST_NULL(VDescObj);
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;
54 VDesc.FrameLayout = VDescObj->FrameLayout;
55 VDesc.StoredWidth = VDescObj->StoredWidth;
56 VDesc.StoredHeight = VDescObj->StoredHeight;
57 VDesc.AspectRatio = VDescObj->AspectRatio;
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;
65 VDesc.LowDelay = VDescObj->LowDelay == 0 ? false : true;
66 VDesc.BitRate = VDescObj->BitRate;
67 VDesc.ProfileAndLevel = VDescObj->ProfileAndLevel;
74 MPEG2_VDesc_to_MD(MPEG2::VideoDescriptor& VDesc, MXF::MPEG2VideoDescriptor* VDescObj)
76 ASDCP_TEST_NULL(VDescObj);
78 VDescObj->SampleRate = VDesc.SampleRate;
79 VDescObj->ContainerDuration = VDesc.ContainerDuration;
81 VDescObj->FrameLayout = VDesc.FrameLayout;
82 VDescObj->StoredWidth = VDesc.StoredWidth;
83 VDescObj->StoredHeight = VDesc.StoredHeight;
84 VDescObj->AspectRatio = VDesc.AspectRatio;
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;
92 VDescObj->LowDelay = VDesc.LowDelay ? 1 : 0;
93 VDescObj->BitRate = VDesc.BitRate;
94 VDescObj->ProfileAndLevel = VDesc.ProfileAndLevel;
100 ASDCP::MPEG2::operator << (std::ostream& strm, const VideoDescriptor& VDesc)
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;
122 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
132 AspectRatio: %d/%d\n\
133 ComponentDepth: %u\n\
134 HorizontalSubsmpl: %u\n\
135 VerticalSubsmpl: %u\n\
137 CodedContentType: %u\n\
140 ProfileAndLevel: %u\n\
141 ContainerDuration: %u\n",
142 VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
146 VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
147 VDesc.ComponentDepth,
148 VDesc.HorizontalSubsampling,
149 VDesc.VerticalSubsampling,
151 VDesc.CodedContentType,
154 VDesc.ProfileAndLevel,
155 VDesc.ContainerDuration
159 //------------------------------------------------------------------------------------------
161 // hidden, internal implementation of MPEG2 reader
163 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
165 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
169 VideoDescriptor m_VDesc; // video parameter list
171 h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
173 Result_t OpenRead(const char*);
174 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
175 Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
176 Result_t FindFrameGOPStart(ui32_t, ui32_t&);
177 Result_t FrameType(ui32_t FrameNum, FrameType_t& type);
184 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
186 Result_t result = OpenMXFRead(filename);
188 if( ASDCP_SUCCESS(result) )
190 InterchangeObject* Object;
191 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
194 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
198 if( ASDCP_SUCCESS(result) )
199 result = InitMXFIndex();
201 if( ASDCP_SUCCESS(result) )
211 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
212 AESDecContext* Ctx, HMACContext* HMAC)
216 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
218 if ( ASDCP_SUCCESS(result) )
219 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
228 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
232 if ( ! m_File.IsOpen() )
235 // look up frame index node
236 IndexTableSegment::IndexEntry TmpEntry;
238 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
240 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
244 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
251 ASDCP::MPEG2::MXFReader::h__Reader::FrameType(ui32_t FrameNum, FrameType_t& type)
253 if ( ! m_File.IsOpen() )
256 // look up frame index node
257 IndexTableSegment::IndexEntry TmpEntry;
259 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
261 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
265 type = ( (TmpEntry.Flags & 0x0f) == 3 ) ? FRAME_B : ( (TmpEntry.Flags & 0x0f) == 2 ) ? FRAME_P : FRAME_I;
273 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
274 AESDecContext* Ctx, HMACContext* HMAC)
277 if ( ! m_File.IsOpen() )
280 Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_MPEG2Essence), Ctx, HMAC);
282 if ( ASDCP_FAILURE(result) )
285 IndexTableSegment::IndexEntry TmpEntry;
286 m_FooterPart.Lookup(FrameNum, TmpEntry);
288 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
290 case 0: FrameBuf.FrameType(FRAME_I); break;
291 case 2: FrameBuf.FrameType(FRAME_P); break;
292 case 3: FrameBuf.FrameType(FRAME_B); break;
293 default: FrameBuf.FrameType(FRAME_U);
296 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
297 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
298 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
303 //------------------------------------------------------------------------------------------
308 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
313 fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
314 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
317 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
322 Kumu::hexdump(m_Data, dump_len, stream);
326 //------------------------------------------------------------------------------------------
328 ASDCP::MPEG2::MXFReader::MXFReader()
330 m_Reader = new h__Reader(DefaultCompositeDict());
334 ASDCP::MPEG2::MXFReader::~MXFReader()
338 // Warning: direct manipulation of MXF structures can interfere
339 // with the normal operation of the wrapper. Caveat emptor!
341 ASDCP::MXF::OPAtomHeader&
342 ASDCP::MPEG2::MXFReader::OPAtomHeader()
344 if ( m_Reader.empty() )
346 assert(g_OPAtomHeader);
347 return *g_OPAtomHeader;
350 return m_Reader->m_HeaderPart;
353 // Warning: direct manipulation of MXF structures can interfere
354 // with the normal operation of the wrapper. Caveat emptor!
356 ASDCP::MXF::OPAtomIndexFooter&
357 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
359 if ( m_Reader.empty() )
361 assert(g_OPAtomIndexFooter);
362 return *g_OPAtomIndexFooter;
365 return m_Reader->m_FooterPart;
368 // Open the file for reading. The file must exist. Returns error if the
369 // operation cannot be completed.
371 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
373 return m_Reader->OpenRead(filename);
378 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
379 AESDecContext* Ctx, HMACContext* HMAC) const
381 if ( m_Reader && m_Reader->m_File.IsOpen() )
382 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
390 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
391 AESDecContext* Ctx, HMACContext* HMAC) const
393 if ( m_Reader && m_Reader->m_File.IsOpen() )
394 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
402 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
404 if ( m_Reader && m_Reader->m_File.IsOpen() )
405 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
411 // Fill the struct with the values from the file's header.
412 // Returns RESULT_INIT if the file is not open.
414 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
416 if ( m_Reader && m_Reader->m_File.IsOpen() )
418 VDesc = m_Reader->m_VDesc;
426 // Fill the struct with the values from the file's header.
427 // Returns RESULT_INIT if the file is not open.
429 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
431 if ( m_Reader && m_Reader->m_File.IsOpen() )
433 Info = m_Reader->m_Info;
442 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
444 if ( m_Reader->m_File.IsOpen() )
445 m_Reader->m_HeaderPart.Dump(stream);
451 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
453 if ( m_Reader->m_File.IsOpen() )
454 m_Reader->m_FooterPart.Dump(stream);
459 ASDCP::MPEG2::MXFReader::Close() const
461 if ( m_Reader && m_Reader->m_File.IsOpen() )
472 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
477 return m_Reader->FrameType(FrameNum, type);
481 //------------------------------------------------------------------------------------------
484 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
486 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
490 VideoDescriptor m_VDesc;
492 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
494 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_GOPOffset(0) {
495 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
500 Result_t OpenWrite(const char*, ui32_t HeaderSize);
501 Result_t SetSourceStream(const VideoDescriptor&);
502 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
507 // Open the file for writing. The file must not exist. Returns error if
508 // the operation cannot be completed.
510 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
512 if ( ! m_State.Test_BEGIN() )
515 Result_t result = m_File.OpenWrite(filename);
517 if ( ASDCP_SUCCESS(result) )
519 m_HeaderSize = HeaderSize;
520 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
521 result = m_State.Goto_INIT();
527 // Automatically sets the MXF file's metadata from the MPEG stream.
529 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
532 if ( ! m_State.Test_INIT() )
536 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
538 if ( ASDCP_SUCCESS(result) )
540 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
541 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
542 result = m_State.Goto_READY();
545 if ( ASDCP_SUCCESS(result) )
547 ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98 ) ? 24 : m_VDesc.EditRate.Numerator;
549 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
550 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
551 m_VDesc.EditRate, TCFrameRate);
557 // Writes a frame of essence to the MXF file. If the optional AESEncContext
558 // argument is present, the essence is encrypted prior to writing.
559 // Fails if the file is not open, is finalized, or an operating system
563 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
566 Result_t result = RESULT_OK;
568 if ( m_State.Test_READY() )
569 result = m_State.Goto_RUNNING(); // first time through, get the body location
571 IndexTableSegment::IndexEntry Entry;
572 Entry.StreamOffset = m_StreamOffset;
574 if ( ASDCP_SUCCESS(result) )
575 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
577 if ( ASDCP_FAILURE(result) )
580 // create mxflib flags
583 switch ( FrameBuf.FrameType() )
585 case FRAME_I: Flags = 0x00; break;
586 case FRAME_P: Flags = 0x22; break;
587 case FRAME_B: Flags = 0x33; break;
592 if ( FrameBuf.GOPStart() )
597 if ( FrameBuf.ClosedGOP() )
601 // update the index manager
602 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
603 Entry.KeyFrameOffset = 0 - m_GOPOffset;
606 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
607 Entry.TemporalOffset, Entry.KeyFrameOffset,
608 m_GOPOffset + Entry.TemporalOffset,
609 Entry.KeyFrameOffset - Entry.TemporalOffset,
612 m_FooterPart.PushIndexEntry(Entry);
620 // Closes the MXF file, writing the index and other closing information.
623 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
625 if ( ! m_State.Test_RUNNING() )
628 m_State.Goto_FINAL();
630 return WriteMXFFooter();
634 //------------------------------------------------------------------------------------------
638 ASDCP::MPEG2::MXFWriter::MXFWriter()
642 ASDCP::MPEG2::MXFWriter::~MXFWriter()
646 // Warning: direct manipulation of MXF structures can interfere
647 // with the normal operation of the wrapper. Caveat emptor!
649 ASDCP::MXF::OPAtomHeader&
650 ASDCP::MPEG2::MXFWriter::OPAtomHeader()
652 if ( m_Writer.empty() )
654 assert(g_OPAtomHeader);
655 return *g_OPAtomHeader;
658 return m_Writer->m_HeaderPart;
661 // Warning: direct manipulation of MXF structures can interfere
662 // with the normal operation of the wrapper. Caveat emptor!
664 ASDCP::MXF::OPAtomIndexFooter&
665 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
667 if ( m_Writer.empty() )
669 assert(g_OPAtomIndexFooter);
670 return *g_OPAtomIndexFooter;
673 return m_Writer->m_FooterPart;
676 // Open the file for writing. The file must not exist. Returns error if
677 // the operation cannot be completed.
679 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
680 const VideoDescriptor& VDesc, ui32_t HeaderSize)
682 if ( Info.LabelSetType == LS_MXF_SMPTE )
683 m_Writer = new h__Writer(DefaultSMPTEDict());
685 m_Writer = new h__Writer(DefaultInteropDict());
687 m_Writer->m_Info = Info;
689 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
691 if ( ASDCP_SUCCESS(result) )
692 result = m_Writer->SetSourceStream(VDesc);
694 if ( ASDCP_FAILURE(result) )
701 // Writes a frame of essence to the MXF file. If the optional AESEncContext
702 // argument is present, the essence is encrypted prior to writing.
703 // Fails if the file is not open, is finalized, or an operating system
706 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
708 if ( m_Writer.empty() )
711 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
714 // Closes the MXF file, writing the index and other closing information.
716 ASDCP::MPEG2::MXFWriter::Finalize()
718 if ( m_Writer.empty() )
721 return m_Writer->Finalize();
726 // end AS_DCP_MPEG2.cpp