2 Copyright (c) 2004-2010, 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) )
466 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
467 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
468 result = m_State.Goto_READY();
471 if ( ASDCP_SUCCESS(result) )
473 ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98 ) ? 24 : m_VDesc.EditRate.Numerator;
475 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
476 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
477 m_VDesc.EditRate, TCFrameRate);
483 // Writes a frame of essence to the MXF file. If the optional AESEncContext
484 // argument is present, the essence is encrypted prior to writing.
485 // Fails if the file is not open, is finalized, or an operating system
489 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
492 Result_t result = RESULT_OK;
494 if ( m_State.Test_READY() )
495 result = m_State.Goto_RUNNING(); // first time through, get the body location
497 IndexTableSegment::IndexEntry Entry;
498 Entry.StreamOffset = m_StreamOffset;
500 if ( ASDCP_SUCCESS(result) )
501 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
503 if ( ASDCP_FAILURE(result) )
506 // create mxflib flags
509 switch ( FrameBuf.FrameType() )
511 case FRAME_I: Flags = 0x00; break;
512 case FRAME_P: Flags = 0x22; break;
513 case FRAME_B: Flags = 0x33; break;
516 if ( FrameBuf.GOPStart() )
521 if ( FrameBuf.ClosedGOP() )
525 // update the index manager
526 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
527 Entry.KeyFrameOffset = 0 - m_GOPOffset;
530 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
531 Entry.TemporalOffset, Entry.KeyFrameOffset,
532 m_GOPOffset + Entry.TemporalOffset,
533 Entry.KeyFrameOffset - Entry.TemporalOffset,
536 m_FooterPart.PushIndexEntry(Entry);
544 // Closes the MXF file, writing the index and other closing information.
547 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
549 if ( ! m_State.Test_RUNNING() )
552 m_State.Goto_FINAL();
554 return WriteMXFFooter();
558 //------------------------------------------------------------------------------------------
562 ASDCP::MPEG2::MXFWriter::MXFWriter()
566 ASDCP::MPEG2::MXFWriter::~MXFWriter()
571 // Open the file for writing. The file must not exist. Returns error if
572 // the operation cannot be completed.
574 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
575 const VideoDescriptor& VDesc, ui32_t HeaderSize)
577 if ( Info.LabelSetType == LS_MXF_SMPTE )
578 m_Writer = new h__Writer(DefaultSMPTEDict());
580 m_Writer = new h__Writer(DefaultInteropDict());
582 m_Writer->m_Info = Info;
584 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
586 if ( ASDCP_SUCCESS(result) )
587 result = m_Writer->SetSourceStream(VDesc);
589 if ( ASDCP_FAILURE(result) )
596 // Writes a frame of essence to the MXF file. If the optional AESEncContext
597 // argument is present, the essence is encrypted prior to writing.
598 // Fails if the file is not open, is finalized, or an operating system
601 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
603 if ( m_Writer.empty() )
606 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
609 // Closes the MXF file, writing the index and other closing information.
611 ASDCP::MPEG2::MXFWriter::Finalize()
613 if ( m_Writer.empty() )
616 return m_Writer->Finalize();
621 // end AS_DCP_MPEG2.cpp