2 Copyright (c) 2004-2009, 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&);
183 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
185 Result_t result = OpenMXFRead(filename);
187 if( ASDCP_SUCCESS(result) )
189 InterchangeObject* Object;
190 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
193 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
197 if( ASDCP_SUCCESS(result) )
198 result = InitMXFIndex();
200 if( ASDCP_SUCCESS(result) )
210 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
211 AESDecContext* Ctx, HMACContext* HMAC)
215 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
217 if ( ASDCP_SUCCESS(result) )
218 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
227 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
231 if ( ! m_File.IsOpen() )
234 // look up frame index node
235 IndexTableSegment::IndexEntry TmpEntry;
237 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
239 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
243 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
252 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
253 AESDecContext* Ctx, HMACContext* HMAC)
256 if ( ! m_File.IsOpen() )
259 Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_MPEG2Essence), Ctx, HMAC);
261 if ( ASDCP_FAILURE(result) )
264 IndexTableSegment::IndexEntry TmpEntry;
265 m_FooterPart.Lookup(FrameNum, TmpEntry);
267 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
269 case 0: FrameBuf.FrameType(FRAME_I); break;
270 case 2: FrameBuf.FrameType(FRAME_P); break;
271 case 3: FrameBuf.FrameType(FRAME_B); break;
272 default: FrameBuf.FrameType(FRAME_U);
275 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
276 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
277 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
282 //------------------------------------------------------------------------------------------
287 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
292 fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
293 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
296 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
301 Kumu::hexdump(m_Data, dump_len, stream);
305 //------------------------------------------------------------------------------------------
307 ASDCP::MPEG2::MXFReader::MXFReader()
309 m_Reader = new h__Reader(DefaultCompositeDict());
313 ASDCP::MPEG2::MXFReader::~MXFReader()
317 // Open the file for reading. The file must exist. Returns error if the
318 // operation cannot be completed.
320 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
322 return m_Reader->OpenRead(filename);
327 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
328 AESDecContext* Ctx, HMACContext* HMAC) const
330 if ( m_Reader && m_Reader->m_File.IsOpen() )
331 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
339 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
340 AESDecContext* Ctx, HMACContext* HMAC) const
342 if ( m_Reader && m_Reader->m_File.IsOpen() )
343 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
351 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
353 if ( m_Reader && m_Reader->m_File.IsOpen() )
354 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
360 // Fill the struct with the values from the file's header.
361 // Returns RESULT_INIT if the file is not open.
363 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
365 if ( m_Reader && m_Reader->m_File.IsOpen() )
367 VDesc = m_Reader->m_VDesc;
375 // Fill the struct with the values from the file's header.
376 // Returns RESULT_INIT if the file is not open.
378 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
380 if ( m_Reader && m_Reader->m_File.IsOpen() )
382 Info = m_Reader->m_Info;
391 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
393 if ( m_Reader->m_File.IsOpen() )
394 m_Reader->m_HeaderPart.Dump(stream);
400 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
402 if ( m_Reader->m_File.IsOpen() )
403 m_Reader->m_FooterPart.Dump(stream);
407 //------------------------------------------------------------------------------------------
410 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
412 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
416 VideoDescriptor m_VDesc;
418 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
420 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_GOPOffset(0) {
421 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
426 Result_t OpenWrite(const char*, ui32_t HeaderSize);
427 Result_t SetSourceStream(const VideoDescriptor&);
428 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
433 // Open the file for writing. The file must not exist. Returns error if
434 // the operation cannot be completed.
436 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
438 if ( ! m_State.Test_BEGIN() )
441 Result_t result = m_File.OpenWrite(filename);
443 if ( ASDCP_SUCCESS(result) )
445 m_HeaderSize = HeaderSize;
446 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
447 result = m_State.Goto_INIT();
453 // Automatically sets the MXF file's metadata from the MPEG stream.
455 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
458 if ( ! m_State.Test_INIT() )
462 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
464 if ( ASDCP_SUCCESS(result) )
465 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
466 PICT_DEF_LABEL, UL(m_Dict->ul(MDD_PictureDataDef)),
467 m_VDesc.EditRate, 24 /* TCFrameRate */);
469 if ( ASDCP_SUCCESS(result) )
471 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
472 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
473 result = m_State.Goto_READY();
479 // Writes a frame of essence to the MXF file. If the optional AESEncContext
480 // argument is present, the essence is encrypted prior to writing.
481 // Fails if the file is not open, is finalized, or an operating system
485 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
488 Result_t result = RESULT_OK;
490 if ( m_State.Test_READY() )
491 result = m_State.Goto_RUNNING(); // first time through, get the body location
493 IndexTableSegment::IndexEntry Entry;
494 Entry.StreamOffset = m_StreamOffset;
496 if ( ASDCP_SUCCESS(result) )
497 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
499 if ( ASDCP_FAILURE(result) )
502 // create mxflib flags
505 switch ( FrameBuf.FrameType() )
507 case FRAME_I: Flags = 0x00; break;
508 case FRAME_P: Flags = 0x22; break;
509 case FRAME_B: Flags = 0x33; break;
512 if ( FrameBuf.GOPStart() )
517 if ( FrameBuf.ClosedGOP() )
521 // update the index manager
522 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
523 Entry.KeyFrameOffset = 0 - m_GOPOffset;
526 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
527 Entry.TemporalOffset, Entry.KeyFrameOffset,
528 m_GOPOffset + Entry.TemporalOffset,
529 Entry.KeyFrameOffset - Entry.TemporalOffset,
532 m_FooterPart.PushIndexEntry(Entry);
540 // Closes the MXF file, writing the index and other closing information.
543 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
545 if ( ! m_State.Test_RUNNING() )
548 m_State.Goto_FINAL();
550 return WriteMXFFooter();
554 //------------------------------------------------------------------------------------------
558 ASDCP::MPEG2::MXFWriter::MXFWriter()
562 ASDCP::MPEG2::MXFWriter::~MXFWriter()
567 // Open the file for writing. The file must not exist. Returns error if
568 // the operation cannot be completed.
570 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
571 const VideoDescriptor& VDesc, ui32_t HeaderSize)
573 if ( Info.LabelSetType == LS_MXF_SMPTE )
574 m_Writer = new h__Writer(DefaultSMPTEDict());
576 m_Writer = new h__Writer(DefaultInteropDict());
578 m_Writer->m_Info = Info;
580 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
582 if ( ASDCP_SUCCESS(result) )
583 result = m_Writer->SetSourceStream(VDesc);
585 if ( ASDCP_FAILURE(result) )
592 // Writes a frame of essence to the MXF file. If the optional AESEncContext
593 // argument is present, the essence is encrypted prior to writing.
594 // Fails if the file is not open, is finalized, or an operating system
597 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
599 if ( m_Writer.empty() )
602 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
605 // Closes the MXF file, writing the index and other closing information.
607 ASDCP::MPEG2::MXFWriter::Finalize()
609 if ( m_Writer.empty() )
612 return m_Writer->Finalize();
617 // end AS_DCP_MPEG2.cpp