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);
43 VDesc = *((MPEG2::VideoDescriptor*)VDescObj);
51 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
57 SampleRate: %lu/%lu\n\
61 AspectRatio: %lu/%lu\n\
62 ComponentDepth: %lu\n\
63 HorizontalSubsampling: %lu\n\
64 VerticalSubsampling: %lu\n\
66 CodedContentType: %lu\n\
69 ProfileAndLevel: %lu\n\
70 ContainerDuration: %lu\n",
71 VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
75 VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
77 VDesc.HorizontalSubsampling,
78 VDesc.VerticalSubsampling,
80 VDesc.CodedContentType,
83 VDesc.ProfileAndLevel,
84 VDesc.ContainerDuration
88 //------------------------------------------------------------------------------------------
90 // hidden, internal implementation of MPEG2 reader
92 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
94 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
97 VideoDescriptor m_VDesc; // video parameter list
101 Result_t OpenRead(const char*);
102 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
103 Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
104 Result_t FindFrameGOPStart(ui32_t, ui32_t&);
111 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
113 Result_t result = OpenMXFRead(filename);
115 if( ASDCP_SUCCESS(result) )
117 InterchangeObject* Object;
118 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
121 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
125 if( ASDCP_SUCCESS(result) )
126 result = InitMXFIndex();
128 if( ASDCP_SUCCESS(result) )
129 result = InitInfo(m_Info);
138 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
139 AESDecContext* Ctx, HMACContext* HMAC)
143 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
145 if ( ASDCP_SUCCESS(result) )
146 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
155 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
159 if ( ! m_File.IsOpen() )
162 // look up frame index node
163 IndexTableSegment::IndexEntry TmpEntry;
165 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
167 DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
171 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
180 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
181 AESDecContext* Ctx, HMACContext* HMAC)
183 if ( ! m_File.IsOpen() )
186 Result_t result = ReadEKLVPacket(FrameNum, FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC);
188 if ( ASDCP_FAILURE(result) )
191 IndexTableSegment::IndexEntry TmpEntry;
192 m_FooterPart.Lookup(FrameNum, TmpEntry);
194 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
196 case 0: FrameBuf.FrameType(FRAME_I); break;
197 case 2: FrameBuf.FrameType(FRAME_P); break;
198 case 3: FrameBuf.FrameType(FRAME_B); break;
199 default: FrameBuf.FrameType(FRAME_U);
202 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
203 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
204 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
209 //------------------------------------------------------------------------------------------
214 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
219 fprintf(stream, "Frame: %06lu, %c%-2hu, %7lu bytes",
220 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
223 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
228 hexdump(m_Data, dump_len, stream);
232 //------------------------------------------------------------------------------------------
234 ASDCP::MPEG2::MXFReader::MXFReader()
236 m_Reader = new h__Reader;
240 ASDCP::MPEG2::MXFReader::~MXFReader()
244 // Open the file for reading. The file must exist. Returns error if the
245 // operation cannot be completed.
247 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
249 return m_Reader->OpenRead(filename);
254 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
255 AESDecContext* Ctx, HMACContext* HMAC) const
257 if ( m_Reader && m_Reader->m_File.IsOpen() )
258 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
266 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
267 AESDecContext* Ctx, HMACContext* HMAC) const
269 if ( m_Reader && m_Reader->m_File.IsOpen() )
270 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
278 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
280 if ( m_Reader && m_Reader->m_File.IsOpen() )
281 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
287 // Fill the struct with the values from the file's header.
288 // Returns RESULT_INIT if the file is not open.
290 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
292 if ( m_Reader && m_Reader->m_File.IsOpen() )
294 VDesc = m_Reader->m_VDesc;
302 // Fill the struct with the values from the file's header.
303 // Returns RESULT_INIT if the file is not open.
305 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
307 if ( m_Reader && m_Reader->m_File.IsOpen() )
309 Info = m_Reader->m_Info;
318 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
320 if ( m_Reader->m_File.IsOpen() )
321 m_Reader->m_HeaderPart.Dump(stream);
327 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
329 if ( m_Reader->m_File.IsOpen() )
330 m_Reader->m_FooterPart.Dump(stream);
334 //------------------------------------------------------------------------------------------
339 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
342 VideoDescriptor m_VDesc;
345 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
347 h__Writer() : m_GOPOffset(0) {}
350 Result_t OpenWrite(const char*, ui32_t HeaderSize);
351 Result_t SetSourceStream(const VideoDescriptor&);
352 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
357 // Open the file for writing. The file must not exist. Returns error if
358 // the operation cannot be completed.
360 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
362 if ( ! m_State.Test_BEGIN() )
365 m_File = new MXFFile;
367 Result_t result = m_File->OpenWrite(filename);
369 if ( ASDCP_SUCCESS(result) )
371 m_EssenceDescriptor = new MDObject("MPEG2VideoDescriptor");
372 result = m_State.Goto_INIT();
378 // Automatically sets the MXF file's metadata from the MPEG stream.
380 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
382 if ( ! m_State.Test_INIT() )
386 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, *m_EssenceDescriptor);
388 if ( ASDCP_SUCCESS(result) )
389 result = WriteMXFHeader(ESS_MPEG2_VES, m_VDesc.EditRate, 24 /* TCFrameRate */);
391 if ( ASDCP_SUCCESS(result) )
392 result = m_State.Goto_READY();
397 // Writes a frame of essence to the MXF file. If the optional AESEncContext
398 // argument is present, the essence is encrypted prior to writing.
399 // Fails if the file is not open, is finalized, or an operating system
403 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
406 Result_t result = RESULT_OK;
408 if ( m_State.Test_READY() )
409 result = m_State.Goto_RUNNING(); // first time through, get the body location
411 ui64_t ThisOffset = m_StreamOffset;
413 if ( ASDCP_SUCCESS(result) )
414 result = WriteEKLVPacket(FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC);
416 if ( ASDCP_FAILURE(result) )
419 // create mxflib flags
422 switch ( FrameBuf.FrameType() )
424 case FRAME_I: Flags = 0x00; break;
425 case FRAME_P: Flags = 0x22; break;
426 case FRAME_B: Flags = 0x33; break;
429 if ( FrameBuf.GOPStart() )
434 if ( FrameBuf.ClosedGOP() )
438 // update the index manager
439 m_IndexMan->OfferEditUnit(0, m_FramesWritten, m_GOPOffset, Flags);
440 m_IndexMan->OfferTemporalOffset(m_FramesWritten, m_GOPOffset - FrameBuf.TemporalOffset());
441 m_IndexMan->OfferOffset(0, m_FramesWritten, ThisOffset);
450 // Closes the MXF file, writing the index and other closing information.
453 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
455 if ( ! m_State.Test_RUNNING() )
461 m_State.Goto_FINAL();
463 return WriteMXFFooter(ESS_MPEG2_VES);
467 //------------------------------------------------------------------------------------------
471 ASDCP::MPEG2::MXFWriter::MXFWriter()
475 ASDCP::MPEG2::MXFWriter::~MXFWriter()
480 // Open the file for writing. The file must not exist. Returns error if
481 // the operation cannot be completed.
483 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
484 const VideoDescriptor& VDesc, ui32_t HeaderSize)
486 m_Writer = new h__Writer;
488 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
490 if ( ASDCP_SUCCESS(result) )
492 m_Writer->m_Info = Info;
493 result = m_Writer->SetSourceStream(VDesc);
496 if ( ASDCP_FAILURE(result) )
503 // Writes a frame of essence to the MXF file. If the optional AESEncContext
504 // argument is present, the essence is encrypted prior to writing.
505 // Fails if the file is not open, is finalized, or an operating system
508 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
510 if ( m_Writer.empty() )
513 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
516 // Closes the MXF file, writing the index and other closing information.
518 ASDCP::MPEG2::MXFWriter::Finalize()
520 if ( m_Writer.empty() )
523 return m_Writer->Finalize();
528 // end AS_DCP_MPEG2.cpp