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 const ASDCP::Dictionary *sg_dict = &DefaultSMPTEDict();
40 static MXF::OPAtomHeader sg_OPAtomHeader(sg_dict);
41 static MXF::OPAtomIndexFooter sg_OPAtomIndexFooter(sg_dict);
43 static std::string MPEG_PACKAGE_LABEL = "File Package: SMPTE 381M frame wrapping of MPEG2 video elementary stream";
44 static std::string PICT_DEF_LABEL = "Picture Track";
48 MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc)
50 ASDCP_TEST_NULL(VDescObj);
52 VDesc.SampleRate = VDescObj->SampleRate;
53 VDesc.EditRate = VDescObj->SampleRate;
54 VDesc.FrameRate = VDescObj->SampleRate.Numerator;
55 assert(VDescObj->ContainerDuration <= 0xFFFFFFFFL);
56 VDesc.ContainerDuration = (ui32_t) VDescObj->ContainerDuration;
58 VDesc.FrameLayout = VDescObj->FrameLayout;
59 VDesc.StoredWidth = VDescObj->StoredWidth;
60 VDesc.StoredHeight = VDescObj->StoredHeight;
61 VDesc.AspectRatio = VDescObj->AspectRatio;
63 VDesc.ComponentDepth = VDescObj->ComponentDepth;
64 VDesc.HorizontalSubsampling = VDescObj->HorizontalSubsampling;
65 VDesc.VerticalSubsampling = VDescObj->VerticalSubsampling;
66 VDesc.ColorSiting = VDescObj->ColorSiting;
67 VDesc.CodedContentType = VDescObj->CodedContentType;
69 VDesc.LowDelay = VDescObj->LowDelay == 0 ? false : true;
70 VDesc.BitRate = VDescObj->BitRate;
71 VDesc.ProfileAndLevel = VDescObj->ProfileAndLevel;
78 MPEG2_VDesc_to_MD(MPEG2::VideoDescriptor& VDesc, MXF::MPEG2VideoDescriptor* VDescObj)
80 ASDCP_TEST_NULL(VDescObj);
82 VDescObj->SampleRate = VDesc.SampleRate;
83 VDescObj->ContainerDuration = VDesc.ContainerDuration;
85 VDescObj->FrameLayout = VDesc.FrameLayout;
86 VDescObj->StoredWidth = VDesc.StoredWidth;
87 VDescObj->StoredHeight = VDesc.StoredHeight;
88 VDescObj->AspectRatio = VDesc.AspectRatio;
90 VDescObj->ComponentDepth = VDesc.ComponentDepth;
91 VDescObj->HorizontalSubsampling = VDesc.HorizontalSubsampling;
92 VDescObj->VerticalSubsampling = VDesc.VerticalSubsampling;
93 VDescObj->ColorSiting = VDesc.ColorSiting;
94 VDescObj->CodedContentType = VDesc.CodedContentType;
96 VDescObj->LowDelay = VDesc.LowDelay ? 1 : 0;
97 VDescObj->BitRate = VDesc.BitRate;
98 VDescObj->ProfileAndLevel = VDesc.ProfileAndLevel;
104 ASDCP::MPEG2::operator << (std::ostream& strm, const VideoDescriptor& VDesc)
106 strm << " SampleRate: " << VDesc.SampleRate.Numerator << "/" << VDesc.SampleRate.Denominator << std::endl;
107 strm << " FrameLayout: " << (unsigned) VDesc.FrameLayout << std::endl;
108 strm << " StoredWidth: " << (unsigned) VDesc.StoredWidth << std::endl;
109 strm << " StoredHeight: " << (unsigned) VDesc.StoredHeight << std::endl;
110 strm << " AspectRatio: " << VDesc.AspectRatio.Numerator << "/" << VDesc.AspectRatio.Denominator << std::endl;
111 strm << " ComponentDepth: " << (unsigned) VDesc.ComponentDepth << std::endl;
112 strm << " HorizontalSubsmpl: " << (unsigned) VDesc.HorizontalSubsampling << std::endl;
113 strm << " VerticalSubsmpl: " << (unsigned) VDesc.VerticalSubsampling << std::endl;
114 strm << " ColorSiting: " << (unsigned) VDesc.ColorSiting << std::endl;
115 strm << " CodedContentType: " << (unsigned) VDesc.CodedContentType << std::endl;
116 strm << " LowDelay: " << (unsigned) VDesc.LowDelay << std::endl;
117 strm << " BitRate: " << (unsigned) VDesc.BitRate << std::endl;
118 strm << " ProfileAndLevel: " << (unsigned) VDesc.ProfileAndLevel << std::endl;
119 strm << " ContainerDuration: " << (unsigned) VDesc.ContainerDuration << std::endl;
126 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
136 AspectRatio: %d/%d\n\
137 ComponentDepth: %u\n\
138 HorizontalSubsmpl: %u\n\
139 VerticalSubsmpl: %u\n\
141 CodedContentType: %u\n\
144 ProfileAndLevel: %u\n\
145 ContainerDuration: %u\n",
146 VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
150 VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
151 VDesc.ComponentDepth,
152 VDesc.HorizontalSubsampling,
153 VDesc.VerticalSubsampling,
155 VDesc.CodedContentType,
158 VDesc.ProfileAndLevel,
159 VDesc.ContainerDuration
163 //------------------------------------------------------------------------------------------
165 // hidden, internal implementation of MPEG2 reader
167 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
169 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
173 VideoDescriptor m_VDesc; // video parameter list
175 h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
177 Result_t OpenRead(const char*);
178 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
179 Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
180 Result_t FindFrameGOPStart(ui32_t, ui32_t&);
181 Result_t FrameType(ui32_t FrameNum, FrameType_t& type);
188 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
190 Result_t result = OpenMXFRead(filename);
192 if( ASDCP_SUCCESS(result) )
194 InterchangeObject* Object;
195 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
198 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
202 if( ASDCP_SUCCESS(result) )
203 result = InitMXFIndex();
205 if( ASDCP_SUCCESS(result) )
215 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
216 AESDecContext* Ctx, HMACContext* HMAC)
220 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
222 if ( ASDCP_SUCCESS(result) )
223 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
232 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
236 if ( ! m_File.IsOpen() )
239 // look up frame index node
240 IndexTableSegment::IndexEntry TmpEntry;
242 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
244 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
248 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
255 ASDCP::MPEG2::MXFReader::h__Reader::FrameType(ui32_t FrameNum, FrameType_t& type)
257 if ( ! m_File.IsOpen() )
260 // look up frame index node
261 IndexTableSegment::IndexEntry TmpEntry;
263 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
265 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
269 type = ( (TmpEntry.Flags & 0x0f) == 3 ) ? FRAME_B : ( (TmpEntry.Flags & 0x0f) == 2 ) ? FRAME_P : FRAME_I;
277 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
278 AESDecContext* Ctx, HMACContext* HMAC)
281 if ( ! m_File.IsOpen() )
284 Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_MPEG2Essence), Ctx, HMAC);
286 if ( ASDCP_FAILURE(result) )
289 IndexTableSegment::IndexEntry TmpEntry;
290 m_FooterPart.Lookup(FrameNum, TmpEntry);
292 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
294 case 0: FrameBuf.FrameType(FRAME_I); break;
295 case 2: FrameBuf.FrameType(FRAME_P); break;
296 case 3: FrameBuf.FrameType(FRAME_B); break;
297 default: FrameBuf.FrameType(FRAME_U);
300 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
301 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
302 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
307 //------------------------------------------------------------------------------------------
312 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
317 fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
318 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
321 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
326 Kumu::hexdump(m_Data, dump_len, stream);
330 //------------------------------------------------------------------------------------------
332 ASDCP::MPEG2::MXFReader::MXFReader()
334 m_Reader = new h__Reader(DefaultCompositeDict());
338 ASDCP::MPEG2::MXFReader::~MXFReader()
342 // Warning: direct manipulation of MXF structures can interfere
343 // with the normal operation of the wrapper. Caveat emptor!
345 ASDCP::MXF::OPAtomHeader&
346 ASDCP::MPEG2::MXFReader::OPAtomHeader()
348 if ( m_Reader.empty() )
349 return sg_OPAtomHeader;
351 return m_Reader->m_HeaderPart;
354 // Warning: direct manipulation of MXF structures can interfere
355 // with the normal operation of the wrapper. Caveat emptor!
357 ASDCP::MXF::OPAtomIndexFooter&
358 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
360 if ( m_Reader.empty() )
361 return sg_OPAtomIndexFooter;
363 return m_Reader->m_FooterPart;
366 // Open the file for reading. The file must exist. Returns error if the
367 // operation cannot be completed.
369 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
371 return m_Reader->OpenRead(filename);
376 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
377 AESDecContext* Ctx, HMACContext* HMAC) const
379 if ( m_Reader && m_Reader->m_File.IsOpen() )
380 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
388 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
389 AESDecContext* Ctx, HMACContext* HMAC) const
391 if ( m_Reader && m_Reader->m_File.IsOpen() )
392 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
400 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
402 if ( m_Reader && m_Reader->m_File.IsOpen() )
403 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
409 // Fill the struct with the values from the file's header.
410 // Returns RESULT_INIT if the file is not open.
412 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
414 if ( m_Reader && m_Reader->m_File.IsOpen() )
416 VDesc = m_Reader->m_VDesc;
424 // Fill the struct with the values from the file's header.
425 // Returns RESULT_INIT if the file is not open.
427 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
429 if ( m_Reader && m_Reader->m_File.IsOpen() )
431 Info = m_Reader->m_Info;
440 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
442 if ( m_Reader->m_File.IsOpen() )
443 m_Reader->m_HeaderPart.Dump(stream);
449 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
451 if ( m_Reader->m_File.IsOpen() )
452 m_Reader->m_FooterPart.Dump(stream);
457 ASDCP::MPEG2::MXFReader::Close() const
459 if ( m_Reader && m_Reader->m_File.IsOpen() )
470 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
475 return m_Reader->FrameType(FrameNum, type);
479 //------------------------------------------------------------------------------------------
482 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
484 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
488 VideoDescriptor m_VDesc;
490 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
492 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_GOPOffset(0) {
493 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
498 Result_t OpenWrite(const char*, ui32_t HeaderSize);
499 Result_t SetSourceStream(const VideoDescriptor&);
500 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
505 // Open the file for writing. The file must not exist. Returns error if
506 // the operation cannot be completed.
508 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
510 if ( ! m_State.Test_BEGIN() )
513 Result_t result = m_File.OpenWrite(filename);
515 if ( ASDCP_SUCCESS(result) )
517 m_HeaderSize = HeaderSize;
518 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
519 result = m_State.Goto_INIT();
525 // Automatically sets the MXF file's metadata from the MPEG stream.
527 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
530 if ( ! m_State.Test_INIT() )
534 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
536 if ( ASDCP_SUCCESS(result) )
538 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
539 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
540 result = m_State.Goto_READY();
543 if ( ASDCP_SUCCESS(result) )
545 ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98 ) ? 24 : m_VDesc.EditRate.Numerator;
547 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
548 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
549 m_VDesc.EditRate, TCFrameRate);
555 // Writes a frame of essence to the MXF file. If the optional AESEncContext
556 // argument is present, the essence is encrypted prior to writing.
557 // Fails if the file is not open, is finalized, or an operating system
561 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
564 Result_t result = RESULT_OK;
566 if ( m_State.Test_READY() )
567 result = m_State.Goto_RUNNING(); // first time through, get the body location
569 IndexTableSegment::IndexEntry Entry;
570 Entry.StreamOffset = m_StreamOffset;
572 if ( ASDCP_SUCCESS(result) )
573 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
575 if ( ASDCP_FAILURE(result) )
578 // create mxflib flags
581 switch ( FrameBuf.FrameType() )
583 case FRAME_I: Flags = 0x00; break;
584 case FRAME_P: Flags = 0x22; break;
585 case FRAME_B: Flags = 0x33; break;
588 if ( FrameBuf.GOPStart() )
593 if ( FrameBuf.ClosedGOP() )
597 // update the index manager
598 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
599 Entry.KeyFrameOffset = 0 - m_GOPOffset;
602 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
603 Entry.TemporalOffset, Entry.KeyFrameOffset,
604 m_GOPOffset + Entry.TemporalOffset,
605 Entry.KeyFrameOffset - Entry.TemporalOffset,
608 m_FooterPart.PushIndexEntry(Entry);
616 // Closes the MXF file, writing the index and other closing information.
619 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
621 if ( ! m_State.Test_RUNNING() )
624 m_State.Goto_FINAL();
626 return WriteMXFFooter();
630 //------------------------------------------------------------------------------------------
634 ASDCP::MPEG2::MXFWriter::MXFWriter()
638 ASDCP::MPEG2::MXFWriter::~MXFWriter()
642 // Warning: direct manipulation of MXF structures can interfere
643 // with the normal operation of the wrapper. Caveat emptor!
645 ASDCP::MXF::OPAtomHeader&
646 ASDCP::MPEG2::MXFWriter::OPAtomHeader()
648 if ( m_Writer.empty() )
649 return sg_OPAtomHeader;
651 return m_Writer->m_HeaderPart;
654 // Warning: direct manipulation of MXF structures can interfere
655 // with the normal operation of the wrapper. Caveat emptor!
657 ASDCP::MXF::OPAtomIndexFooter&
658 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
660 if ( m_Writer.empty() )
661 return sg_OPAtomIndexFooter;
663 return m_Writer->m_FooterPart;
666 // Open the file for writing. The file must not exist. Returns error if
667 // the operation cannot be completed.
669 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
670 const VideoDescriptor& VDesc, ui32_t HeaderSize)
672 if ( Info.LabelSetType == LS_MXF_SMPTE )
673 m_Writer = new h__Writer(DefaultSMPTEDict());
675 m_Writer = new h__Writer(DefaultInteropDict());
677 m_Writer->m_Info = Info;
679 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
681 if ( ASDCP_SUCCESS(result) )
682 result = m_Writer->SetSourceStream(VDesc);
684 if ( ASDCP_FAILURE(result) )
691 // Writes a frame of essence to the MXF file. If the optional AESEncContext
692 // argument is present, the essence is encrypted prior to writing.
693 // Fails if the file is not open, is finalized, or an operating system
696 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
698 if ( m_Writer.empty() )
701 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
704 // Closes the MXF file, writing the index and other closing information.
706 ASDCP::MPEG2::MXFWriter::Finalize()
708 if ( m_Writer.empty() )
711 return m_Writer->Finalize();
716 // end AS_DCP_MPEG2.cpp