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.12 2014/01/02 23:29:22 jhurst Exp $
29 \brief AS-DCP library, MPEG2 raw essence reader implementation
32 #include <asdcp/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()
101 DefaultLogSink().Error("SEQ follows %s\n", StringParserState(m_State));
107 inline Result_t Goto_SLICE()
119 DefaultLogSink().Error("Slice follows %s\n", StringParserState(m_State));
125 inline Result_t Goto_PIC()
139 DefaultLogSink().Error("PIC follows %s\n", StringParserState(m_State));
145 inline Result_t Goto_GOP()
157 DefaultLogSink().Error("GOP follows %s\n", StringParserState(m_State));
162 inline Result_t Goto_EXT()
176 DefaultLogSink().Error("EXT follows %s\n", StringParserState(m_State));
181 //------------------------------------------------------------------------------------------
183 // This is a parser delagate that reads the stream params from a
184 // sequence header and sequence extension header. The parser is
185 // commanded to return after processing the sequence extension
187 class StreamParams : public VESParserDelegate
189 h__ParserState m_State;
191 ASDCP_NO_COPY_CONSTRUCT(StreamParams);
194 VideoDescriptor m_VDesc;
198 m_VDesc.ContainerDuration = 0;
199 m_VDesc.ComponentDepth = 8;
205 Result_t Sequence(VESParser*, const byte_t* b, ui32_t)
207 Result_t result = m_State.Goto_SEQ();
209 if ( ASDCP_FAILURE(result) )
212 Accessor::Sequence SEQ(b);
213 m_VDesc.AspectRatio = SEQ.AspectRatio();
214 m_VDesc.FrameRate = SEQ.FrameRate();
215 m_VDesc.StoredWidth = SEQ.HorizontalSize();
216 m_VDesc.StoredHeight = SEQ.VerticalSize();
217 m_VDesc.BitRate = SEQ.BitRate();
218 m_VDesc.EditRate = SEQ.Pulldown() ? Rational(SEQ.FrameRate() * 1000, 1001) : Rational(SEQ.FrameRate(), 1);
219 m_VDesc.SampleRate = m_VDesc.EditRate;
224 Result_t Extension(VESParser*, const byte_t* b, ui32_t)
226 Result_t result = m_State.Goto_EXT();
228 if ( ASDCP_FAILURE(result) )
231 Accessor::SequenceEx SEQX(b);
232 m_VDesc.ProfileAndLevel = SEQX.ProfileAndLevel();
233 m_VDesc.FrameLayout = SEQX.Progressive() ? 0 : 1;
234 m_VDesc.CodedContentType = SEQX.Progressive() ? 1 : 2;
235 m_VDesc.LowDelay = SEQX.LowDelay();
236 m_VDesc.HorizontalSubsampling = SEQX.ChromaFormat() == 3 ? 1 : 2;
237 m_VDesc.VerticalSubsampling = SEQX.ChromaFormat() >= 3 ? 1 : 2;
239 if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 2 ) )
240 m_VDesc.ColorSiting = 3; // 4:2:0
242 else if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 1 ) )
243 m_VDesc.ColorSiting = 4; // 4:2:2
245 else if ( ( m_VDesc.HorizontalSubsampling == 1 ) && ( m_VDesc.VerticalSubsampling == 1 ) )
246 m_VDesc.ColorSiting = 0; // 4:4:4
248 // TODO: get H&V size and bit rate extensions
253 Result_t GOP(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; }
254 Result_t Picture(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; }
255 Result_t Slice(VESParser*, byte_t) { return RESULT_FALSE; }
256 Result_t Data(VESParser*, const byte_t*, i32_t) { return RESULT_OK; }
260 //------------------------------------------------------------------------------------------
262 // This is a parser delagate that reads a VES stream and sets public
263 // instance variables to indicate state. It is used below to read an
264 // entire frame into a buffer. The delegate retains metadata about the
265 // frame for later access.
267 class FrameParser : public VESParserDelegate
269 h__ParserState m_State;
270 ASDCP_NO_COPY_CONSTRUCT(FrameParser);
274 bool m_CompletePicture;
278 ui32_t m_PlaintextOffset;
279 FrameType_t m_FrameType;
291 m_HasGOP = m_ClosedGOP = false;
292 m_CompletePicture = false;
294 m_PlaintextOffset = 0;
295 m_FrameType = FRAME_U;
299 Result_t Sequence(VESParser*, const byte_t*, ui32_t s)
301 if ( m_State.Test_SLICE() )
303 m_CompletePicture = true;
308 return m_State.Goto_SEQ();
311 Result_t Picture(VESParser*, const byte_t* b, ui32_t s)
313 if ( m_State.Test_SLICE() )
315 m_CompletePicture = true;
319 Accessor::Picture pic(b);
320 m_TemporalRef = pic.TemporalRef();
321 m_FrameType = pic.FrameType();
323 return m_State.Goto_PIC();
326 Result_t Slice(VESParser*, byte_t slice_id)
328 if ( slice_id == FIRST_SLICE )
330 m_PlaintextOffset = m_FrameSize;
331 return m_State.Goto_SLICE();
334 return m_State.Test_SLICE() ? RESULT_OK : RESULT_FAIL;
337 Result_t Extension(VESParser*, const byte_t*, ui32_t s)
340 return m_State.Goto_EXT();
343 Result_t GOP(VESParser*, const byte_t* b, ui32_t s)
345 Accessor::GOP GOP(b);
346 m_ClosedGOP = GOP.Closed();
349 return m_State.Goto_GOP();
352 Result_t Data(VESParser*, const byte_t*, i32_t s)
359 //------------------------------------------------------------------------------------------
361 // The following code assumes the following things:
362 // - each frame will begin with a picture header or a sequence header
363 // - any frame that begins with a sequence header is an I frame and is
364 // assumed to contain a GOP header, a picture header and complete picture data
365 // - any frame that begins with a picture header is either an I, B or P frame
366 // and is assumed to contain a complete picture header and picture data
368 class ASDCP::MPEG2::Parser::h__Parser
370 StreamParams m_ParamsDelegate;
371 FrameParser m_ParserDelegate;
373 Kumu::FileReader m_FileReader;
374 ui32_t m_FrameNumber;
376 ASDCP::MPEG2::FrameBuffer m_TmpBuffer;
378 ASDCP_NO_COPY_CONSTRUCT(h__Parser);
381 h__Parser() : m_TmpBuffer(VESReadSize*8) {}
382 ~h__Parser() { Close(); }
384 Result_t OpenRead(const std::string& filename);
387 Result_t ReadFrame(FrameBuffer&);
388 Result_t FillVideoDescriptor(VideoDescriptor&);
394 ASDCP::MPEG2::Parser::h__Parser::Reset()
398 m_FileReader.Seek(0);
399 m_ParserDelegate.Reset();
405 ASDCP::MPEG2::Parser::h__Parser::Close()
407 m_FileReader.Close();
412 ASDCP::MPEG2::Parser::h__Parser::OpenRead(const std::string& filename)
414 ui32_t read_count = 0;
416 Result_t result = m_FileReader.OpenRead(filename);
418 if ( ASDCP_SUCCESS(result) )
419 result = m_FileReader.Read(m_TmpBuffer.Data(), m_TmpBuffer.Capacity(), &read_count);
421 if ( ASDCP_SUCCESS(result) )
423 const byte_t* p = m_TmpBuffer.RoData();
425 // the mxflib parser demanded the file start with a sequence header.
426 // Since no one complained and that's the easiest thing to implement,
427 // I have left it that way. Let me know if you want to be able to
428 // locate the first GOP in the stream.
430 while ( p[i] == 0 ) i++;
432 if ( i < 2 || p[i] != 1 || ! ( p[i+1] == SEQ_START || p[i+1] == PIC_START ) )
434 DefaultLogSink().Error("Frame buffer does not begin with a PIC or SEQ start code.\n");
435 return RESULT_RAW_FORMAT;
438 if ( ASDCP_SUCCESS(result) )
440 m_Parser.SetDelegate(&m_ParamsDelegate);
441 result = m_Parser.Parse(p, read_count);
445 if ( ASDCP_SUCCESS(result) )
447 ui64_t tmp = m_FileReader.Size() / 65536; // a gross approximation
448 m_ParamsDelegate.m_VDesc.ContainerDuration = (ui32_t) tmp;
449 m_Parser.SetDelegate(&m_ParserDelegate);
450 m_FileReader.Seek(0);
453 if ( ASDCP_FAILURE(result) )
455 DefaultLogSink().Error("Unable to identify a wrapping mode for the essence in file \"%s\"\n", filename.c_str());
456 m_FileReader.Close();
465 ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
467 Result_t result = RESULT_OK;
468 ui32_t write_offset = 0;
469 ui32_t read_count = 0;
474 return RESULT_ENDOFFILE;
476 // Data is read in VESReadSize chunks. Each chunk is parsed, and the
477 // process is stopped when a Sequence or Picture header is found or when
478 // the input file is exhausted. The partial next frame is cached for the
480 m_ParserDelegate.Reset();
483 if ( m_TmpBuffer.Size() > 0 )
485 memcpy(FB.Data(), m_TmpBuffer.RoData(), m_TmpBuffer.Size());
486 result = m_Parser.Parse(FB.RoData(), m_TmpBuffer.Size());
487 write_offset = m_TmpBuffer.Size();
491 while ( ! m_ParserDelegate.m_CompletePicture && result == RESULT_OK )
493 if ( FB.Capacity() < ( write_offset + VESReadSize ) )
495 DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %u\n",
496 FB.Capacity(), ( write_offset + VESReadSize ));
497 return RESULT_SMALLBUF;
500 result = m_FileReader.Read(FB.Data() + write_offset, VESReadSize, &read_count);
502 if ( result == RESULT_ENDOFFILE || read_count == 0 )
506 if ( write_offset > 0 )
510 if ( ASDCP_SUCCESS(result) )
512 result = m_Parser.Parse(FB.RoData() + write_offset, read_count);
513 write_offset += read_count;
519 assert(m_ParserDelegate.m_FrameSize <= write_offset);
521 if ( ASDCP_SUCCESS(result)
522 && m_ParserDelegate.m_FrameSize < write_offset )
524 assert(m_TmpBuffer.Size() == 0);
525 ui32_t diff = write_offset - m_ParserDelegate.m_FrameSize;
526 assert(diff <= m_TmpBuffer.Capacity());
528 memcpy(m_TmpBuffer.Data(), FB.RoData() + m_ParserDelegate.m_FrameSize, diff);
529 m_TmpBuffer.Size(diff);
532 if ( ASDCP_SUCCESS(result) )
534 const byte_t* p = FB.RoData();
535 if ( p[0] != 0 || p[1] != 0 || p[2] != 1 || ! ( p[3] == SEQ_START || p[3] == PIC_START ) )
537 DefaultLogSink().Error("Frame buffer does not begin with a PIC or SEQ start code.\n");
538 return RESULT_RAW_FORMAT;
542 if ( ASDCP_SUCCESS(result) )
544 FB.Size(m_ParserDelegate.m_FrameSize);
545 FB.TemporalOffset(m_ParserDelegate.m_TemporalRef);
546 FB.FrameType(m_ParserDelegate.m_FrameType);
547 FB.PlaintextOffset(m_ParserDelegate.m_PlaintextOffset);
548 FB.FrameNumber(m_FrameNumber++);
549 FB.GOPStart(m_ParserDelegate.m_HasGOP);
550 FB.ClosedGOP(m_ParserDelegate.m_ClosedGOP);
557 // Fill a VideoDescriptor struct with the values from the file's header.
559 ASDCP::MPEG2::Parser::h__Parser::FillVideoDescriptor(VideoDescriptor& VDesc)
561 VDesc = m_ParamsDelegate.m_VDesc;
565 //------------------------------------------------------------------------------------------
567 ASDCP::MPEG2::Parser::Parser()
571 ASDCP::MPEG2::Parser::~Parser()
575 // Opens the stream for reading, parses enough data to provide a complete
576 // set of stream metadata for the MXFWriter below.
578 ASDCP::MPEG2::Parser::OpenRead(const std::string& filename) const
580 const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser = new h__Parser;
582 Result_t result = m_Parser->OpenRead(filename);
584 if ( ASDCP_FAILURE(result) )
585 const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser.release();
590 // Rewinds the stream to the beginning.
592 ASDCP::MPEG2::Parser::Reset() const
594 if ( m_Parser.empty() )
597 return m_Parser->Reset();
600 // Places a frame of data in the frame buffer. Fails if the buffer is too small
601 // or the stream is empty.
603 ASDCP::MPEG2::Parser::ReadFrame(FrameBuffer& FB) const
605 if ( m_Parser.empty() )
608 return m_Parser->ReadFrame(FB);
612 ASDCP::MPEG2::Parser::FillVideoDescriptor(VideoDescriptor& VDesc) const
614 if ( m_Parser.empty() )
617 return m_Parser->FillVideoDescriptor(VDesc);
621 // end MPEG2_Parser.cpp