2 Copyright (c) 2004-2006, 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"
35 //------------------------------------------------------------------------------------------
37 static std::string MPEG_PACKAGE_LABEL = "File Package: SMPTE 381M frame wrapping of MPEG2 video elementary stream";
38 static std::string PICT_DEF_LABEL = "Picture Track";
42 MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc)
44 ASDCP_TEST_NULL(VDescObj);
46 VDesc.SampleRate = VDescObj->SampleRate;
47 VDesc.EditRate = VDescObj->SampleRate;
48 VDesc.FrameRate = VDescObj->SampleRate.Numerator;
49 VDesc.ContainerDuration = VDescObj->ContainerDuration;
51 VDesc.FrameLayout = VDescObj->FrameLayout;
52 VDesc.StoredWidth = VDescObj->StoredWidth;
53 VDesc.StoredHeight = VDescObj->StoredHeight;
54 VDesc.AspectRatio = VDescObj->AspectRatio;
56 VDesc.ComponentDepth = VDescObj->ComponentDepth;
57 VDesc.HorizontalSubsampling = VDescObj->HorizontalSubsampling;
58 VDesc.VerticalSubsampling = VDescObj->VerticalSubsampling;
59 VDesc.ColorSiting = VDescObj->ColorSiting;
60 VDesc.CodedContentType = VDescObj->CodedContentType;
62 VDesc.LowDelay = VDescObj->LowDelay == 0 ? false : true;
63 VDesc.BitRate = VDescObj->BitRate;
64 VDesc.ProfileAndLevel = VDescObj->ProfileAndLevel;
71 MPEG2_VDesc_to_MD(MPEG2::VideoDescriptor& VDesc, MXF::MPEG2VideoDescriptor* VDescObj)
73 ASDCP_TEST_NULL(VDescObj);
75 VDescObj->SampleRate = VDesc.SampleRate;
76 VDescObj->SampleRate.Numerator = VDesc.FrameRate;
77 VDescObj->ContainerDuration = VDesc.ContainerDuration;
79 VDescObj->FrameLayout = VDesc.FrameLayout;
80 VDescObj->StoredWidth = VDesc.StoredWidth;
81 VDescObj->StoredHeight = VDesc.StoredHeight;
82 VDescObj->AspectRatio = VDesc.AspectRatio;
84 VDescObj->ComponentDepth = VDesc.ComponentDepth;
85 VDescObj->HorizontalSubsampling = VDesc.HorizontalSubsampling;
86 VDescObj->VerticalSubsampling = VDesc.VerticalSubsampling;
87 VDescObj->ColorSiting = VDesc.ColorSiting;
88 VDescObj->CodedContentType = VDesc.CodedContentType;
90 VDescObj->LowDelay = VDesc.LowDelay ? 1 : 0;
91 VDescObj->BitRate = VDesc.BitRate;
92 VDescObj->ProfileAndLevel = VDesc.ProfileAndLevel;
99 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
105 SampleRate: %lu/%lu\n\
109 AspectRatio: %lu/%lu\n\
110 ComponentDepth: %lu\n\
111 HorizontalSubsmpl: %lu\n\
112 VerticalSubsmpl: %lu\n\
114 CodedContentType: %lu\n\
117 ProfileAndLevel: %lu\n\
118 ContainerDuration: %lu\n",
119 VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
123 VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
124 VDesc.ComponentDepth,
125 VDesc.HorizontalSubsampling,
126 VDesc.VerticalSubsampling,
128 VDesc.CodedContentType,
131 VDesc.ProfileAndLevel,
132 VDesc.ContainerDuration
136 //------------------------------------------------------------------------------------------
138 // hidden, internal implementation of MPEG2 reader
140 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
142 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
145 VideoDescriptor m_VDesc; // video parameter list
149 Result_t OpenRead(const char*);
150 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
151 Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
152 Result_t FindFrameGOPStart(ui32_t, ui32_t&);
159 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
161 Result_t result = OpenMXFRead(filename);
163 if( ASDCP_SUCCESS(result) )
165 InterchangeObject* Object;
166 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
169 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
173 if( ASDCP_SUCCESS(result) )
174 result = InitMXFIndex();
176 if( ASDCP_SUCCESS(result) )
186 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
187 AESDecContext* Ctx, HMACContext* HMAC)
191 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
193 if ( ASDCP_SUCCESS(result) )
194 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
203 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
207 if ( ! m_File.IsOpen() )
210 // look up frame index node
211 IndexTableSegment::IndexEntry TmpEntry;
213 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
215 DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
219 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
228 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
229 AESDecContext* Ctx, HMACContext* HMAC)
231 if ( ! m_File.IsOpen() )
234 Result_t result = ReadEKLVPacket(FrameNum, FrameBuf, Dict::ul(MDD_MPEG2Essence), Ctx, HMAC);
236 if ( ASDCP_FAILURE(result) )
239 IndexTableSegment::IndexEntry TmpEntry;
240 m_FooterPart.Lookup(FrameNum, TmpEntry);
242 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
244 case 0: FrameBuf.FrameType(FRAME_I); break;
245 case 2: FrameBuf.FrameType(FRAME_P); break;
246 case 3: FrameBuf.FrameType(FRAME_B); break;
247 default: FrameBuf.FrameType(FRAME_U);
250 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
251 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
252 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
257 //------------------------------------------------------------------------------------------
262 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
267 fprintf(stream, "Frame: %06lu, %c%-2hu, %7lu bytes",
268 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
271 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
276 hexdump(m_Data, dump_len, stream);
280 //------------------------------------------------------------------------------------------
282 ASDCP::MPEG2::MXFReader::MXFReader()
284 m_Reader = new h__Reader;
288 ASDCP::MPEG2::MXFReader::~MXFReader()
292 // Open the file for reading. The file must exist. Returns error if the
293 // operation cannot be completed.
295 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
297 return m_Reader->OpenRead(filename);
302 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
303 AESDecContext* Ctx, HMACContext* HMAC) const
305 if ( m_Reader && m_Reader->m_File.IsOpen() )
306 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
314 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
315 AESDecContext* Ctx, HMACContext* HMAC) const
317 if ( m_Reader && m_Reader->m_File.IsOpen() )
318 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
326 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
328 if ( m_Reader && m_Reader->m_File.IsOpen() )
329 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
335 // Fill the struct with the values from the file's header.
336 // Returns RESULT_INIT if the file is not open.
338 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
340 if ( m_Reader && m_Reader->m_File.IsOpen() )
342 VDesc = m_Reader->m_VDesc;
350 // Fill the struct with the values from the file's header.
351 // Returns RESULT_INIT if the file is not open.
353 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
355 if ( m_Reader && m_Reader->m_File.IsOpen() )
357 Info = m_Reader->m_Info;
366 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
368 if ( m_Reader->m_File.IsOpen() )
369 m_Reader->m_HeaderPart.Dump(stream);
375 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
377 if ( m_Reader->m_File.IsOpen() )
378 m_Reader->m_FooterPart.Dump(stream);
382 //------------------------------------------------------------------------------------------
385 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
388 VideoDescriptor m_VDesc;
391 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
393 h__Writer() : m_GOPOffset(0) {}
396 Result_t OpenWrite(const char*, ui32_t HeaderSize);
397 Result_t SetSourceStream(const VideoDescriptor&);
398 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
403 // Open the file for writing. The file must not exist. Returns error if
404 // the operation cannot be completed.
406 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
408 if ( ! m_State.Test_BEGIN() )
411 Result_t result = m_File.OpenWrite(filename);
413 if ( ASDCP_SUCCESS(result) )
415 m_HeaderSize = HeaderSize;
416 m_EssenceDescriptor = new MPEG2VideoDescriptor;
417 result = m_State.Goto_INIT();
423 // Automatically sets the MXF file's metadata from the MPEG stream.
425 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
427 if ( ! m_State.Test_INIT() )
431 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
433 if ( ASDCP_SUCCESS(result) )
434 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(Dict::ul(MDD_MPEG2_VESWrapping)),
435 PICT_DEF_LABEL, UL(Dict::ul(MDD_PictureDataDef)),
436 m_VDesc.EditRate, 24 /* TCFrameRate */);
438 if ( ASDCP_SUCCESS(result) )
439 result = m_State.Goto_READY();
444 // Writes a frame of essence to the MXF file. If the optional AESEncContext
445 // argument is present, the essence is encrypted prior to writing.
446 // Fails if the file is not open, is finalized, or an operating system
450 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
453 Result_t result = RESULT_OK;
455 if ( m_State.Test_READY() )
456 result = m_State.Goto_RUNNING(); // first time through, get the body location
458 IndexTableSegment::IndexEntry Entry;
459 Entry.StreamOffset = m_StreamOffset;
461 if ( ASDCP_SUCCESS(result) )
462 result = WriteEKLVPacket(FrameBuf, Dict::ul(MDD_MPEG2Essence), Ctx, HMAC);
464 if ( ASDCP_FAILURE(result) )
467 // create mxflib flags
470 switch ( FrameBuf.FrameType() )
472 case FRAME_I: Flags = 0x00; break;
473 case FRAME_P: Flags = 0x22; break;
474 case FRAME_B: Flags = 0x33; break;
477 if ( FrameBuf.GOPStart() )
482 if ( FrameBuf.ClosedGOP() )
486 // update the index manager
487 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
488 Entry.KeyFrameOffset = m_GOPOffset;
490 m_FooterPart.PushIndexEntry(Entry);
498 // Closes the MXF file, writing the index and other closing information.
501 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
503 if ( ! m_State.Test_RUNNING() )
506 m_State.Goto_FINAL();
508 return WriteMXFFooter();
512 //------------------------------------------------------------------------------------------
516 ASDCP::MPEG2::MXFWriter::MXFWriter()
520 ASDCP::MPEG2::MXFWriter::~MXFWriter()
525 // Open the file for writing. The file must not exist. Returns error if
526 // the operation cannot be completed.
528 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
529 const VideoDescriptor& VDesc, ui32_t HeaderSize)
531 m_Writer = new h__Writer;
533 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
535 if ( ASDCP_SUCCESS(result) )
537 m_Writer->m_Info = Info;
538 result = m_Writer->SetSourceStream(VDesc);
541 if ( ASDCP_FAILURE(result) )
548 // Writes a frame of essence to the MXF file. If the optional AESEncContext
549 // argument is present, the essence is encrypted prior to writing.
550 // Fails if the file is not open, is finalized, or an operating system
553 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
555 if ( m_Writer.empty() )
558 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
561 // Closes the MXF file, writing the index and other closing information.
563 ASDCP::MPEG2::MXFWriter::Finalize()
565 if ( m_Writer.empty() )
568 return m_Writer->Finalize();
573 // end AS_DCP_MPEG2.cpp