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 //------------------------------------------------------------------------------------------
39 ASDCP::MD_to_MPEG2_VDesc(MXF::MPEG2VideoDescriptor* VDescObj, MPEG2::VideoDescriptor& VDesc)
41 ASDCP_TEST_NULL(VDescObj);
43 VDesc.SampleRate = VDescObj->SampleRate;
44 VDesc.EditRate = VDescObj->SampleRate;
45 VDesc.FrameRate = VDescObj->SampleRate.Numerator;
46 VDesc.ContainerDuration = VDescObj->ContainerDuration;
48 VDesc.FrameLayout = VDescObj->FrameLayout;
49 VDesc.StoredWidth = VDescObj->StoredWidth;
50 VDesc.StoredHeight = VDescObj->StoredHeight;
51 VDesc.AspectRatio = VDescObj->AspectRatio;
53 VDesc.ComponentDepth = VDescObj->ComponentDepth;
54 VDesc.HorizontalSubsampling = VDescObj->HorizontalSubsampling;
55 VDesc.VerticalSubsampling = VDescObj->VerticalSubsampling;
56 VDesc.ColorSiting = VDescObj->ColorSiting;
57 VDesc.CodedContentType = VDescObj->CodedContentType;
59 VDesc.LowDelay = VDescObj->LowDelay == 0 ? false : true;
60 VDesc.BitRate = VDescObj->BitRate;
61 VDesc.ProfileAndLevel = VDescObj->ProfileAndLevel;
68 ASDCP::MPEG2_VDesc_to_MD(MPEG2::VideoDescriptor& VDesc, MXF::MPEG2VideoDescriptor* VDescObj)
70 ASDCP_TEST_NULL(VDescObj);
72 VDescObj->SampleRate = VDesc.SampleRate;
73 VDescObj->SampleRate.Numerator = VDesc.FrameRate;
74 VDescObj->ContainerDuration = VDesc.ContainerDuration;
76 VDescObj->FrameLayout = VDesc.FrameLayout;
77 VDescObj->StoredWidth = VDesc.StoredWidth;
78 VDescObj->StoredHeight = VDesc.StoredHeight;
79 VDescObj->AspectRatio = VDesc.AspectRatio;
81 VDescObj->ComponentDepth = VDesc.ComponentDepth;
82 VDescObj->HorizontalSubsampling = VDesc.HorizontalSubsampling;
83 VDescObj->VerticalSubsampling = VDesc.VerticalSubsampling;
84 VDescObj->ColorSiting = VDesc.ColorSiting;
85 VDescObj->CodedContentType = VDesc.CodedContentType;
87 VDescObj->LowDelay = VDesc.LowDelay ? 1 : 0;
88 VDescObj->BitRate = VDesc.BitRate;
89 VDescObj->ProfileAndLevel = VDesc.ProfileAndLevel;
96 ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
102 SampleRate: %lu/%lu\n\
106 AspectRatio: %lu/%lu\n\
107 ComponentDepth: %lu\n\
108 HorizontalSubsampling: %lu\n\
109 VerticalSubsampling: %lu\n\
111 CodedContentType: %lu\n\
114 ProfileAndLevel: %lu\n\
115 ContainerDuration: %lu\n",
116 VDesc.SampleRate.Numerator ,VDesc.SampleRate.Denominator,
120 VDesc.AspectRatio.Numerator ,VDesc.AspectRatio.Denominator,
121 VDesc.ComponentDepth,
122 VDesc.HorizontalSubsampling,
123 VDesc.VerticalSubsampling,
125 VDesc.CodedContentType,
128 VDesc.ProfileAndLevel,
129 VDesc.ContainerDuration
133 //------------------------------------------------------------------------------------------
135 // hidden, internal implementation of MPEG2 reader
137 class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
139 ASDCP_NO_COPY_CONSTRUCT(h__Reader);
142 VideoDescriptor m_VDesc; // video parameter list
146 Result_t OpenRead(const char*);
147 Result_t ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
148 Result_t ReadFrameGOPStart(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
149 Result_t FindFrameGOPStart(ui32_t, ui32_t&);
156 ASDCP::MPEG2::MXFReader::h__Reader::OpenRead(const char* filename)
158 Result_t result = OpenMXFRead(filename);
160 if( ASDCP_SUCCESS(result) )
162 InterchangeObject* Object;
163 if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(MPEG2VideoDescriptor), &Object)) )
166 result = MD_to_MPEG2_VDesc((MXF::MPEG2VideoDescriptor*)Object, m_VDesc);
170 if( ASDCP_SUCCESS(result) )
171 result = InitMXFIndex();
173 if( ASDCP_SUCCESS(result) )
174 result = InitInfo(m_Info);
183 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
184 AESDecContext* Ctx, HMACContext* HMAC)
188 Result_t result = FindFrameGOPStart(FrameNum, KeyFrameNum);
190 if ( ASDCP_SUCCESS(result) )
191 result = ReadFrame(KeyFrameNum, FrameBuf, Ctx, HMAC);
200 ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum)
204 if ( ! m_File.IsOpen() )
207 // look up frame index node
208 IndexTableSegment::IndexEntry TmpEntry;
210 if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
212 DefaultLogSink().Error("Frame value out of range: %lu\n", FrameNum);
216 KeyFrameNum = FrameNum - TmpEntry.KeyFrameOffset;
225 ASDCP::MPEG2::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
226 AESDecContext* Ctx, HMACContext* HMAC)
228 if ( ! m_File.IsOpen() )
231 Result_t result = ReadEKLVPacket(FrameNum, FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC);
233 if ( ASDCP_FAILURE(result) )
236 IndexTableSegment::IndexEntry TmpEntry;
237 m_FooterPart.Lookup(FrameNum, TmpEntry);
239 switch ( ( TmpEntry.Flags >> 4 ) & 0x03 )
241 case 0: FrameBuf.FrameType(FRAME_I); break;
242 case 2: FrameBuf.FrameType(FRAME_P); break;
243 case 3: FrameBuf.FrameType(FRAME_B); break;
244 default: FrameBuf.FrameType(FRAME_U);
247 FrameBuf.TemporalOffset(TmpEntry.TemporalOffset);
248 FrameBuf.GOPStart(TmpEntry.Flags & 0x40 ? true : false);
249 FrameBuf.ClosedGOP(TmpEntry.Flags & 0x80 ? true : false);
254 //------------------------------------------------------------------------------------------
259 ASDCP::MPEG2::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
264 fprintf(stream, "Frame: %06lu, %c%-2hu, %7lu bytes",
265 m_FrameNumber, FrameTypeChar(m_FrameType), m_TemporalOffset, m_Size);
268 fprintf(stream, " (start %s GOP)", ( m_ClosedGOP ? "closed" : "open"));
273 hexdump(m_Data, dump_len, stream);
277 //------------------------------------------------------------------------------------------
279 ASDCP::MPEG2::MXFReader::MXFReader()
281 m_Reader = new h__Reader;
285 ASDCP::MPEG2::MXFReader::~MXFReader()
289 // Open the file for reading. The file must exist. Returns error if the
290 // operation cannot be completed.
292 ASDCP::MPEG2::MXFReader::OpenRead(const char* filename) const
294 return m_Reader->OpenRead(filename);
299 ASDCP::MPEG2::MXFReader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameBuf,
300 AESDecContext* Ctx, HMACContext* HMAC) const
302 if ( m_Reader && m_Reader->m_File.IsOpen() )
303 return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
311 ASDCP::MPEG2::MXFReader::ReadFrameGOPStart(ui32_t FrameNum, FrameBuffer& FrameBuf,
312 AESDecContext* Ctx, HMACContext* HMAC) const
314 if ( m_Reader && m_Reader->m_File.IsOpen() )
315 return m_Reader->ReadFrameGOPStart(FrameNum, FrameBuf, Ctx, HMAC);
323 ASDCP::MPEG2::MXFReader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& KeyFrameNum) const
325 if ( m_Reader && m_Reader->m_File.IsOpen() )
326 return m_Reader->FindFrameGOPStart(FrameNum, KeyFrameNum);
332 // Fill the struct with the values from the file's header.
333 // Returns RESULT_INIT if the file is not open.
335 ASDCP::MPEG2::MXFReader::FillVideoDescriptor(VideoDescriptor& VDesc) const
337 if ( m_Reader && m_Reader->m_File.IsOpen() )
339 VDesc = m_Reader->m_VDesc;
347 // Fill the struct with the values from the file's header.
348 // Returns RESULT_INIT if the file is not open.
350 ASDCP::MPEG2::MXFReader::FillWriterInfo(WriterInfo& Info) const
352 if ( m_Reader && m_Reader->m_File.IsOpen() )
354 Info = m_Reader->m_Info;
363 ASDCP::MPEG2::MXFReader::DumpHeaderMetadata(FILE* stream) const
365 if ( m_Reader->m_File.IsOpen() )
366 m_Reader->m_HeaderPart.Dump(stream);
372 ASDCP::MPEG2::MXFReader::DumpIndex(FILE* stream) const
374 if ( m_Reader->m_File.IsOpen() )
375 m_Reader->m_FooterPart.Dump(stream);
379 //------------------------------------------------------------------------------------------
382 class ASDCP::MPEG2::MXFWriter::h__Writer : public ASDCP::h__Writer
385 VideoDescriptor m_VDesc;
388 ASDCP_NO_COPY_CONSTRUCT(h__Writer);
390 h__Writer() : m_GOPOffset(0) {}
393 Result_t OpenWrite(const char*, ui32_t HeaderSize);
394 Result_t SetSourceStream(const VideoDescriptor&);
395 Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
400 // Open the file for writing. The file must not exist. Returns error if
401 // the operation cannot be completed.
403 ASDCP::MPEG2::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
405 if ( ! m_State.Test_BEGIN() )
408 Result_t result = m_File.OpenWrite(filename);
410 if ( ASDCP_SUCCESS(result) )
412 m_EssenceDescriptor = new MPEG2VideoDescriptor;
413 result = m_State.Goto_INIT();
419 // Automatically sets the MXF file's metadata from the MPEG stream.
421 ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc)
423 if ( ! m_State.Test_INIT() )
427 Result_t result = MPEG2_VDesc_to_MD(m_VDesc, (MPEG2VideoDescriptor*)m_EssenceDescriptor);
429 if ( ASDCP_SUCCESS(result) )
430 result = WriteMXFHeader(MPEG_PACKAGE_LABEL,
431 UL(WrappingUL_Data_MPEG2_VES),
432 m_VDesc.EditRate, 24 /* TCFrameRate */);
434 if ( ASDCP_SUCCESS(result) )
435 result = m_State.Goto_READY();
440 // Writes a frame of essence to the MXF file. If the optional AESEncContext
441 // argument is present, the essence is encrypted prior to writing.
442 // Fails if the file is not open, is finalized, or an operating system
446 ASDCP::MPEG2::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
449 Result_t result = RESULT_OK;
451 if ( m_State.Test_READY() )
452 result = m_State.Goto_RUNNING(); // first time through, get the body location
454 ui64_t ThisOffset = m_File.Tell();
456 if ( ASDCP_SUCCESS(result) )
457 result = WriteEKLVPacket(FrameBuf, MPEGEssenceUL_Data, Ctx, HMAC);
459 if ( ASDCP_FAILURE(result) )
462 // create mxflib flags
465 switch ( FrameBuf.FrameType() )
467 case FRAME_I: Flags = 0x00; break;
468 case FRAME_P: Flags = 0x22; break;
469 case FRAME_B: Flags = 0x33; break;
472 if ( FrameBuf.GOPStart() )
477 if ( FrameBuf.ClosedGOP() )
481 // update the index manager
482 IndexTableSegment::IndexEntry Entry;
483 Entry.TemporalOffset = - FrameBuf.TemporalOffset();
484 Entry.KeyFrameOffset = m_GOPOffset;
486 Entry.StreamOffset = ThisOffset - m_FooterPart.m_ECOffset;
487 m_FooterPart.PushIndexEntry(Entry);
495 // Closes the MXF file, writing the index and other closing information.
498 ASDCP::MPEG2::MXFWriter::h__Writer::Finalize()
500 if ( ! m_State.Test_RUNNING() )
503 m_State.Goto_FINAL();
505 return WriteMXFFooter();
509 //------------------------------------------------------------------------------------------
513 ASDCP::MPEG2::MXFWriter::MXFWriter()
517 ASDCP::MPEG2::MXFWriter::~MXFWriter()
522 // Open the file for writing. The file must not exist. Returns error if
523 // the operation cannot be completed.
525 ASDCP::MPEG2::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
526 const VideoDescriptor& VDesc, ui32_t HeaderSize)
528 m_Writer = new h__Writer;
530 Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
532 if ( ASDCP_SUCCESS(result) )
534 m_Writer->m_Info = Info;
535 result = m_Writer->SetSourceStream(VDesc);
538 if ( ASDCP_FAILURE(result) )
545 // Writes a frame of essence to the MXF file. If the optional AESEncContext
546 // argument is present, the essence is encrypted prior to writing.
547 // Fails if the file is not open, is finalized, or an operating system
550 ASDCP::MPEG2::MXFWriter::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
552 if ( m_Writer.empty() )
555 return m_Writer->WriteFrame(FrameBuf, Ctx, HMAC);
558 // Closes the MXF file, writing the index and other closing information.
560 ASDCP::MPEG2::MXFWriter::Finalize()
562 if ( m_Writer.empty() )
565 return m_Writer->Finalize();
570 // end AS_DCP_MPEG2.cpp