2 Copyright (c) 2004, 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 MPEG2_Parser.cpp
29 \brief AS-DCP library, MPEG2 raw essence reader implementation
35 using namespace ASDCP;
36 using namespace ASDCP::MPEG2;
38 // data will be read from a VES file in chunks of this size
39 const ui32_t VESReadSize = 4096;
42 //------------------------------------------------------------------------------------------
58 ParserState_t m_State;
59 ASDCP_NO_COPY_CONSTRUCT(h__ParserState);
62 h__ParserState() : m_State(::ST_INIT) {}
65 bool Test_SLICE() { return m_State == ST_SLICE; }
66 void Reset() { m_State = ST_INIT; }
69 inline Result_t Goto_SEQ()
83 inline Result_t Goto_SLICE()
98 inline Result_t Goto_PIC()
115 inline Result_t Goto_GOP()
129 inline Result_t Goto_EXT()
145 //------------------------------------------------------------------------------------------
147 // This is a parser delagate that reads the stream params from a
148 // sequence header and sequence extension header. The parser is
149 // commanded to return after processing the sequence extension
151 class StreamParams : public VESParserDelegate
153 h__ParserState m_State;
155 ASDCP_NO_COPY_CONSTRUCT(StreamParams);
158 VideoDescriptor m_VDesc;
162 m_VDesc.ContainerDuration = 0;
163 m_VDesc.ComponentDepth = 8;
169 Result_t Sequence(VESParser*, const byte_t* b, ui32_t s)
171 Result_t result = m_State.Goto_SEQ();
173 if ( ASDCP_FAILURE(result) )
176 Accessor::Sequence SEQ(b);
177 m_VDesc.AspectRatio = SEQ.AspectRatio();
178 m_VDesc.FrameRate = SEQ.FrameRate();
179 m_VDesc.StoredWidth = SEQ.HorizontalSize();
180 m_VDesc.StoredHeight = SEQ.VerticalSize();
181 m_VDesc.BitRate = SEQ.BitRate();
182 m_VDesc.EditRate = SEQ.Pulldown() ? Rational(SEQ.FrameRate() * 1000, 1001) : Rational(SEQ.FrameRate(), 1);
183 m_VDesc.SampleRate = m_VDesc.EditRate;
188 Result_t Extension(VESParser*, const byte_t* b, ui32_t s)
190 Result_t result = m_State.Goto_EXT();
192 if ( ASDCP_FAILURE(result) )
195 Accessor::SequenceEx SEQX(b);
196 m_VDesc.ProfileAndLevel = SEQX.ProfileAndLevel();
197 m_VDesc.FrameLayout = SEQX.Progressive() ? 0 : 1;
198 m_VDesc.CodedContentType = SEQX.Progressive() ? 1 : 2;
199 m_VDesc.LowDelay = SEQX.LowDelay();
200 m_VDesc.HorizontalSubsampling = SEQX.ChromaFormat() == 3 ? 1 : 2;
201 m_VDesc.VerticalSubsampling = SEQX.ChromaFormat() >= 3 ? 1 : 2;
203 if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 2 ) )
204 m_VDesc.ColorSiting = 3; // 4:2:0
206 else if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 1 ) )
207 m_VDesc.ColorSiting = 4; // 4:2:2
209 else if ( ( m_VDesc.HorizontalSubsampling == 1 ) && ( m_VDesc.VerticalSubsampling == 1 ) )
210 m_VDesc.ColorSiting = 0; // 4:4:4
212 // TODO: get H&V size and bit rate extensions
217 Result_t GOP(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; }
218 Result_t Picture(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; }
219 Result_t Slice(VESParser*) { return RESULT_FALSE; }
220 Result_t Data(VESParser*, const byte_t*, ui32_t) { return RESULT_OK; }
224 //------------------------------------------------------------------------------------------
226 // This is a parser delagate that reads a VES stream and sets public
227 // instance variables to indicate state. It is used below to read an
228 // entire frame into a buffer. The delegate retains metadata about the
229 // frame for later access.
231 class FrameParser : public VESParserDelegate
233 h__ParserState m_State;
234 ASDCP_NO_COPY_CONSTRUCT(FrameParser);
238 bool m_CompletePicture;
242 ui32_t m_PlaintextOffset;
243 FrameType_t m_FrameType;
255 m_HasGOP = m_ClosedGOP = false;
256 m_CompletePicture = false;
258 m_PlaintextOffset = 0;
259 m_FrameType = FRAME_U;
263 Result_t Sequence(VESParser*, const byte_t* b, ui32_t s)
265 if ( m_State.Test_SLICE() )
267 m_CompletePicture = true;
272 return m_State.Goto_SEQ();
275 Result_t Picture(VESParser*, const byte_t* b, ui32_t s)
277 if ( m_State.Test_SLICE() )
279 m_CompletePicture = true;
283 Accessor::Picture PIC(b);
284 m_TemporalRef = PIC.TemporalRef();
285 m_FrameType = PIC.FrameType();
287 return m_State.Goto_PIC();
290 Result_t Slice(VESParser*)
292 m_PlaintextOffset = m_FrameSize;
293 return m_State.Goto_SLICE();
296 Result_t Extension(VESParser*, const byte_t* b, ui32_t s)
299 return m_State.Goto_EXT();
302 Result_t GOP(VESParser*, const byte_t* b, ui32_t s)
304 Accessor::GOP GOP(b);
305 m_ClosedGOP = GOP.Closed();
308 return m_State.Goto_GOP();
311 Result_t Data(VESParser*, const byte_t* b, ui32_t s)
318 //------------------------------------------------------------------------------------------
320 // The following code assumes the following things:
321 // - each frame will begin with a picture header or a sequence header
322 // - any frame that begins with a sequence header is an I frame and is
323 // assumed to contain a GOP header, a picture header and complete picture data
324 // - any frame that begins with a picture header is either an I, B or P frame
325 // and is assumed to contain a complete picture header and picture data
328 class ASDCP::MPEG2::Parser::h__Parser
330 StreamParams m_ParamsDelegate;
331 FrameParser m_ParserDelegate;
333 FileReader m_FileReader;
334 ui32_t m_FrameNumber;
336 ASDCP::MPEG2::FrameBuffer m_TmpBuffer;
338 ASDCP_NO_COPY_CONSTRUCT(h__Parser);
341 h__Parser() : m_TmpBuffer(VESReadSize*2) {}
342 ~h__Parser() { Close(); }
344 Result_t OpenRead(const char* filename);
347 Result_t ReadFrame(FrameBuffer&);
348 Result_t FillVideoDescriptor(VideoDescriptor&);
354 ASDCP::MPEG2::Parser::h__Parser::Reset()
358 m_FileReader.Seek(0);
359 m_ParserDelegate.Reset();
365 ASDCP::MPEG2::Parser::h__Parser::Close()
367 m_FileReader.Close();
372 ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename)
374 ASDCP_TEST_NULL_STR(filename)
375 ui32_t read_count = 0;
377 Result_t result = m_FileReader.OpenRead(filename);
379 if ( ASDCP_SUCCESS(result) )
380 result = m_FileReader.Read(m_TmpBuffer.Data(), m_TmpBuffer.Capacity(), &read_count);
382 if ( ASDCP_SUCCESS(result) )
384 const byte_t* p = m_TmpBuffer.RoData();
386 // the mxflib parser demanded the file start with a sequence header.
387 // Since no one complained and that's the easiest thing to implement,
388 // I have left it that way. Let me know if you want to be able to
389 // locate the first GOP in the stream.
390 if ( p[0] != 0 || p[1] != 0 || p[2] != 1 )
392 DefaultLogSink().Error("Frame buffer does not begin with a start code.\n");
393 return RESULT_RAW_FORMAT;
396 if ( ASDCP_SUCCESS(result) )
398 m_Parser.SetDelegate(&m_ParamsDelegate);
399 result = m_Parser.Parse(p, read_count);
403 if ( ASDCP_SUCCESS(result) )
405 m_Parser.SetDelegate(&m_ParserDelegate);
406 m_FileReader.Seek(0);
409 if ( ASDCP_FAILURE(result) )
411 DefaultLogSink().Error("Unable to identify a wrapping mode for the essence in file \"%s\"\n", filename);
412 m_FileReader.Close();
421 ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
423 Result_t result = RESULT_OK;
424 ui32_t write_offset = 0;
425 ui32_t read_count = 0;
430 return RESULT_ENDOFFILE;
432 // Data is read in VESReadSize chunks. Each chunk is parsed, and the
433 // process is stopped when a Sequence or Picture header is found or when
434 // the input file is exhausted. The partial next frame is cached for the
436 m_ParserDelegate.Reset();
438 if ( m_TmpBuffer.Size() > 0 )
440 memcpy(FB.Data(), m_TmpBuffer.RoData(), m_TmpBuffer.Size());
441 result = m_Parser.Parse(FB.RoData(), m_TmpBuffer.Size());
442 write_offset = m_TmpBuffer.Size();
446 while ( ! m_ParserDelegate.m_CompletePicture && ASDCP_SUCCESS(result) )
448 if ( FB.Capacity() < ( write_offset + VESReadSize ) )
450 DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %lu\n",
451 FB.Capacity(), ( write_offset + VESReadSize ));
452 return RESULT_SMALLBUF;
455 result = m_FileReader.Read(FB.Data() + write_offset, VESReadSize, &read_count);
457 if ( result == RESULT_ENDOFFILE )
461 if ( write_offset > 0 )
465 if ( ASDCP_SUCCESS(result) )
467 result = m_Parser.Parse(FB.RoData() + write_offset, read_count);
468 write_offset += read_count;
475 if ( ASDCP_SUCCESS(result)
476 && m_ParserDelegate.m_FrameSize < write_offset )
478 assert(m_TmpBuffer.Size() == 0);
479 ui32_t diff = write_offset - m_ParserDelegate.m_FrameSize;
480 assert(diff <= m_TmpBuffer.Capacity());
481 memcpy(m_TmpBuffer.Data(), FB.RoData() + m_ParserDelegate.m_FrameSize, diff);
482 m_TmpBuffer.Size(diff);
486 if ( ASDCP_SUCCESS(result) )
488 const byte_t* p = FB.RoData();
489 if ( p[0] != 0 || p[1] != 0 || p[2] != 1 )
491 DefaultLogSink().Error("Parsed frame buffer does not begin with a start code.\n");
492 return RESULT_RAW_FORMAT;
497 if ( ASDCP_SUCCESS(result) )
499 FB.Size(m_ParserDelegate.m_FrameSize);
500 FB.TemporalOffset(m_ParserDelegate.m_TemporalRef);
501 FB.FrameType(m_ParserDelegate.m_FrameType);
502 FB.PlaintextOffset(m_ParserDelegate.m_PlaintextOffset);
503 FB.FrameNumber(m_FrameNumber++);
504 FB.GOPStart(m_ParserDelegate.m_HasGOP);
505 FB.ClosedGOP(m_ParserDelegate.m_ClosedGOP);
512 // Fill a VideoDescriptor struct with the values from the file's header.
514 ASDCP::MPEG2::Parser::h__Parser::FillVideoDescriptor(VideoDescriptor& VDesc)
516 VDesc = m_ParamsDelegate.m_VDesc;
520 //------------------------------------------------------------------------------------------
522 ASDCP::MPEG2::Parser::Parser()
526 ASDCP::MPEG2::Parser::~Parser()
530 // Opens the stream for reading, parses enough data to provide a complete
531 // set of stream metadata for the MXFWriter below.
533 ASDCP::MPEG2::Parser::OpenRead(const char* filename) const
535 const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser = new h__Parser;
537 Result_t result = m_Parser->OpenRead(filename);
539 if ( ASDCP_FAILURE(result) )
540 const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser.release();
545 // Rewinds the stream to the beginning.
547 ASDCP::MPEG2::Parser::Reset() const
549 if ( m_Parser.empty() )
552 return m_Parser->Reset();
555 // Places a frame of data in the frame buffer. Fails if the buffer is too small
556 // or the stream is empty.
558 ASDCP::MPEG2::Parser::ReadFrame(FrameBuffer& FB) const
560 if ( m_Parser.empty() )
563 return m_Parser->ReadFrame(FB);
567 ASDCP::MPEG2::Parser::FillVideoDescriptor(VideoDescriptor& VDesc) const
569 if ( m_Parser.empty() )
572 return m_Parser->FillVideoDescriptor(VDesc);
576 // end AS_DCP_MPEG2_Parser.cpp