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)
255 if ( ! m_File.IsOpen() )
258 Result_t result = ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_MPEG2Essence), Ctx, HMAC);
260 if ( ASDCP_FAILURE(result) )
263 IndexTableSegment::IndexEntry TmpEntry;
264 m_FooterPart.Lookup(FrameNum, TmpEntry);
266 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
268 case 0: FrameBuf.FrameType(FRAME_I); break;
269 case 2: FrameBuf.FrameType(FRAME_P); break;
270 case 3: FrameBuf.FrameType(FRAME_B); break;
271 default: FrameBuf.FrameType(FRAME_U);
274 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
275 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
276 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
281 //------------------------------------------------------------------------------------------
286 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
291 fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
292 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
295 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
300 Kumu::hexdump(m_Data, dump_len, stream);
304 //------------------------------------------------------------------------------------------
306 ASDCP::MPEG2::MXFReader::MXFReader()
308 m_Reader = new h__Reader(DefaultCompositeDict());
312 ASDCP::MPEG2::MXFReader::~MXFReader()
316 // Open the file for reading. The file must exist. Returns error if the
317 // operation cannot be completed.
319 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
321 return m_Reader->OpenRead(filename);
326 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
327 AESDecContext* Ctx, HMACContext* HMAC) const
329 if ( m_Reader && m_Reader->m_File.IsOpen() )
330 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
338 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
339 AESDecContext* Ctx, HMACContext* HMAC) const
341 if ( m_Reader && m_Reader->m_File.IsOpen() )
342 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
350 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
352 if ( m_Reader && m_Reader->m_File.IsOpen() )
353 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
359 // Fill the struct with the values from the file's header.
360 // Returns RESULT_INIT if the file is not open.
362 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
364 if ( m_Reader && m_Reader->m_File.IsOpen() )
366 VDesc = m_Reader->m_VDesc;
374 // Fill the struct with the values from the file's header.
375 // Returns RESULT_INIT if the file is not open.
377 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
379 if ( m_Reader && m_Reader->m_File.IsOpen() )
381 Info = m_Reader->m_Info;
390 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
392 if ( m_Reader->m_File.IsOpen() )
393 m_Reader->m_HeaderPart.Dump(stream);
399 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
401 if ( m_Reader->m_File.IsOpen() )
402 m_Reader->m_FooterPart.Dump(stream);
406 //------------------------------------------------------------------------------------------
409 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
411 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
415 VideoDescriptor m_VDesc;
417 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
419 h__Writer(const Dictionary& d) : ASDCP::h__Writer(d), m_GOPOffset(0) {
420 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
425 Result_t OpenWrite(const char*, ui32_t HeaderSize);
426 Result_t SetSourceStream(const VideoDescriptor&);
427 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
432 // Open the file for writing. The file must not exist. Returns error if
433 // the operation cannot be completed.
435 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
437 if ( ! m_State.Test_BEGIN() )
440 Result_t result = m_File.OpenWrite(filename);
442 if ( ASDCP_SUCCESS(result) )
444 m_HeaderSize = HeaderSize;
445 m_EssenceDescriptor = new MPEG2VideoDescriptor(m_Dict);
446 result = m_State.Goto_INIT();
452 // Automatically sets the MXF file's metadata from the MPEG stream.
454 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
456 if ( ! m_State.Test_INIT() )
460 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
462 if ( ASDCP_SUCCESS(result) )
463 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)),
464 PICT_DEF_LABEL, UL(m_Dict->ul(MDD_PictureDataDef)),
465 m_VDesc.EditRate, 24 /* TCFrameRate */);
467 if ( ASDCP_SUCCESS(result) )
469 memcpy(m_EssenceUL, m_Dict->ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
470 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
471 result = m_State.Goto_READY();
477 // Writes a frame of essence to the MXF file. If the optional AESEncContext
478 // argument is present, the essence is encrypted prior to writing.
479 // Fails if the file is not open, is finalized, or an operating system
483 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
486 Result_t result = RESULT_OK;
488 if ( m_State.Test_READY() )
489 result = m_State.Goto_RUNNING(); // first time through, get the body location
491 IndexTableSegment::IndexEntry Entry;
492 Entry.StreamOffset = m_StreamOffset;
494 if ( ASDCP_SUCCESS(result) )
495 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
497 if ( ASDCP_FAILURE(result) )
500 // create mxflib flags
503 switch ( FrameBuf.FrameType() )
505 case FRAME_I: Flags = 0x00; break;
506 case FRAME_P: Flags = 0x22; break;
507 case FRAME_B: Flags = 0x33; break;
510 if ( FrameBuf.GOPStart() )
515 if ( FrameBuf.ClosedGOP() )
519 // update the index manager
520 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
521 Entry.KeyFrameOffset = 0 - m_GOPOffset;
524 fprintf(stderr, "to: %4hd ko: %4hd c1: %4hd c2: %4hd fl: 0x%02x\n",
525 Entry.TemporalOffset, Entry.KeyFrameOffset,
526 m_GOPOffset + Entry.TemporalOffset,
527 Entry.KeyFrameOffset - Entry.TemporalOffset,
530 m_FooterPart.PushIndexEntry(Entry);
538 // Closes the MXF file, writing the index and other closing information.
541 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
543 if ( ! m_State.Test_RUNNING() )
546 m_State.Goto_FINAL();
548 return WriteMXFFooter();
552 //------------------------------------------------------------------------------------------
556 ASDCP::MPEG2::MXFWriter::MXFWriter()
560 ASDCP::MPEG2::MXFWriter::~MXFWriter()
565 // Open the file for writing. The file must not exist. Returns error if
566 // the operation cannot be completed.
568 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
569 const VideoDescriptor& VDesc, ui32_t HeaderSize)
571 if ( Info.LabelSetType == LS_MXF_SMPTE )
572 m_Writer = new h__Writer(DefaultSMPTEDict());
574 m_Writer = new h__Writer(DefaultInteropDict());
576 m_Writer->m_Info = Info;
578 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
580 if ( ASDCP_SUCCESS(result) )
581 result = m_Writer->SetSourceStream(VDesc);
583 if ( ASDCP_FAILURE(result) )
590 // Writes a frame of essence to the MXF file. If the optional AESEncContext
591 // argument is present, the essence is encrypted prior to writing.
592 // Fails if the file is not open, is finalized, or an operating system
595 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
597 if ( m_Writer.empty() )
600 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
603 // Closes the MXF file, writing the index and other closing information.
605 ASDCP::MPEG2::MXFWriter::Finalize()
607 if ( m_Writer.empty() )
610 return m_Writer->Finalize();
615 // end AS_DCP_MPEG2.cpp