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)) )
243 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
250 ASDCP::MPEG2::MXFReader::h__Reader::FrameType(ui32_t FrameNum, FrameType_t& type)
252 if ( ! m_File.IsOpen() )
255 // look up frame index node
256 IndexTableSegment::IndexEntry TmpEntry;
258 if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
263 type = ( (TmpEntry.Flags & 0x0f) == 3 ) ? FRAME_B : ( (TmpEntry.Flags & 0x0f) == 2 ) ? FRAME_P : FRAME_I;
271 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
272 AESDecContext* Ctx, HMACContext* HMAC)
275 if ( ! m_File.IsOpen() )
278 Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_MPEG2Essence), Ctx, HMAC);
280 if ( ASDCP_FAILURE(result) )
283 IndexTableSegment::IndexEntry TmpEntry;
284 m_IndexAccess.Lookup(FrameNum, TmpEntry);
286 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
288 case 0: FrameBuf.FrameType(FRAME_I); break;
289 case 2: FrameBuf.FrameType(FRAME_P); break;
290 case 3: FrameBuf.FrameType(FRAME_B); break;
291 default: FrameBuf.FrameType(FRAME_U);
294 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
295 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
296 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
301 //------------------------------------------------------------------------------------------
306 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
311 fprintf(stream, "Frame: %06u, %c%-2hhu, %7u bytes",
312 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
315 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
320 Kumu::hexdump(m_Data, dump_len, stream);
324 //------------------------------------------------------------------------------------------
326 ASDCP::MPEG2::MXFReader::MXFReader()
328 m_Reader = new h__Reader(DefaultCompositeDict());
332 ASDCP::MPEG2::MXFReader::~MXFReader()
334 if ( m_Reader && m_Reader->m_File.IsOpen() )
338 // Warning: direct manipulation of MXF structures can interfere
339 // with the normal operation of the wrapper. Caveat emptor!
341 ASDCP::MXF::OP1aHeader&
342 ASDCP::MPEG2::MXFReader::OP1aHeader()
344 if ( m_Reader.empty() )
346 assert(g_OP1aHeader);
347 return *g_OP1aHeader;
350 return m_Reader->m_HeaderPart;
353 // Warning: direct manipulation of MXF structures can interfere
354 // with the normal operation of the wrapper. Caveat emptor!
356 ASDCP::MXF::OPAtomIndexFooter&
357 ASDCP::MPEG2::MXFReader::OPAtomIndexFooter()
359 if ( m_Reader.empty() )
361 assert(g_OPAtomIndexFooter);
362 return *g_OPAtomIndexFooter;
365 return m_Reader->m_IndexAccess;
368 // Warning: direct manipulation of MXF structures can interfere
369 // with the normal operation of the wrapper. Caveat emptor!
372 ASDCP::MPEG2::MXFReader::RIP()
374 if ( m_Reader.empty() )
380 return m_Reader->m_RIP;
383 // Open the file for reading. The file must exist. Returns error if the
384 // operation cannot be completed.
386 ASDCP::MPEG2::MXFReader::OpenRead(const std::string& filename) const
388 return m_Reader->OpenRead(filename);
393 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
394 AESDecContext* Ctx, HMACContext* HMAC) const
396 if ( m_Reader && m_Reader->m_File.IsOpen() )
397 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
405 ASDCP::MPEG2::MXFReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const
407 return m_Reader->LocateFrame(FrameNum, streamOffset, temporalOffset, keyFrameOffset);
412 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
413 AESDecContext* Ctx, HMACContext* HMAC) const
415 if ( m_Reader && m_Reader->m_File.IsOpen() )
416 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
424 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
426 if ( m_Reader && m_Reader->m_File.IsOpen() )
427 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
433 // Fill the struct with the values from the file's header.
434 // Returns RESULT_INIT if the file is not open.
436 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
438 if ( m_Reader && m_Reader->m_File.IsOpen() )
440 VDesc = m_Reader->m_VDesc;
448 // Fill the struct with the values from the file's header.
449 // Returns RESULT_INIT if the file is not open.
451 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
453 if ( m_Reader && m_Reader->m_File.IsOpen() )
455 Info = m_Reader->m_Info;
464 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
466 if ( m_Reader->m_File.IsOpen() )
467 m_Reader->m_HeaderPart.Dump(stream);
473 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
475 if ( m_Reader->m_File.IsOpen() )
476 m_Reader->m_IndexAccess.Dump(stream);
481 ASDCP::MPEG2::MXFReader::Close() const
483 if ( m_Reader && m_Reader->m_File.IsOpen() )
494 ASDCP::MPEG2::MXFReader::FrameType(ui32_t FrameNum, FrameType_t& type) const
499 return m_Reader->FrameType(FrameNum, type);
503 //------------------------------------------------------------------------------------------
506 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__ASDCPWriter
508 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
512 VideoDescriptor m_VDesc;
514 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
516 h__Writer(const Dictionary& d) : ASDCP::h__ASDCPWriter(d), m_GOPOffset(0) {
517 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
520 virtual ~h__Writer(){}
522 Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
523 Result_t SetSourceStream(const VideoDescriptor&);
524 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
529 // Open the file for writing. The file must not exist. Returns error if
530 // the operation cannot be completed.
532 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
534 if ( ! m_State.Test_BEGIN() )
537 Result_t result = m_File.OpenWrite(filename);
539 if ( ASDCP_SUCCESS(result) )
541 m_HeaderSize = HeaderSize;
542 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
543 result = m_State.Goto_INIT();
549 // Automatically sets the MXF file's metadata from the MPEG stream.
551 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
554 if ( ! m_State.Test_INIT() )
558 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
560 if ( ASDCP_SUCCESS(result) )
562 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
563 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
564 result = m_State.Goto_READY();
567 if ( ASDCP_SUCCESS(result) )
569 m_FooterPart.SetDeltaParams(IndexTableSegment::DeltaEntry(-1, 0, 0));
571 result = WriteASDCPHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrappingFrame)),
572 PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
573 m_VDesc.EditRate, derive_timecode_rate_from_edit_rate(m_VDesc.EditRate));
579 // Writes a frame of essence to the MXF file. If the optional AESEncContext
580 // argument is present, the essence is encrypted prior to writing.
581 // Fails if the file is not open, is finalized, or an operating system
585 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
588 Result_t result = RESULT_OK;
590 if ( m_State.Test_READY() )
591 result = m_State.Goto_RUNNING(); // first time through, get the body location
593 IndexTableSegment::IndexEntry Entry;
594 Entry.StreamOffset = m_StreamOffset;
596 if ( ASDCP_SUCCESS(result) )
597 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, MXF_BER_LENGTH, Ctx, HMAC);
599 if ( ASDCP_FAILURE(result) )
602 // create mxflib flags
605 switch ( FrameBuf.FrameType() )
607 case FRAME_I: Flags = 0x00; break;
608 case FRAME_P: Flags = 0x22; break;
609 case FRAME_B: Flags = 0x33; break;
612 if ( FrameBuf.GOPStart() )
617 if ( FrameBuf.ClosedGOP() )
621 // update the index manager
622 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
623 Entry.KeyFrameOffset = 0 - m_GOPOffset;
626 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
627 Entry.TemporalOffset, Entry.KeyFrameOffset,
628 m_GOPOffset + Entry.TemporalOffset,
629 Entry.KeyFrameOffset - Entry.TemporalOffset,
632 m_FooterPart.PushIndexEntry(Entry);
640 // Closes the MXF file, writing the index and other closing information.
643 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
645 if ( ! m_State.Test_RUNNING() )
648 m_State.Goto_FINAL();
650 return WriteASDCPFooter();
654 //------------------------------------------------------------------------------------------
658 ASDCP::MPEG2::MXFWriter::MXFWriter()
662 ASDCP::MPEG2::MXFWriter::~MXFWriter()
666 // Warning: direct manipulation of MXF structures can interfere
667 // with the normal operation of the wrapper. Caveat emptor!
669 ASDCP::MXF::OP1aHeader&
670 ASDCP::MPEG2::MXFWriter::OP1aHeader()
672 if ( m_Writer.empty() )
674 assert(g_OP1aHeader);
675 return *g_OP1aHeader;
678 return m_Writer->m_HeaderPart;
681 // Warning: direct manipulation of MXF structures can interfere
682 // with the normal operation of the wrapper. Caveat emptor!
684 ASDCP::MXF::OPAtomIndexFooter&
685 ASDCP::MPEG2::MXFWriter::OPAtomIndexFooter()
687 if ( m_Writer.empty() )
689 assert(g_OPAtomIndexFooter);
690 return *g_OPAtomIndexFooter;
693 return m_Writer->m_FooterPart;
696 // Warning: direct manipulation of MXF structures can interfere
697 // with the normal operation of the wrapper. Caveat emptor!
700 ASDCP::MPEG2::MXFWriter::RIP()
702 if ( m_Writer.empty() )
708 return m_Writer->m_RIP;
711 // Open the file for writing. The file must not exist. Returns error if
712 // the operation cannot be completed.
714 ASDCP::MPEG2::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
715 const VideoDescriptor& VDesc, ui32_t HeaderSize)
717 if ( Info.LabelSetType == LS_MXF_SMPTE )
718 m_Writer = new h__Writer(DefaultSMPTEDict());
720 m_Writer = new h__Writer(DefaultInteropDict());
722 m_Writer->m_Info = Info;
724 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
726 if ( ASDCP_SUCCESS(result) )
727 result = m_Writer->SetSourceStream(VDesc);
729 if ( ASDCP_FAILURE(result) )
736 // Writes a frame of essence to the MXF file. If the optional AESEncContext
737 // argument is present, the essence is encrypted prior to writing.
738 // Fails if the file is not open, is finalized, or an operating system
741 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
743 if ( m_Writer.empty() )
746 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
749 // Closes the MXF file, writing the index and other closing information.
751 ASDCP::MPEG2::MXFWriter::Finalize()
753 if ( m_Writer.empty() )
756 return m_Writer->Finalize();
761 // end AS_DCP_MPEG2.cpp