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 Kumu::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;
390 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
392 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
394 h__Writer() : m_GOPOffset(0) {
395 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
400 Result_t OpenWrite(const char*, ui32_t HeaderSize);
401 Result_t SetSourceStream(const VideoDescriptor&);
402 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
407 // Open the file for writing. The file must not exist. Returns error if
408 // the operation cannot be completed.
410 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
412 if ( ! m_State.Test_BEGIN() )
415 Result_t result = m_File.OpenWrite(filename);
417 if ( ASDCP_SUCCESS(result) )
419 m_HeaderSize = HeaderSize;
420 m_EssenceDescriptor = new MPEG2VideoDescriptor;
421 result = m_State.Goto_INIT();
427 // Automatically sets the MXF file's metadata from the MPEG stream.
429 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
431 if ( ! m_State.Test_INIT() )
435 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
437 if ( ASDCP_SUCCESS(result) )
438 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(Dict::ul(MDD_MPEG2_VESWrapping)),
439 PICT_DEF_LABEL, UL(Dict::ul(MDD_PictureDataDef)),
440 m_VDesc.EditRate, 24 /* TCFrameRate */);
442 if ( ASDCP_SUCCESS(result) )
444 memcpy(m_EssenceUL, Dict::ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
445 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
446 result = m_State.Goto_READY();
452 // Writes a frame of essence to the MXF file. If the optional AESEncContext
453 // argument is present, the essence is encrypted prior to writing.
454 // Fails if the file is not open, is finalized, or an operating system
458 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
461 Result_t result = RESULT_OK;
463 if ( m_State.Test_READY() )
464 result = m_State.Goto_RUNNING(); // first time through, get the body location
466 IndexTableSegment::IndexEntry Entry;
467 Entry.StreamOffset = m_StreamOffset;
469 if ( ASDCP_SUCCESS(result) )
470 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
472 if ( ASDCP_FAILURE(result) )
475 // create mxflib flags
478 switch ( FrameBuf.FrameType() )
480 case FRAME_I: Flags = 0x00; break;
481 case FRAME_P: Flags = 0x22; break;
482 case FRAME_B: Flags = 0x33; break;
485 if ( FrameBuf.GOPStart() )
490 if ( FrameBuf.ClosedGOP() )
494 // update the index manager
495 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
496 Entry.KeyFrameOffset = m_GOPOffset;
498 m_FooterPart.PushIndexEntry(Entry);
506 // Closes the MXF file, writing the index and other closing information.
509 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
511 if ( ! m_State.Test_RUNNING() )
514 m_State.Goto_FINAL();
516 return WriteMXFFooter();
520 //------------------------------------------------------------------------------------------
524 ASDCP::MPEG2::MXFWriter::MXFWriter()
528 ASDCP::MPEG2::MXFWriter::~MXFWriter()
533 // Open the file for writing. The file must not exist. Returns error if
534 // the operation cannot be completed.
536 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
537 const VideoDescriptor& VDesc, ui32_t HeaderSize)
539 m_Writer = new h__Writer;
541 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
543 if ( ASDCP_SUCCESS(result) )
545 m_Writer->m_Info = Info;
546 result = m_Writer->SetSourceStream(VDesc);
549 if ( ASDCP_FAILURE(result) )
556 // Writes a frame of essence to the MXF file. If the optional AESEncContext
557 // argument is present, the essence is encrypted prior to writing.
558 // Fails if the file is not open, is finalized, or an operating system
561 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
563 if ( m_Writer.empty() )
566 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
569 // Closes the MXF file, writing the index and other closing information.
571 ASDCP::MPEG2::MXFWriter::Finalize()
573 if ( m_Writer.empty() )
576 return m_Writer->Finalize();
581 // end AS_DCP_MPEG2.cpp