2 Copyright (c) 2004-2013, 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) {}
172 virtual ~h__Reader() {}
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);
205 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
206 AESDecContext* Ctx, HMACContext* HMAC)
210 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
212 if ( ASDCP_SUCCESS(result) )
213 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
222 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
226 if ( ! m_File.IsOpen() )
229 // look up frame index node
230 IndexTableSegment::IndexEntry TmpEntry;
232 if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
234 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
238 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
245 ASDCP::MPEG2::MXFReader::h__Reader::FrameType(ui32_t FrameNum, FrameType_t& type)
247 if ( ! m_File.IsOpen() )
250 // look up frame index node
251 IndexTableSegment::IndexEntry TmpEntry;
253 if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
255 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
259 type = ( (TmpEntry.Flags & 0x0f) == 3 ) ? FRAME_B : ( (TmpEntry.Flags & 0x0f) == 2 ) ? FRAME_P : FRAME_I;
267 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
268 AESDecContext* Ctx, HMACContext* HMAC)
271 if ( ! m_File.IsOpen() )
274 Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_MPEG2Essence), Ctx, HMAC);
276 if ( ASDCP_FAILURE(result) )
279 IndexTableSegment::IndexEntry TmpEntry;
280 m_IndexAccess.Lookup(FrameNum, TmpEntry);
282 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
284 case 0: FrameBuf.FrameType(FRAME_I); break;
285 case 2: FrameBuf.FrameType(FRAME_P); break;
286 case 3: FrameBuf.FrameType(FRAME_B); break;
287 default: FrameBuf.FrameType(FRAME_U);
290 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
291 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
292 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
297 //------------------------------------------------------------------------------------------
302 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
307 fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
308 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
311 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
316 Kumu::hexdump(m_Data, dump_len, stream);
320 //------------------------------------------------------------------------------------------
322 ASDCP::MPEG2::MXFReader::MXFReader()
324 m_Reader = new h__Reader(DefaultCompositeDict());
328 ASDCP::MPEG2::MXFReader::~MXFReader()
330 if ( m_Reader && m_Reader->m_File.IsOpen() )
334 // Warning: direct manipulation of MXF structures can interfere
335 // with the normal operation of the wrapper. Caveat emptor!
337 ASDCP::MXF::OP1aHeader&
338 ASDCP::MPEG2::MXFReader::OP1aHeader()
340 if ( m_Reader.empty() )
342 assert(g_OP1aHeader);
343 return *g_OP1aHeader;
346 return m_Reader->m_HeaderPart;
349 // Warning: direct manipulation of MXF structures can interfere
350 // with the normal operation of the wrapper. Caveat emptor!
352 ASDCP::MXF::OPAtomIndexFooter&
353 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
355 if ( m_Reader.empty() )
357 assert(g_OPAtomIndexFooter);
358 return *g_OPAtomIndexFooter;
361 return m_Reader->m_IndexAccess;
364 // Warning: direct manipulation of MXF structures can interfere
365 // with the normal operation of the wrapper. Caveat emptor!
368 ASDCP::MPEG2::MXFReader::RIP()
370 if ( m_Reader.empty() )
376 return m_Reader->m_RIP;
379 // Open the file for reading. The file must exist. Returns error if the
380 // operation cannot be completed.
382 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
384 return m_Reader->OpenRead(filename);
389 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
390 AESDecContext* Ctx, HMACContext* HMAC) const
392 if ( m_Reader && m_Reader->m_File.IsOpen() )
393 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
401 ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
403 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
408 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
409 AESDecContext* Ctx, HMACContext* HMAC) const
411 if ( m_Reader && m_Reader->m_File.IsOpen() )
412 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
420 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
422 if ( m_Reader && m_Reader->m_File.IsOpen() )
423 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
429 // Fill the struct with the values from the file's header.
430 // Returns RESULT_INIT if the file is not open.
432 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
434 if ( m_Reader && m_Reader->m_File.IsOpen() )
436 VDesc = m_Reader->m_VDesc;
444 // Fill the struct with the values from the file's header.
445 // Returns RESULT_INIT if the file is not open.
447 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
449 if ( m_Reader && m_Reader->m_File.IsOpen() )
451 Info = m_Reader->m_Info;
460 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
462 if ( m_Reader->m_File.IsOpen() )
463 m_Reader->m_HeaderPart.Dump(stream);
469 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
471 if ( m_Reader->m_File.IsOpen() )
472 m_Reader->m_IndexAccess.Dump(stream);
477 ASDCP::MPEG2::MXFReader::Close() const
479 if ( m_Reader && m_Reader->m_File.IsOpen() )
490 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
495 return m_Reader->FrameType(FrameNum, type);
499 //------------------------------------------------------------------------------------------
502 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
504 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
508 VideoDescriptor m_VDesc;
510 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
512 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_GOPOffset(0) {
513 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
516 virtual ~h__Writer(){}
518 Result_t OpenWrite(const char*, ui32_t HeaderSize);
519 Result_t SetSourceStream(const VideoDescriptor&);
520 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
525 // Open the file for writing. The file must not exist. Returns error if
526 // the operation cannot be completed.
528 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
530 if ( ! m_State.Test_BEGIN() )
533 Result_t result = m_File.OpenWrite(filename);
535 if ( ASDCP_SUCCESS(result) )
537 m_HeaderSize = HeaderSize;
538 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
539 result = m_State.Goto_INIT();
545 // Automatically sets the MXF file's metadata from the MPEG stream.
547 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
550 if ( ! m_State.Test_INIT() )
554 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
556 if ( ASDCP_SUCCESS(result) )
558 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
559 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
560 result = m_State.Goto_READY();
563 if ( ASDCP_SUCCESS(result) )
565 ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98 ) ? 24 : m_VDesc.EditRate.Numerator;
567 result = WriteASDCPHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
568 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
569 m_VDesc.EditRate, TCFrameRate);
575 // Writes a frame of essence to the MXF file. If the optional AESEncContext
576 // argument is present, the essence is encrypted prior to writing.
577 // Fails if the file is not open, is finalized, or an operating system
581 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
584 Result_t result = RESULT_OK;
586 if ( m_State.Test_READY() )
587 result = m_State.Goto_RUNNING(); // first time through, get the body location
589 IndexTableSegment::IndexEntry Entry;
590 Entry.StreamOffset = m_StreamOffset;
592 if ( ASDCP_SUCCESS(result) )
593 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
595 if ( ASDCP_FAILURE(result) )
598 // create mxflib flags
601 switch ( FrameBuf.FrameType() )
603 case FRAME_I: Flags = 0x00; break;
604 case FRAME_P: Flags = 0x22; break;
605 case FRAME_B: Flags = 0x33; break;
608 if ( FrameBuf.GOPStart() )
613 if ( FrameBuf.ClosedGOP() )
617 // update the index manager
618 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
619 Entry.KeyFrameOffset = 0 - m_GOPOffset;
622 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
623 Entry.TemporalOffset, Entry.KeyFrameOffset,
624 m_GOPOffset + Entry.TemporalOffset,
625 Entry.KeyFrameOffset - Entry.TemporalOffset,
628 m_FooterPart.PushIndexEntry(Entry);
636 // Closes the MXF file, writing the index and other closing information.
639 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
641 if ( ! m_State.Test_RUNNING() )
644 m_State.Goto_FINAL();
646 return WriteASDCPFooter();
650 //------------------------------------------------------------------------------------------
654 ASDCP::MPEG2::MXFWriter::MXFWriter()
658 ASDCP::MPEG2::MXFWriter::~MXFWriter()
662 // Warning: direct manipulation of MXF structures can interfere
663 // with the normal operation of the wrapper. Caveat emptor!
665 ASDCP::MXF::OP1aHeader&
666 ASDCP::MPEG2::MXFWriter::OP1aHeader()
668 if ( m_Writer.empty() )
670 assert(g_OP1aHeader);
671 return *g_OP1aHeader;
674 return m_Writer->m_HeaderPart;
677 // Warning: direct manipulation of MXF structures can interfere
678 // with the normal operation of the wrapper. Caveat emptor!
680 ASDCP::MXF::OPAtomIndexFooter&
681 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
683 if ( m_Writer.empty() )
685 assert(g_OPAtomIndexFooter);
686 return *g_OPAtomIndexFooter;
689 return m_Writer->m_FooterPart;
692 // Warning: direct manipulation of MXF structures can interfere
693 // with the normal operation of the wrapper. Caveat emptor!
696 ASDCP::MPEG2::MXFWriter::RIP()
698 if ( m_Writer.empty() )
704 return m_Writer->m_RIP;
707 // Open the file for writing. The file must not exist. Returns error if
708 // the operation cannot be completed.
710 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
711 const VideoDescriptor& VDesc, ui32_t HeaderSize)
713 if ( Info.LabelSetType == LS_MXF_SMPTE )
714 m_Writer = new h__Writer(DefaultSMPTEDict());
716 m_Writer = new h__Writer(DefaultInteropDict());
718 m_Writer->m_Info = Info;
720 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
722 if ( ASDCP_SUCCESS(result) )
723 result = m_Writer->SetSourceStream(VDesc);
725 if ( ASDCP_FAILURE(result) )
732 // Writes a frame of essence to the MXF file. If the optional AESEncContext
733 // argument is present, the essence is encrypted prior to writing.
734 // Fails if the file is not open, is finalized, or an operating system
737 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
739 if ( m_Writer.empty() )
742 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
745 // Closes the MXF file, writing the index and other closing information.
747 ASDCP::MPEG2::MXFWriter::Finalize()
749 if ( m_Writer.empty() )
752 return m_Writer->Finalize();
757 // end AS_DCP_MPEG2.cpp