2 Copyright (c) 2004-2011, 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
28 \version $Id: MPEG2_Parser.cpp,v 1.11 2011/08/30 17:04:25 jhurst Exp $
29 \brief AS-DCP library, MPEG2 raw essence reader implementation
32 #include <KM_fileio.h>
36 using Kumu::DefaultLogSink;
38 using namespace ASDCP;
39 using namespace ASDCP::MPEG2;
41 // data will be read from a VES file in chunks of this size
42 const ui32_t VESReadSize = 4 * Kumu::Kilobyte;
45 //------------------------------------------------------------------------------------------
58 StringParserState(ParserState_t state)
62 case ST_INIT: return "INIT";
63 case ST_SEQ: return "SEQ";
64 case ST_PIC: return "PIC";
65 case ST_GOP: return "GOP";
66 case ST_EXT: return "EXT";
67 case ST_SLICE: return "SLICE";
78 ParserState_t m_State;
79 ASDCP_NO_COPY_CONSTRUCT(h__ParserState);
82 h__ParserState() : m_State(ST_INIT) {}
85 inline bool Test_SLICE() { return m_State == ST_SLICE; }
86 inline void Reset() { m_State = ST_INIT; }
89 inline Result_t Goto_SEQ()
105 DefaultLogSink().Error("SEQ follows %s\n", StringParserState(m_State));
111 inline Result_t Goto_SLICE()
127 DefaultLogSink().Error("Slice follows %s\n", StringParserState(m_State));
133 inline Result_t Goto_PIC()
149 DefaultLogSink().Error("PIC follows %s\n", StringParserState(m_State));
155 inline Result_t Goto_GOP()
171 DefaultLogSink().Error("GOP follows %s\n", StringParserState(m_State));
176 inline Result_t Goto_EXT()
192 DefaultLogSink().Error("EXT follows %s\n", StringParserState(m_State));
197 //------------------------------------------------------------------------------------------
199 // This is a parser delagate that reads the stream params from a
200 // sequence header and sequence extension header. The parser is
201 // commanded to return after processing the sequence extension
203 class StreamParams : public VESParserDelegate
205 h__ParserState m_State;
207 ASDCP_NO_COPY_CONSTRUCT(StreamParams);
210 VideoDescriptor m_VDesc;
214 m_VDesc.ContainerDuration = 0;
215 m_VDesc.ComponentDepth = 8;
221 Result_t Sequence(VESParser*, const byte_t* b, ui32_t)
223 Result_t result = m_State.Goto_SEQ();
225 if ( ASDCP_FAILURE(result) )
228 Accessor::Sequence SEQ(b);
229 m_VDesc.AspectRatio = SEQ.AspectRatio();
230 m_VDesc.FrameRate = SEQ.FrameRate();
231 m_VDesc.StoredWidth = SEQ.HorizontalSize();
232 m_VDesc.StoredHeight = SEQ.VerticalSize();
233 m_VDesc.BitRate = SEQ.BitRate();
234 m_VDesc.EditRate = SEQ.Pulldown() ? Rational(SEQ.FrameRate() * 1000, 1001) : Rational(SEQ.FrameRate(), 1);
235 m_VDesc.SampleRate = m_VDesc.EditRate;
240 Result_t Extension(VESParser*, const byte_t* b, ui32_t)
242 Result_t result = m_State.Goto_EXT();
244 if ( ASDCP_FAILURE(result) )
247 Accessor::SequenceEx SEQX(b);
248 m_VDesc.ProfileAndLevel = SEQX.ProfileAndLevel();
249 m_VDesc.FrameLayout = SEQX.Progressive() ? 0 : 1;
250 m_VDesc.CodedContentType = SEQX.Progressive() ? 1 : 2;
251 m_VDesc.LowDelay = SEQX.LowDelay();
252 m_VDesc.HorizontalSubsampling = SEQX.ChromaFormat() == 3 ? 1 : 2;
253 m_VDesc.VerticalSubsampling = SEQX.ChromaFormat() >= 3 ? 1 : 2;
255 if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 2 ) )
256 m_VDesc.ColorSiting = 3; // 4:2:0
258 else if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 1 ) )
259 m_VDesc.ColorSiting = 4; // 4:2:2
261 else if ( ( m_VDesc.HorizontalSubsampling == 1 ) && ( m_VDesc.VerticalSubsampling == 1 ) )
262 m_VDesc.ColorSiting = 0; // 4:4:4
264 // TODO: get H&V size and bit rate extensions
269 Result_t GOP(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; }
270 Result_t Picture(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; }
271 Result_t Slice(VESParser*, byte_t) { return RESULT_FALSE; }
272 Result_t Data(VESParser*, const byte_t*, i32_t) { return RESULT_OK; }
276 //------------------------------------------------------------------------------------------
278 // This is a parser delagate that reads a VES stream and sets public
279 // instance variables to indicate state. It is used below to read an
280 // entire frame into a buffer. The delegate retains metadata about the
281 // frame for later access.
283 class FrameParser : public VESParserDelegate
285 h__ParserState m_State;
286 ASDCP_NO_COPY_CONSTRUCT(FrameParser);
290 bool m_CompletePicture;
294 ui32_t m_PlaintextOffset;
295 FrameType_t m_FrameType;
307 m_HasGOP = m_ClosedGOP = false;
308 m_CompletePicture = false;
310 m_PlaintextOffset = 0;
311 m_FrameType = FRAME_U;
315 Result_t Sequence(VESParser*, const byte_t*, ui32_t s)
317 if ( m_State.Test_SLICE() )
319 m_CompletePicture = true;
324 return m_State.Goto_SEQ();
327 Result_t Picture(VESParser*, const byte_t* b, ui32_t s)
329 if ( m_State.Test_SLICE() )
331 m_CompletePicture = true;
335 Accessor::Picture pic(b);
336 m_TemporalRef = pic.TemporalRef();
337 m_FrameType = pic.FrameType();
339 return m_State.Goto_PIC();
342 Result_t Slice(VESParser*, byte_t slice_id)
344 if ( slice_id == FIRST_SLICE )
346 m_PlaintextOffset = m_FrameSize;
347 return m_State.Goto_SLICE();
350 return m_State.Test_SLICE() ? RESULT_OK : RESULT_FAIL;
353 Result_t Extension(VESParser*, const byte_t*, ui32_t s)
356 return m_State.Goto_EXT();
359 Result_t GOP(VESParser*, const byte_t* b, ui32_t s)
361 Accessor::GOP GOP(b);
362 m_ClosedGOP = GOP.Closed();
365 return m_State.Goto_GOP();
368 Result_t Data(VESParser*, const byte_t*, i32_t s)
375 //------------------------------------------------------------------------------------------
377 // The following code assumes the following things:
378 // - each frame will begin with a picture header or a sequence header
379 // - any frame that begins with a sequence header is an I frame and is
380 // assumed to contain a GOP header, a picture header and complete picture data
381 // - any frame that begins with a picture header is either an I, B or P frame
382 // and is assumed to contain a complete picture header and picture data
384 class ASDCP::MPEG2::Parser::h__Parser
386 StreamParams m_ParamsDelegate;
387 FrameParser m_ParserDelegate;
389 Kumu::FileReader m_FileReader;
390 ui32_t m_FrameNumber;
392 ASDCP::MPEG2::FrameBuffer m_TmpBuffer;
394 ASDCP_NO_COPY_CONSTRUCT(h__Parser);
397 h__Parser() : m_TmpBuffer(VESReadSize*8) {}
398 ~h__Parser() { Close(); }
400 Result_t OpenRead(const char* filename);
403 Result_t ReadFrame(FrameBuffer&);
404 Result_t FillVideoDescriptor(VideoDescriptor&);
410 ASDCP::MPEG2::Parser::h__Parser::Reset()
414 m_FileReader.Seek(0);
415 m_ParserDelegate.Reset();
421 ASDCP::MPEG2::Parser::h__Parser::Close()
423 m_FileReader.Close();
428 ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename)
430 ASDCP_TEST_NULL_STR(filename)
431 ui32_t read_count = 0;
433 Result_t result = m_FileReader.OpenRead(filename);
435 if ( ASDCP_SUCCESS(result) )
436 result = m_FileReader.Read(m_TmpBuffer.Data(), m_TmpBuffer.Capacity(), &read_count);
438 if ( ASDCP_SUCCESS(result) )
440 const byte_t* p = m_TmpBuffer.RoData();
442 // the mxflib parser demanded the file start with a sequence header.
443 // Since no one complained and that's the easiest thing to implement,
444 // I have left it that way. Let me know if you want to be able to
445 // locate the first GOP in the stream.
447 while ( p[i] == 0 ) i++;
449 if ( i < 2 || p[i] != 1 || ! ( p[i+1] == SEQ_START || p[i+1] == PIC_START ) )
451 DefaultLogSink().Error("Frame buffer does not begin with a PIC or SEQ start code.\n");
452 return RESULT_RAW_FORMAT;
455 if ( ASDCP_SUCCESS(result) )
457 m_Parser.SetDelegate(&m_ParamsDelegate);
458 result = m_Parser.Parse(p, read_count);
462 if ( ASDCP_SUCCESS(result) )
464 ui64_t tmp = m_FileReader.Size() / 65536; // a gross approximation
465 m_ParamsDelegate.m_VDesc.ContainerDuration = (ui32_t) tmp;
466 m_Parser.SetDelegate(&m_ParserDelegate);
467 m_FileReader.Seek(0);
470 if ( ASDCP_FAILURE(result) )
472 DefaultLogSink().Error("Unable to identify a wrapping mode for the essence in file \"%s\"\n", filename);
473 m_FileReader.Close();
482 ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
484 Result_t result = RESULT_OK;
485 ui32_t write_offset = 0;
486 ui32_t read_count = 0;
491 return RESULT_ENDOFFILE;
493 // Data is read in VESReadSize chunks. Each chunk is parsed, and the
494 // process is stopped when a Sequence or Picture header is found or when
495 // the input file is exhausted. The partial next frame is cached for the
497 m_ParserDelegate.Reset();
500 if ( m_TmpBuffer.Size() > 0 )
502 memcpy(FB.Data(), m_TmpBuffer.RoData(), m_TmpBuffer.Size());
503 result = m_Parser.Parse(FB.RoData(), m_TmpBuffer.Size());
504 write_offset = m_TmpBuffer.Size();
508 while ( ! m_ParserDelegate.m_CompletePicture && result == RESULT_OK )
510 if ( FB.Capacity() < ( write_offset + VESReadSize ) )
512 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %u\n",
513 FB.Capacity(), ( write_offset + VESReadSize ));
514 return RESULT_SMALLBUF;
517 result = m_FileReader.Read(FB.Data() + write_offset, VESReadSize, &read_count);
519 if ( result == RESULT_ENDOFFILE || read_count == 0 )
523 if ( write_offset > 0 )
527 if ( ASDCP_SUCCESS(result) )
529 result = m_Parser.Parse(FB.RoData() + write_offset, read_count);
530 write_offset += read_count;
536 assert(m_ParserDelegate.m_FrameSize <= write_offset);
538 if ( ASDCP_SUCCESS(result)
539 && m_ParserDelegate.m_FrameSize < write_offset )
541 assert(m_TmpBuffer.Size() == 0);
542 ui32_t diff = write_offset - m_ParserDelegate.m_FrameSize;
543 assert(diff <= m_TmpBuffer.Capacity());
545 memcpy(m_TmpBuffer.Data(), FB.RoData() + m_ParserDelegate.m_FrameSize, diff);
546 m_TmpBuffer.Size(diff);
549 if ( ASDCP_SUCCESS(result) )
551 const byte_t* p = FB.RoData();
552 if ( p[0] != 0 || p[1] != 0 || p[2] != 1 || ! ( p[3] == SEQ_START || p[3] == PIC_START ) )
554 DefaultLogSink().Error("Frame buffer does not begin with a PIC or SEQ start code.\n");
555 return RESULT_RAW_FORMAT;
559 if ( ASDCP_SUCCESS(result) )
561 FB.Size(m_ParserDelegate.m_FrameSize);
562 FB.TemporalOffset(m_ParserDelegate.m_TemporalRef);
563 FB.FrameType(m_ParserDelegate.m_FrameType);
564 FB.PlaintextOffset(m_ParserDelegate.m_PlaintextOffset);
565 FB.FrameNumber(m_FrameNumber++);
566 FB.GOPStart(m_ParserDelegate.m_HasGOP);
567 FB.ClosedGOP(m_ParserDelegate.m_ClosedGOP);
574 // Fill a VideoDescriptor struct with the values from the file's header.
576 ASDCP::MPEG2::Parser::h__Parser::FillVideoDescriptor(VideoDescriptor& VDesc)
578 VDesc = m_ParamsDelegate.m_VDesc;
582 //------------------------------------------------------------------------------------------
584 ASDCP::MPEG2::Parser::Parser()
588 ASDCP::MPEG2::Parser::~Parser()
592 // Opens the stream for reading, parses enough data to provide a complete
593 // set of stream metadata for the MXFWriter below.
595 ASDCP::MPEG2::Parser::OpenRead(const char* filename) const
597 const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser = new h__Parser;
599 Result_t result = m_Parser->OpenRead(filename);
601 if ( ASDCP_FAILURE(result) )
602 const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser.release();
607 // Rewinds the stream to the beginning.
609 ASDCP::MPEG2::Parser::Reset() const
611 if ( m_Parser.empty() )
614 return m_Parser->Reset();
617 // Places a frame of data in the frame buffer. Fails if the buffer is too small
618 // or the stream is empty.
620 ASDCP::MPEG2::Parser::ReadFrame(FrameBuffer& FB) const
622 if ( m_Parser.empty() )
625 return m_Parser->ReadFrame(FB);
629 ASDCP::MPEG2::Parser::FillVideoDescriptor(VideoDescriptor& VDesc) const
631 if ( m_Parser.empty() )
634 return m_Parser->FillVideoDescriptor(VDesc);
638 // end MPEG2_Parser.cpp