2 Copyright (c) 2004-2013, 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.get() == 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__ASDCPReader
165 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
169 VideoDescriptor m_VDesc; // video parameter list
171 h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d) {}
172 virtual ~h__Reader() {}
173 Result_t OpenRead(const std::string&);
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 std::string& filename)
186 Result_t result = OpenMXFRead(filename);
188 if( ASDCP_SUCCESS(result) )
190 InterchangeObject* Object = 0;
192 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
196 DefaultLogSink().Error("MPEG2VideoDescriptor object not found.\n");
197 return RESULT_FORMAT;
200 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
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_IndexAccess.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_IndexAccess.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_IndexAccess.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()
336 if ( m_Reader && m_Reader->m_File.IsOpen() )
340 // Warning: direct manipulation of MXF structures can interfere
341 // with the normal operation of the wrapper. Caveat emptor!
343 ASDCP::MXF::OP1aHeader&
344 ASDCP::MPEG2::MXFReader::OP1aHeader()
346 if ( m_Reader.empty() )
348 assert(g_OP1aHeader);
349 return *g_OP1aHeader;
352 return m_Reader->m_HeaderPart;
355 // Warning: direct manipulation of MXF structures can interfere
356 // with the normal operation of the wrapper. Caveat emptor!
358 ASDCP::MXF::OPAtomIndexFooter&
359 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
361 if ( m_Reader.empty() )
363 assert(g_OPAtomIndexFooter);
364 return *g_OPAtomIndexFooter;
367 return m_Reader->m_IndexAccess;
370 // Warning: direct manipulation of MXF structures can interfere
371 // with the normal operation of the wrapper. Caveat emptor!
374 ASDCP::MPEG2::MXFReader::RIP()
376 if ( m_Reader.empty() )
382 return m_Reader->m_RIP;
385 // Open the file for reading. The file must exist. Returns error if the
386 // operation cannot be completed.
388 ASDCP::MPEG2::MXFReader::OpenRead(const std::string& filename) const
390 return m_Reader->OpenRead(filename);
395 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
396 AESDecContext* Ctx, HMACContext* HMAC) const
398 if ( m_Reader && m_Reader->m_File.IsOpen() )
399 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
407 ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
409 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
414 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
415 AESDecContext* Ctx, HMACContext* HMAC) const
417 if ( m_Reader && m_Reader->m_File.IsOpen() )
418 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
426 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
428 if ( m_Reader && m_Reader->m_File.IsOpen() )
429 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
435 // Fill the struct with the values from the file's header.
436 // Returns RESULT_INIT if the file is not open.
438 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
440 if ( m_Reader && m_Reader->m_File.IsOpen() )
442 VDesc = m_Reader->m_VDesc;
450 // Fill the struct with the values from the file's header.
451 // Returns RESULT_INIT if the file is not open.
453 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
455 if ( m_Reader && m_Reader->m_File.IsOpen() )
457 Info = m_Reader->m_Info;
466 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
468 if ( m_Reader->m_File.IsOpen() )
469 m_Reader->m_HeaderPart.Dump(stream);
475 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
477 if ( m_Reader->m_File.IsOpen() )
478 m_Reader->m_IndexAccess.Dump(stream);
483 ASDCP::MPEG2::MXFReader::Close() const
485 if ( m_Reader && m_Reader->m_File.IsOpen() )
496 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
501 return m_Reader->FrameType(FrameNum, type);
505 //------------------------------------------------------------------------------------------
508 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
510 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
514 VideoDescriptor m_VDesc;
516 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
518 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_GOPOffset(0) {
519 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
522 virtual ~h__Writer(){}
524 Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
525 Result_t SetSourceStream(const VideoDescriptor&);
526 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
531 // Open the file for writing. The file must not exist. Returns error if
532 // the operation cannot be completed.
534 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
536 if ( ! m_State.Test_BEGIN() )
539 Result_t result = m_File.OpenWrite(filename);
541 if ( ASDCP_SUCCESS(result) )
543 m_HeaderSize = HeaderSize;
544 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
545 result = m_State.Goto_INIT();
551 // Automatically sets the MXF file's metadata from the MPEG stream.
553 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
556 if ( ! m_State.Test_INIT() )
560 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
562 if ( ASDCP_SUCCESS(result) )
564 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
565 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
566 result = m_State.Goto_READY();
569 if ( ASDCP_SUCCESS(result) )
571 m_FooterPart.SetDeltaParams(IndexTableSegment::DeltaEntry(-1, 0, 0));
573 result = WriteASDCPHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrappingFrame)),
574 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
575 m_VDesc.EditRate, derive_timecode_rate_from_edit_rate(m_VDesc.EditRate));
581 // Writes a frame of essence to the MXF file. If the optional AESEncContext
582 // argument is present, the essence is encrypted prior to writing.
583 // Fails if the file is not open, is finalized, or an operating system
587 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
590 Result_t result = RESULT_OK;
592 if ( m_State.Test_READY() )
593 result = m_State.Goto_RUNNING(); // first time through, get the body location
595 IndexTableSegment::IndexEntry Entry;
596 Entry.StreamOffset = m_StreamOffset;
598 if ( ASDCP_SUCCESS(result) )
599 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
601 if ( ASDCP_FAILURE(result) )
604 // create mxflib flags
607 switch ( FrameBuf.FrameType() )
609 case FRAME_I: Flags = 0x00; break;
610 case FRAME_P: Flags = 0x22; break;
611 case FRAME_B: Flags = 0x33; break;
614 if ( FrameBuf.GOPStart() )
619 if ( FrameBuf.ClosedGOP() )
623 // update the index manager
624 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
625 Entry.KeyFrameOffset = 0 - m_GOPOffset;
628 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
629 Entry.TemporalOffset, Entry.KeyFrameOffset,
630 m_GOPOffset + Entry.TemporalOffset,
631 Entry.KeyFrameOffset - Entry.TemporalOffset,
634 m_FooterPart.PushIndexEntry(Entry);
642 // Closes the MXF file, writing the index and other closing information.
645 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
647 if ( ! m_State.Test_RUNNING() )
650 m_State.Goto_FINAL();
652 return WriteASDCPFooter();
656 //------------------------------------------------------------------------------------------
660 ASDCP::MPEG2::MXFWriter::MXFWriter()
664 ASDCP::MPEG2::MXFWriter::~MXFWriter()
668 // Warning: direct manipulation of MXF structures can interfere
669 // with the normal operation of the wrapper. Caveat emptor!
671 ASDCP::MXF::OP1aHeader&
672 ASDCP::MPEG2::MXFWriter::OP1aHeader()
674 if ( m_Writer.empty() )
676 assert(g_OP1aHeader);
677 return *g_OP1aHeader;
680 return m_Writer->m_HeaderPart;
683 // Warning: direct manipulation of MXF structures can interfere
684 // with the normal operation of the wrapper. Caveat emptor!
686 ASDCP::MXF::OPAtomIndexFooter&
687 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
689 if ( m_Writer.empty() )
691 assert(g_OPAtomIndexFooter);
692 return *g_OPAtomIndexFooter;
695 return m_Writer->m_FooterPart;
698 // Warning: direct manipulation of MXF structures can interfere
699 // with the normal operation of the wrapper. Caveat emptor!
702 ASDCP::MPEG2::MXFWriter::RIP()
704 if ( m_Writer.empty() )
710 return m_Writer->m_RIP;
713 // Open the file for writing. The file must not exist. Returns error if
714 // the operation cannot be completed.
716 ASDCP::MPEG2::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
717 const VideoDescriptor& VDesc, ui32_t HeaderSize)
719 if ( Info.LabelSetType == LS_MXF_SMPTE )
720 m_Writer = new h__Writer(DefaultSMPTEDict());
722 m_Writer = new h__Writer(DefaultInteropDict());
724 m_Writer->m_Info = Info;
726 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
728 if ( ASDCP_SUCCESS(result) )
729 result = m_Writer->SetSourceStream(VDesc);
731 if ( ASDCP_FAILURE(result) )
738 // Writes a frame of essence to the MXF file. If the optional AESEncContext
739 // argument is present, the essence is encrypted prior to writing.
740 // Fails if the file is not open, is finalized, or an operating system
743 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
745 if ( m_Writer.empty() )
748 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
751 // Closes the MXF file, writing the index and other closing information.
753 ASDCP::MPEG2::MXFWriter::Finalize()
755 if ( m_Writer.empty() )
758 return m_Writer->Finalize();
763 // end AS_DCP_MPEG2.cpp