2 Copyright (c) 2004-2007, 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->ContainerDuration = VDesc.ContainerDuration;
78 VDescObj->FrameLayout = VDesc.FrameLayout;
79 VDescObj->StoredWidth = VDesc.StoredWidth;
80 VDescObj->StoredHeight = VDesc.StoredHeight;
81 VDescObj->AspectRatio = VDesc.AspectRatio;
83 VDescObj->ComponentDepth = VDesc.ComponentDepth;
84 VDescObj->HorizontalSubsampling = VDesc.HorizontalSubsampling;
85 VDescObj->VerticalSubsampling = VDesc.VerticalSubsampling;
86 VDescObj->ColorSiting = VDesc.ColorSiting;
87 VDescObj->CodedContentType = VDesc.CodedContentType;
89 VDescObj->LowDelay = VDesc.LowDelay ? 1 : 0;
90 VDescObj->BitRate = VDesc.BitRate;
91 VDescObj->ProfileAndLevel = VDesc.ProfileAndLevel;
98 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
108 AspectRatio: %d/%d\n\
109 ComponentDepth: %u\n\
110 HorizontalSubsmpl: %u\n\
111 VerticalSubsmpl: %u\n\
113 CodedContentType: %u\n\
116 ProfileAndLevel: %u\n\
117 ContainerDuration: %u\n",
118 VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
122 VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
123 VDesc.ComponentDepth,
124 VDesc.HorizontalSubsampling,
125 VDesc.VerticalSubsampling,
127 VDesc.CodedContentType,
130 VDesc.ProfileAndLevel,
131 VDesc.ContainerDuration
135 //------------------------------------------------------------------------------------------
137 // hidden, internal implementation of MPEG2 reader
139 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
141 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
144 VideoDescriptor m_VDesc; // video parameter list
148 Result_t OpenRead(const char*);
149 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
150 Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
151 Result_t FindFrameGOPStart(ui32_t, ui32_t&);
158 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
160 Result_t result = OpenMXFRead(filename);
162 if( ASDCP_SUCCESS(result) )
164 InterchangeObject* Object;
165 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
168 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
172 if( ASDCP_SUCCESS(result) )
173 result = InitMXFIndex();
175 if( ASDCP_SUCCESS(result) )
185 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
186 AESDecContext* Ctx, HMACContext* HMAC)
190 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
192 if ( ASDCP_SUCCESS(result) )
193 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
202 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
206 if ( ! m_File.IsOpen() )
209 // look up frame index node
210 IndexTableSegment::IndexEntry TmpEntry;
212 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
214 DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
218 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
227 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
228 AESDecContext* Ctx, HMACContext* HMAC)
230 if ( ! m_File.IsOpen() )
233 Result_t result = ReadEKLVPacket(FrameNum, FrameBuf, Dict::ul(MDD_MPEG2Essence), Ctx, HMAC);
235 if ( ASDCP_FAILURE(result) )
238 IndexTableSegment::IndexEntry TmpEntry;
239 m_FooterPart.Lookup(FrameNum, TmpEntry);
241 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
243 case 0: FrameBuf.FrameType(FRAME_I); break;
244 case 2: FrameBuf.FrameType(FRAME_P); break;
245 case 3: FrameBuf.FrameType(FRAME_B); break;
246 default: FrameBuf.FrameType(FRAME_U);
249 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
250 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
251 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
256 //------------------------------------------------------------------------------------------
261 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
266 fprintf(stream, "Frame: %06u, %c%-2hu, %7u bytes",
267 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
270 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
275 Kumu::hexdump(m_Data, dump_len, stream);
279 //------------------------------------------------------------------------------------------
281 ASDCP::MPEG2::MXFReader::MXFReader()
283 m_Reader = new h__Reader;
287 ASDCP::MPEG2::MXFReader::~MXFReader()
291 // Open the file for reading. The file must exist. Returns error if the
292 // operation cannot be completed.
294 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
296 return m_Reader->OpenRead(filename);
301 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
302 AESDecContext* Ctx, HMACContext* HMAC) const
304 if ( m_Reader && m_Reader->m_File.IsOpen() )
305 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
313 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
314 AESDecContext* Ctx, HMACContext* HMAC) const
316 if ( m_Reader && m_Reader->m_File.IsOpen() )
317 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
325 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
327 if ( m_Reader && m_Reader->m_File.IsOpen() )
328 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
334 // Fill the struct with the values from the file's header.
335 // Returns RESULT_INIT if the file is not open.
337 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
339 if ( m_Reader && m_Reader->m_File.IsOpen() )
341 VDesc = m_Reader->m_VDesc;
349 // Fill the struct with the values from the file's header.
350 // Returns RESULT_INIT if the file is not open.
352 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
354 if ( m_Reader && m_Reader->m_File.IsOpen() )
356 Info = m_Reader->m_Info;
365 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
367 if ( m_Reader->m_File.IsOpen() )
368 m_Reader->m_HeaderPart.Dump(stream);
374 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
376 if ( m_Reader->m_File.IsOpen() )
377 m_Reader->m_FooterPart.Dump(stream);
381 //------------------------------------------------------------------------------------------
384 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
387 VideoDescriptor m_VDesc;
389 byte_t m_EssenceUL[SMPTE_UL_LENGTH];
391 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
393 h__Writer() : m_GOPOffset(0) {
394 memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
399 Result_t OpenWrite(const char*, ui32_t HeaderSize);
400 Result_t SetSourceStream(const VideoDescriptor&);
401 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
406 // Open the file for writing. The file must not exist. Returns error if
407 // the operation cannot be completed.
409 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
411 if ( ! m_State.Test_BEGIN() )
414 Result_t result = m_File.OpenWrite(filename);
416 if ( ASDCP_SUCCESS(result) )
418 m_HeaderSize = HeaderSize;
419 m_EssenceDescriptor = new MPEG2VideoDescriptor;
420 result = m_State.Goto_INIT();
426 // Automatically sets the MXF file's metadata from the MPEG stream.
428 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
430 if ( ! m_State.Test_INIT() )
434 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
436 if ( ASDCP_SUCCESS(result) )
437 result = WriteMXFHeader(MPEG_PACKAGE_LABEL, UL(Dict::ul(MDD_MPEG2_VESWrapping)),
438 PICT_DEF_LABEL, UL(Dict::ul(MDD_PictureDataDef)),
439 m_VDesc.EditRate, 24 /* TCFrameRate */);
441 if ( ASDCP_SUCCESS(result) )
443 memcpy(m_EssenceUL, Dict::ul(MDD_MPEG2Essence), SMPTE_UL_LENGTH);
444 m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
445 result = m_State.Goto_READY();
451 // Writes a frame of essence to the MXF file. If the optional AESEncContext
452 // argument is present, the essence is encrypted prior to writing.
453 // Fails if the file is not open, is finalized, or an operating system
457 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
460 Result_t result = RESULT_OK;
462 if ( m_State.Test_READY() )
463 result = m_State.Goto_RUNNING(); // first time through, get the body location
465 IndexTableSegment::IndexEntry Entry;
466 Entry.StreamOffset = m_StreamOffset;
468 if ( ASDCP_SUCCESS(result) )
469 result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
471 if ( ASDCP_FAILURE(result) )
474 // create mxflib flags
477 switch ( FrameBuf.FrameType() )
479 case FRAME_I: Flags = 0x00; break;
480 case FRAME_P: Flags = 0x22; break;
481 case FRAME_B: Flags = 0x33; break;
484 if ( FrameBuf.GOPStart() )
489 if ( FrameBuf.ClosedGOP() )
493 // update the index manager
494 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
495 Entry.KeyFrameOffset = m_GOPOffset;
497 m_FooterPart.PushIndexEntry(Entry);
505 // Closes the MXF file, writing the index and other closing information.
508 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
510 if ( ! m_State.Test_RUNNING() )
513 m_State.Goto_FINAL();
515 return WriteMXFFooter();
519 //------------------------------------------------------------------------------------------
523 ASDCP::MPEG2::MXFWriter::MXFWriter()
527 ASDCP::MPEG2::MXFWriter::~MXFWriter()
532 // Open the file for writing. The file must not exist. Returns error if
533 // the operation cannot be completed.
535 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
536 const VideoDescriptor& VDesc, ui32_t HeaderSize)
538 m_Writer = new h__Writer;
540 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
542 if ( ASDCP_SUCCESS(result) )
544 m_Writer->m_Info = Info;
545 result = m_Writer->SetSourceStream(VDesc);
548 if ( ASDCP_FAILURE(result) )
555 // Writes a frame of essence to the MXF file. If the optional AESEncContext
556 // argument is present, the essence is encrypted prior to writing.
557 // Fails if the file is not open, is finalized, or an operating system
560 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
562 if ( m_Writer.empty() )
565 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
568 // Closes the MXF file, writing the index and other closing information.
570 ASDCP::MPEG2::MXFWriter::Finalize()
572 if ( m_Writer.empty() )
575 return m_Writer->Finalize();
580 // end AS_DCP_MPEG2.cpp