2 Copyright (c) 2004-2007, 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);
168 VideoDescriptor m_VDesc; // video parameter list
172 Result_t OpenRead(const char*);
173 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
174 Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
175 Result_t FindFrameGOPStart(ui32_t, ui32_t&);
182 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
184 Result_t result = OpenMXFRead(filename);
186 if( ASDCP_SUCCESS(result) )
188 InterchangeObject* Object;
189 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
192 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
196 if( ASDCP_SUCCESS(result) )
197 result = InitMXFIndex();
199 if( ASDCP_SUCCESS(result) )
209 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
210 AESDecContext* Ctx, HMACContext* HMAC)
214 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
216 if ( ASDCP_SUCCESS(result) )
217 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
226 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
230 if ( ! m_File.IsOpen() )
233 // look up frame index node
234 IndexTableSegment::IndexEntry TmpEntry;
236 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
238 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
242 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
251 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
252 AESDecContext* Ctx, HMACContext* HMAC)
254 if ( ! m_File.IsOpen() )
257 Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, Dict::ul(MDD_MPEG2Essence), Ctx, HMAC);
259 if ( ASDCP_FAILURE(result) )
262 IndexTableSegment::IndexEntry TmpEntry;
263 m_FooterPart.Lookup(FrameNum, TmpEntry);
265 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
267 case 0: FrameBuf.FrameType(FRAME_I); break;
268 case 2: FrameBuf.FrameType(FRAME_P); break;
269 case 3: FrameBuf.FrameType(FRAME_B); break;
270 default: FrameBuf.FrameType(FRAME_U);
273 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
274 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
275 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
280 //------------------------------------------------------------------------------------------
285 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
290 fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
291 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
294 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
299 Kumu::hexdump(m_Data, dump_len, stream);
303 //------------------------------------------------------------------------------------------
305 ASDCP::MPEG2::MXFReader::MXFReader()
307 m_Reader = new h__Reader;
311 ASDCP::MPEG2::MXFReader::~MXFReader()
315 // Open the file for reading. The file must exist. Returns error if the
316 // operation cannot be completed.
318 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
320 return m_Reader->OpenRead(filename);
325 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
326 AESDecContext* Ctx, HMACContext* HMAC) const
328 if ( m_Reader && m_Reader->m_File.IsOpen() )
329 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
337 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
338 AESDecContext* Ctx, HMACContext* HMAC) const
340 if ( m_Reader && m_Reader->m_File.IsOpen() )
341 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
349 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
351 if ( m_Reader && m_Reader->m_File.IsOpen() )
352 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
358 // Fill the struct with the values from the file's header.
359 // Returns RESULT_INIT if the file is not open.
361 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
363 if ( m_Reader && m_Reader->m_File.IsOpen() )
365 VDesc = m_Reader->m_VDesc;
373 // Fill the struct with the values from the file's header.
374 // Returns RESULT_INIT if the file is not open.
376 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
378 if ( m_Reader && m_Reader->m_File.IsOpen() )
380 Info = m_Reader->m_Info;
389 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
391 if ( m_Reader->m_File.IsOpen() )
392 m_Reader->m_HeaderPart.Dump(stream);
398 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
400 if ( m_Reader->m_File.IsOpen() )
401 m_Reader->m_FooterPart.Dump(stream);
405 //------------------------------------------------------------------------------------------
408 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
411 VideoDescriptor m_VDesc;
413 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
415 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
417 h__Writer() : m_GOPOffset(0) {
418 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
423 Result_t OpenWrite(const char*, ui32_t HeaderSize);
424 Result_t SetSourceStream(const VideoDescriptor&);
425 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
430 // Open the file for writing. The file must not exist. Returns error if
431 // the operation cannot be completed.
433 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
435 if ( ! m_State.Test_BEGIN() )
438 Result_t result = m_File.OpenWrite(filename);
440 if ( ASDCP_SUCCESS(result) )
442 m_HeaderSize = HeaderSize;
443 m_EssenceDescriptor = new MPEG2VideoDescriptor;
444 result = m_State.Goto_INIT();
450 // Automatically sets the MXF file's metadata from the MPEG stream.
452 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
454 if ( ! m_State.Test_INIT() )
458 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
460 if ( ASDCP_SUCCESS(result) )
461 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(Dict::ul(MDD_MPEG2_VESWrapping)),
462 PICT_DEF_LABEL, UL(Dict::ul(MDD_PictureDataDef)),
463 m_VDesc.EditRate, 24 /* TCFrameRate */);
465 if ( ASDCP_SUCCESS(result) )
467 memcpy(m_EssenceUL, Dict::ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
468 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
469 result = m_State.Goto_READY();
475 // Writes a frame of essence to the MXF file. If the optional AESEncContext
476 // argument is present, the essence is encrypted prior to writing.
477 // Fails if the file is not open, is finalized, or an operating system
481 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
484 Result_t result = RESULT_OK;
486 if ( m_State.Test_READY() )
487 result = m_State.Goto_RUNNING(); // first time through, get the body location
489 IndexTableSegment::IndexEntry Entry;
490 Entry.StreamOffset = m_StreamOffset;
492 if ( ASDCP_SUCCESS(result) )
493 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
495 if ( ASDCP_FAILURE(result) )
498 // create mxflib flags
501 switch ( FrameBuf.FrameType() )
503 case FRAME_I: Flags = 0x00; break;
504 case FRAME_P: Flags = 0x22; break;
505 case FRAME_B: Flags = 0x33; break;
508 if ( FrameBuf.GOPStart() )
513 if ( FrameBuf.ClosedGOP() )
517 // update the index manager
518 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
519 Entry.KeyFrameOffset = 0 - m_GOPOffset;
522 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
523 Entry.TemporalOffset, Entry.KeyFrameOffset,
524 m_GOPOffset + Entry.TemporalOffset,
525 Entry.KeyFrameOffset - Entry.TemporalOffset,
528 m_FooterPart.PushIndexEntry(Entry);
536 // Closes the MXF file, writing the index and other closing information.
539 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
541 if ( ! m_State.Test_RUNNING() )
544 m_State.Goto_FINAL();
546 return WriteMXFFooter();
550 //------------------------------------------------------------------------------------------
554 ASDCP::MPEG2::MXFWriter::MXFWriter()
558 ASDCP::MPEG2::MXFWriter::~MXFWriter()
563 // Open the file for writing. The file must not exist. Returns error if
564 // the operation cannot be completed.
566 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
567 const VideoDescriptor& VDesc, ui32_t HeaderSize)
569 m_Writer = new h__Writer;
570 m_Writer->m_Info = Info;
572 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
574 if ( ASDCP_SUCCESS(result) )
575 result = m_Writer->SetSourceStream(VDesc);
577 if ( ASDCP_FAILURE(result) )
584 // Writes a frame of essence to the MXF file. If the optional AESEncContext
585 // argument is present, the essence is encrypted prior to writing.
586 // Fails if the file is not open, is finalized, or an operating system
589 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
591 if ( m_Writer.empty() )
594 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
597 // Closes the MXF file, writing the index and other closing information.
599 ASDCP::MPEG2::MXFWriter::Finalize()
601 if ( m_Writer.empty() )
604 return m_Writer->Finalize();
609 // end AS_DCP_MPEG2.cpp