2 Copyright (c) 2004-2011, 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__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 // Open the file for reading. The file must exist. Returns error if the
339 // operation cannot be completed.
341 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
343 return m_Reader->OpenRead(filename);
348 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
349 AESDecContext* Ctx, HMACContext* HMAC) const
351 if ( m_Reader && m_Reader->m_File.IsOpen() )
352 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
360 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
361 AESDecContext* Ctx, HMACContext* HMAC) const
363 if ( m_Reader && m_Reader->m_File.IsOpen() )
364 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
372 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
374 if ( m_Reader && m_Reader->m_File.IsOpen() )
375 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
381 // Fill the struct with the values from the file's header.
382 // Returns RESULT_INIT if the file is not open.
384 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
386 if ( m_Reader && m_Reader->m_File.IsOpen() )
388 VDesc = m_Reader->m_VDesc;
396 // Fill the struct with the values from the file's header.
397 // Returns RESULT_INIT if the file is not open.
399 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
401 if ( m_Reader && m_Reader->m_File.IsOpen() )
403 Info = m_Reader->m_Info;
412 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
414 if ( m_Reader->m_File.IsOpen() )
415 m_Reader->m_HeaderPart.Dump(stream);
421 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
423 if ( m_Reader->m_File.IsOpen() )
424 m_Reader->m_FooterPart.Dump(stream);
429 ASDCP::MPEG2::MXFReader::Close() const
431 if ( m_Reader && m_Reader->m_File.IsOpen() )
442 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
447 return m_Reader->FrameType(FrameNum, type);
451 //------------------------------------------------------------------------------------------
454 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
456 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
460 VideoDescriptor m_VDesc;
462 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
464 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_GOPOffset(0) {
465 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
470 Result_t OpenWrite(const char*, ui32_t HeaderSize);
471 Result_t SetSourceStream(const VideoDescriptor&);
472 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
477 // Open the file for writing. The file must not exist. Returns error if
478 // the operation cannot be completed.
480 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
482 if ( ! m_State.Test_BEGIN() )
485 Result_t result = m_File.OpenWrite(filename);
487 if ( ASDCP_SUCCESS(result) )
489 m_HeaderSize = HeaderSize;
490 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
491 result = m_State.Goto_INIT();
497 // Automatically sets the MXF file's metadata from the MPEG stream.
499 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
502 if ( ! m_State.Test_INIT() )
506 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
508 if ( ASDCP_SUCCESS(result) )
510 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
511 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
512 result = m_State.Goto_READY();
515 if ( ASDCP_SUCCESS(result) )
517 ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98 ) ? 24 : m_VDesc.EditRate.Numerator;
519 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
520 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
521 m_VDesc.EditRate, TCFrameRate);
527 // Writes a frame of essence to the MXF file. If the optional AESEncContext
528 // argument is present, the essence is encrypted prior to writing.
529 // Fails if the file is not open, is finalized, or an operating system
533 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
536 Result_t result = RESULT_OK;
538 if ( m_State.Test_READY() )
539 result = m_State.Goto_RUNNING(); // first time through, get the body location
541 IndexTableSegment::IndexEntry Entry;
542 Entry.StreamOffset = m_StreamOffset;
544 if ( ASDCP_SUCCESS(result) )
545 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
547 if ( ASDCP_FAILURE(result) )
550 // create mxflib flags
553 switch ( FrameBuf.FrameType() )
555 case FRAME_I: Flags = 0x00; break;
556 case FRAME_P: Flags = 0x22; break;
557 case FRAME_B: Flags = 0x33; break;
560 if ( FrameBuf.GOPStart() )
565 if ( FrameBuf.ClosedGOP() )
569 // update the index manager
570 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
571 Entry.KeyFrameOffset = 0 - m_GOPOffset;
574 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
575 Entry.TemporalOffset, Entry.KeyFrameOffset,
576 m_GOPOffset + Entry.TemporalOffset,
577 Entry.KeyFrameOffset - Entry.TemporalOffset,
580 m_FooterPart.PushIndexEntry(Entry);
588 // Closes the MXF file, writing the index and other closing information.
591 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
593 if ( ! m_State.Test_RUNNING() )
596 m_State.Goto_FINAL();
598 return WriteMXFFooter();
602 //------------------------------------------------------------------------------------------
606 ASDCP::MPEG2::MXFWriter::MXFWriter()
610 ASDCP::MPEG2::MXFWriter::~MXFWriter()
615 // Open the file for writing. The file must not exist. Returns error if
616 // the operation cannot be completed.
618 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
619 const VideoDescriptor& VDesc, ui32_t HeaderSize)
621 if ( Info.LabelSetType == LS_MXF_SMPTE )
622 m_Writer = new h__Writer(DefaultSMPTEDict());
624 m_Writer = new h__Writer(DefaultInteropDict());
626 m_Writer->m_Info = Info;
628 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
630 if ( ASDCP_SUCCESS(result) )
631 result = m_Writer->SetSourceStream(VDesc);
633 if ( ASDCP_FAILURE(result) )
640 // Writes a frame of essence to the MXF file. If the optional AESEncContext
641 // argument is present, the essence is encrypted prior to writing.
642 // Fails if the file is not open, is finalized, or an operating system
645 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
647 if ( m_Writer.empty() )
650 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
653 // Closes the MXF file, writing the index and other closing information.
655 ASDCP::MPEG2::MXFWriter::Finalize()
657 if ( m_Writer.empty() )
660 return m_Writer->Finalize();
665 // end AS_DCP_MPEG2.cpp