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"
36 //------------------------------------------------------------------------------------------
40 ASDCP::MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc)
42 ASDCP_TEST_NULL(VDescObj);
44 VDesc.SampleRate = VDescObj->SampleRate;
45 VDesc.EditRate = VDescObj->SampleRate;
46 VDesc.FrameRate = VDescObj->SampleRate.Numerator;
47 VDesc.ContainerDuration = VDescObj->ContainerDuration;
49 VDesc.FrameLayout = VDescObj->FrameLayout;
50 VDesc.StoredWidth = VDescObj->StoredWidth;
51 VDesc.StoredHeight = VDescObj->StoredHeight;
52 VDesc.AspectRatio = VDescObj->AspectRatio;
54 VDesc.ComponentDepth = VDescObj->ComponentDepth;
55 VDesc.HorizontalSubsampling = VDescObj->HorizontalSubsampling;
56 VDesc.VerticalSubsampling = VDescObj->VerticalSubsampling;
57 VDesc.ColorSiting = VDescObj->ColorSiting;
58 VDesc.CodedContentType = VDescObj->CodedContentType;
60 VDesc.LowDelay = VDescObj->LowDelay;
61 VDesc.BitRate = VDescObj->BitRate;
62 VDesc.ProfileAndLevel = VDescObj->ProfileAndLevel;
69 ASDCP::MPEG2_VDesc_to_MD(MPEG2::VideoDescriptor&, MXF::MPEG2VideoDescriptor*)
77 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
83 SampleRate: %lu/%lu\n\
87 AspectRatio: %lu/%lu\n\
88 ComponentDepth: %lu\n\
89 HorizontalSubsampling: %lu\n\
90 VerticalSubsampling: %lu\n\
92 CodedContentType: %lu\n\
95 ProfileAndLevel: %lu\n\
96 ContainerDuration: %lu\n",
97 VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
101 VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
102 VDesc.ComponentDepth,
103 VDesc.HorizontalSubsampling,
104 VDesc.VerticalSubsampling,
106 VDesc.CodedContentType,
109 VDesc.ProfileAndLevel,
110 VDesc.ContainerDuration
114 //------------------------------------------------------------------------------------------
116 // hidden, internal implementation of MPEG2 reader
118 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
120 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
123 VideoDescriptor m_VDesc; // video parameter list
127 Result_t OpenRead(const char*);
128 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
129 Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
130 Result_t FindFrameGOPStart(ui32_t, ui32_t&);
137 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
139 Result_t result = OpenMXFRead(filename);
141 if( ASDCP_SUCCESS(result) )
143 InterchangeObject* Object;
144 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
147 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
151 if( ASDCP_SUCCESS(result) )
152 result = InitMXFIndex();
154 if( ASDCP_SUCCESS(result) )
155 result = InitInfo(m_Info);
164 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
165 AESDecContext* Ctx, HMACContext* HMAC)
169 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
171 if ( ASDCP_SUCCESS(result) )
172 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
181 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
185 if ( ! m_File.IsOpen() )
188 // look up frame index node
189 IndexTableSegment::IndexEntry TmpEntry;
191 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
193 DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
197 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
206 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
207 AESDecContext* Ctx, HMACContext* HMAC)
209 if ( ! m_File.IsOpen() )
212 Result_t result = ReadEKLVPacket(FrameNum, FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC);
214 if ( ASDCP_FAILURE(result) )
217 IndexTableSegment::IndexEntry TmpEntry;
218 m_FooterPart.Lookup(FrameNum, TmpEntry);
220 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
222 case 0: FrameBuf.FrameType(FRAME_I); break;
223 case 2: FrameBuf.FrameType(FRAME_P); break;
224 case 3: FrameBuf.FrameType(FRAME_B); break;
225 default: FrameBuf.FrameType(FRAME_U);
228 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
229 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
230 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
235 //------------------------------------------------------------------------------------------
240 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
245 fprintf(stream, "Frame: %06lu, %c%-2hu, %7lu bytes",
246 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
249 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
254 hexdump(m_Data, dump_len, stream);
258 //------------------------------------------------------------------------------------------
260 ASDCP::MPEG2::MXFReader::MXFReader()
262 m_Reader = new h__Reader;
266 ASDCP::MPEG2::MXFReader::~MXFReader()
270 // Open the file for reading. The file must exist. Returns error if the
271 // operation cannot be completed.
273 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
275 return m_Reader->OpenRead(filename);
280 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
281 AESDecContext* Ctx, HMACContext* HMAC) const
283 if ( m_Reader && m_Reader->m_File.IsOpen() )
284 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
292 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
293 AESDecContext* Ctx, HMACContext* HMAC) const
295 if ( m_Reader && m_Reader->m_File.IsOpen() )
296 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
304 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
306 if ( m_Reader && m_Reader->m_File.IsOpen() )
307 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
313 // Fill the struct with the values from the file's header.
314 // Returns RESULT_INIT if the file is not open.
316 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
318 if ( m_Reader && m_Reader->m_File.IsOpen() )
320 VDesc = m_Reader->m_VDesc;
328 // Fill the struct with the values from the file's header.
329 // Returns RESULT_INIT if the file is not open.
331 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
333 if ( m_Reader && m_Reader->m_File.IsOpen() )
335 Info = m_Reader->m_Info;
344 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
346 if ( m_Reader->m_File.IsOpen() )
347 m_Reader->m_HeaderPart.Dump(stream);
353 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
355 if ( m_Reader->m_File.IsOpen() )
356 m_Reader->m_FooterPart.Dump(stream);
360 //------------------------------------------------------------------------------------------
363 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
366 VideoDescriptor m_VDesc;
369 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
371 h__Writer() : m_GOPOffset(0) {}
374 Result_t OpenWrite(const char*, ui32_t HeaderSize);
375 Result_t SetSourceStream(const VideoDescriptor&);
376 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
381 // Open the file for writing. The file must not exist. Returns error if
382 // the operation cannot be completed.
384 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
386 if ( ! m_State.Test_BEGIN() )
389 Result_t result = m_File.OpenWrite(filename);
391 if ( ASDCP_SUCCESS(result) )
393 // m_EssenceDescriptor = new MDObject("MPEG2VideoDescriptor");
394 result = m_State.Goto_INIT();
400 // Automatically sets the MXF file's metadata from the MPEG stream.
402 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
404 if ( ! m_State.Test_INIT() )
408 Result_t result = RESULT_OK; // MPEG2_VDesc_to_MD(m_VDesc, *m_EssenceDescriptor);
410 if ( ASDCP_SUCCESS(result) )
411 result = WriteMXFHeader(ESS_MPEG2_VES, m_VDesc.EditRate, 24 /* TCFrameRate */);
413 if ( ASDCP_SUCCESS(result) )
414 result = m_State.Goto_READY();
419 // Writes a frame of essence to the MXF file. If the optional AESEncContext
420 // argument is present, the essence is encrypted prior to writing.
421 // Fails if the file is not open, is finalized, or an operating system
425 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
428 Result_t result = RESULT_OK;
430 if ( m_State.Test_READY() )
431 result = m_State.Goto_RUNNING(); // first time through, get the body location
433 ui64_t ThisOffset = m_StreamOffset;
435 if ( ASDCP_SUCCESS(result) )
436 result = WriteEKLVPacket(FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC);
438 if ( ASDCP_FAILURE(result) )
441 // create mxflib flags
444 switch ( FrameBuf.FrameType() )
446 case FRAME_I: Flags = 0x00; break;
447 case FRAME_P: Flags = 0x22; break;
448 case FRAME_B: Flags = 0x33; break;
451 if ( FrameBuf.GOPStart() )
456 if ( FrameBuf.ClosedGOP() )
460 // update the index manager
462 m_IndexMan->OfferEditUnit(0, m_FramesWritten, m_GOPOffset, Flags);
463 m_IndexMan->OfferTemporalOffset(m_FramesWritten, m_GOPOffset - FrameBuf.TemporalOffset());
464 m_IndexMan->OfferOffset(0, m_FramesWritten, ThisOffset);
474 // Closes the MXF file, writing the index and other closing information.
477 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
479 if ( ! m_State.Test_RUNNING() )
482 m_State.Goto_FINAL();
484 return WriteMXFFooter(ESS_MPEG2_VES);
488 //------------------------------------------------------------------------------------------
492 ASDCP::MPEG2::MXFWriter::MXFWriter()
496 ASDCP::MPEG2::MXFWriter::~MXFWriter()
501 // Open the file for writing. The file must not exist. Returns error if
502 // the operation cannot be completed.
504 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
505 const VideoDescriptor& VDesc, ui32_t HeaderSize)
507 m_Writer = new h__Writer;
509 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
511 if ( ASDCP_SUCCESS(result) )
513 m_Writer->m_Info = Info;
514 result = m_Writer->SetSourceStream(VDesc);
517 if ( ASDCP_FAILURE(result) )
524 // Writes a frame of essence to the MXF file. If the optional AESEncContext
525 // argument is present, the essence is encrypted prior to writing.
526 // Fails if the file is not open, is finalized, or an operating system
529 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
531 if ( m_Writer.empty() )
534 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
537 // Closes the MXF file, writing the index and other closing information.
539 ASDCP::MPEG2::MXFWriter::Finalize()
541 if ( m_Writer.empty() )
544 return m_Writer->Finalize();
549 // end AS_DCP_MPEG2.cpp