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
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__ASDCPReader
165 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
169 VideoDescriptor m_VDesc; // video parameter list
171 h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(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()
336 if ( m_Reader && m_Reader->m_File.IsOpen() )
340 // Warning: direct manipulation of MXF structures can interfere
341 // with the normal operation of the wrapper. Caveat emptor!
343 ASDCP::MXF::OPAtomHeader&
344 ASDCP::MPEG2::MXFReader::OPAtomHeader()
346 if ( m_Reader.empty() )
348 assert(g_OPAtomHeader);
349 return *g_OPAtomHeader;
352 return m_Reader->m_HeaderPart;
355 // Warning: direct manipulation of MXF structures can interfere
356 // with the normal operation of the wrapper. Caveat emptor!
358 ASDCP::MXF::OPAtomIndexFooter&
359 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
361 if ( m_Reader.empty() )
363 assert(g_OPAtomIndexFooter);
364 return *g_OPAtomIndexFooter;
367 return m_Reader->m_FooterPart;
370 // Open the file for reading. The file must exist. Returns error if the
371 // operation cannot be completed.
373 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
375 return m_Reader->OpenRead(filename);
380 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
381 AESDecContext* Ctx, HMACContext* HMAC) const
383 if ( m_Reader && m_Reader->m_File.IsOpen() )
384 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
392 ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
394 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
399 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
400 AESDecContext* Ctx, HMACContext* HMAC) const
402 if ( m_Reader && m_Reader->m_File.IsOpen() )
403 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
411 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
413 if ( m_Reader && m_Reader->m_File.IsOpen() )
414 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
420 // Fill the struct with the values from the file's header.
421 // Returns RESULT_INIT if the file is not open.
423 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
425 if ( m_Reader && m_Reader->m_File.IsOpen() )
427 VDesc = m_Reader->m_VDesc;
435 // Fill the struct with the values from the file's header.
436 // Returns RESULT_INIT if the file is not open.
438 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
440 if ( m_Reader && m_Reader->m_File.IsOpen() )
442 Info = m_Reader->m_Info;
451 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
453 if ( m_Reader->m_File.IsOpen() )
454 m_Reader->m_HeaderPart.Dump(stream);
460 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
462 if ( m_Reader->m_File.IsOpen() )
463 m_Reader->m_FooterPart.Dump(stream);
468 ASDCP::MPEG2::MXFReader::Close() const
470 if ( m_Reader && m_Reader->m_File.IsOpen() )
481 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
486 return m_Reader->FrameType(FrameNum, type);
490 //------------------------------------------------------------------------------------------
493 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
495 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
499 VideoDescriptor m_VDesc;
501 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
503 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_GOPOffset(0) {
504 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
509 Result_t OpenWrite(const char*, ui32_t HeaderSize);
510 Result_t SetSourceStream(const VideoDescriptor&);
511 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
516 // Open the file for writing. The file must not exist. Returns error if
517 // the operation cannot be completed.
519 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
521 if ( ! m_State.Test_BEGIN() )
524 Result_t result = m_File.OpenWrite(filename);
526 if ( ASDCP_SUCCESS(result) )
528 m_HeaderSize = HeaderSize;
529 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
530 result = m_State.Goto_INIT();
536 // Automatically sets the MXF file's metadata from the MPEG stream.
538 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
541 if ( ! m_State.Test_INIT() )
545 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
547 if ( ASDCP_SUCCESS(result) )
549 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
550 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
551 result = m_State.Goto_READY();
554 if ( ASDCP_SUCCESS(result) )
556 ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98 ) ? 24 : m_VDesc.EditRate.Numerator;
558 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
559 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
560 m_VDesc.EditRate, TCFrameRate);
566 // Writes a frame of essence to the MXF file. If the optional AESEncContext
567 // argument is present, the essence is encrypted prior to writing.
568 // Fails if the file is not open, is finalized, or an operating system
572 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
575 Result_t result = RESULT_OK;
577 if ( m_State.Test_READY() )
578 result = m_State.Goto_RUNNING(); // first time through, get the body location
580 IndexTableSegment::IndexEntry Entry;
581 Entry.StreamOffset = m_StreamOffset;
583 if ( ASDCP_SUCCESS(result) )
584 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
586 if ( ASDCP_FAILURE(result) )
589 // create mxflib flags
592 switch ( FrameBuf.FrameType() )
594 case FRAME_I: Flags = 0x00; break;
595 case FRAME_P: Flags = 0x22; break;
596 case FRAME_B: Flags = 0x33; break;
599 if ( FrameBuf.GOPStart() )
604 if ( FrameBuf.ClosedGOP() )
608 // update the index manager
609 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
610 Entry.KeyFrameOffset = 0 - m_GOPOffset;
613 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
614 Entry.TemporalOffset, Entry.KeyFrameOffset,
615 m_GOPOffset + Entry.TemporalOffset,
616 Entry.KeyFrameOffset - Entry.TemporalOffset,
619 m_FooterPart.PushIndexEntry(Entry);
627 // Closes the MXF file, writing the index and other closing information.
630 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
632 if ( ! m_State.Test_RUNNING() )
635 m_State.Goto_FINAL();
637 return WriteMXFFooter();
641 //------------------------------------------------------------------------------------------
645 ASDCP::MPEG2::MXFWriter::MXFWriter()
649 ASDCP::MPEG2::MXFWriter::~MXFWriter()
653 // Warning: direct manipulation of MXF structures can interfere
654 // with the normal operation of the wrapper. Caveat emptor!
656 ASDCP::MXF::OPAtomHeader&
657 ASDCP::MPEG2::MXFWriter::OPAtomHeader()
659 if ( m_Writer.empty() )
661 assert(g_OPAtomHeader);
662 return *g_OPAtomHeader;
665 return m_Writer->m_HeaderPart;
668 // Warning: direct manipulation of MXF structures can interfere
669 // with the normal operation of the wrapper. Caveat emptor!
671 ASDCP::MXF::OPAtomIndexFooter&
672 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
674 if ( m_Writer.empty() )
676 assert(g_OPAtomIndexFooter);
677 return *g_OPAtomIndexFooter;
680 return m_Writer->m_FooterPart;
683 // Open the file for writing. The file must not exist. Returns error if
684 // the operation cannot be completed.
686 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
687 const VideoDescriptor& VDesc, ui32_t HeaderSize)
689 if ( Info.LabelSetType == LS_MXF_SMPTE )
690 m_Writer = new h__Writer(DefaultSMPTEDict());
692 m_Writer = new h__Writer(DefaultInteropDict());
694 m_Writer->m_Info = Info;
696 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
698 if ( ASDCP_SUCCESS(result) )
699 result = m_Writer->SetSourceStream(VDesc);
701 if ( ASDCP_FAILURE(result) )
708 // Writes a frame of essence to the MXF file. If the optional AESEncContext
709 // argument is present, the essence is encrypted prior to writing.
710 // Fails if the file is not open, is finalized, or an operating system
713 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
715 if ( m_Writer.empty() )
718 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
721 // Closes the MXF file, writing the index and other closing information.
723 ASDCP::MPEG2::MXFWriter::Finalize()
725 if ( m_Writer.empty() )
728 return m_Writer->Finalize();
733 // end AS_DCP_MPEG2.cpp