2 Copyright (c) 2005-2009, 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.
28 \version $Id: MPEG.cpp,v 1.4 2009/04/09 19:16:49 msheby Exp $
29 \brief MPEG2 VES parser
34 using Kumu::DefaultLogSink;
36 // walk a buffer stopping at the end of the buffer or the end of a VES
37 // start code '00 00 01'. If successful, returns address of first byte
40 ASDCP::MPEG2::FindVESStartCode(const byte_t* buf, ui32_t buf_len, StartCode_t* sc, const byte_t** new_pos)
43 ASDCP_TEST_NULL(new_pos);
46 const byte_t* p = buf;
47 const byte_t* end_p = buf + buf_len;
49 for ( ; p < end_p; p++ )
54 else if ( *p == 1 && zero_i > 1 )
56 // 2 or more 0 bytes followed by a 1, start code is next
61 *sc = (StartCode_t)*p;
68 *new_pos = buf + buf_len;
73 //------------------------------------------------------------------------------------------
77 ASDCP::MPEG2::Accessor::Sequence::AspectRatio()
79 switch ( m_p[3] & 0xf0 )
81 case 0x10: return Rational(1,1);
82 case 0x20: return Rational(4,3);
83 case 0x30: return Rational(16,9);
84 case 0x40: return Rational(221,100);
87 DefaultLogSink().Error("Unknown AspectRatio value: %02x\n", m_p[3]);
91 //------------------------------------------------------------------------------------------
101 class ASDCP::MPEG2::VESParser::h__StreamState
105 h__StreamState() : m_State(ST_IDLE) {}
108 void Goto_START_HEADER() { m_State = ST_START_HEADER; }
109 void Goto_IN_HEADER() { m_State = ST_IN_HEADER; }
110 void Goto_IDLE() { m_State = ST_IDLE; }
111 bool Test_IDLE() { return m_State == ST_IDLE; }
112 bool Test_START_HEADER() { return m_State == ST_START_HEADER; }
113 bool Test_IN_HEADER() { return m_State == ST_IN_HEADER; }
116 //------------------------------------------------------------------------------------------
119 ASDCP::MPEG2::VESParser::VESParser() :
120 m_Delegate(0), m_HBufLen(0), m_ZeroCount(0)
122 m_State = new h__StreamState;
125 ASDCP::MPEG2::VESParser::~VESParser()
132 ASDCP::MPEG2::VESParser::SetDelegate(VESParserDelegate* Delegate)
134 m_Delegate = Delegate;
139 ASDCP::MPEG2::VESParser::Reset()
141 m_State->Goto_IDLE();
148 ASDCP::MPEG2::VESParser::Parse(const byte_t* buf, ui32_t buf_len)
150 ASDCP_TEST_NULL(buf);
151 ASDCP_TEST_NULL(m_Delegate);
153 Result_t result = RESULT_OK;
154 register const byte_t* end_p = buf + buf_len;
155 register const byte_t* run_pos = buf; // track runs of uninteresting data using a position and count
156 register ui32_t run_len = 0;
158 // search for MPEG2 headers
159 // copy interesting data to a buffer and pass to delegate for processing
160 for ( register const byte_t* p = buf; p < end_p; p++ )
162 if ( m_State->Test_IN_HEADER() )
165 m_HBuf[m_HBufLen++] = *p;
166 assert(m_HBufLen < VESHeaderBufSize);
173 if ( m_State->Test_START_HEADER() ) // *p is a start code
175 if ( m_HBufLen == 0) // not already collecting a header
177 m_HBuf[0] = m_HBuf[1] = 0; m_HBuf[2] = 1; m_HBuf[3] = *p;
179 // is this one we want?
180 if ( *p == PIC_START || *p == SEQ_START || *p == EXT_START || *p == GOP_START )
183 m_State->Goto_IN_HEADER();
187 case 1: // we suppressed writing 001 when exiting from the last call
188 case 4: // we have exactly 001x
190 case 2: // we have 1x
191 case 3: // we have 01x
192 m_Delegate->Data(this, run_pos, (run_len == 2 ? -2 : -1));
196 m_Delegate->Data(this, run_pos, run_len - 4);
203 m_State->Goto_IDLE();
205 if ( run_len == 1 ) // did we suppress writing 001 when exiting from the last call?
207 m_Delegate->Data(this, m_HBuf, 4);
212 else // currently collecting a header, requires a flush before handling
214 m_HBufLen -= 3; // remove the current partial start code
216 // let the delegate handle the header
219 case PIC_START: result = m_Delegate->Picture(this, m_HBuf, m_HBufLen); break;
220 case EXT_START: result = m_Delegate->Extension(this, m_HBuf, m_HBufLen); break;
221 case SEQ_START: result = m_Delegate->Sequence(this, m_HBuf, m_HBufLen); break;
222 case GOP_START: result = m_Delegate->GOP(this, m_HBuf, m_HBufLen); break;
225 DefaultLogSink().Error("Unexpected start code: %02x at byte %u\n",
226 m_HBuf[3], (ui32_t)(p - buf));
227 result = RESULT_RAW_FORMAT;
230 // Parser handlers return RESULT_FALSE to teriminate without error
231 if ( result != RESULT_OK )
233 m_State->Goto_IDLE();
237 m_HBuf[0] = m_HBuf[1] = 0; m_HBuf[2] = 1; m_HBuf[3] = *p; // 001x
240 // is this a header we want?
241 if ( *p == PIC_START || *p == SEQ_START || *p == EXT_START || *p == GOP_START )
244 m_State->Goto_IN_HEADER();
249 m_State->Goto_IDLE();
251 if ( *p >= FIRST_SLICE && *p <= LAST_SLICE )
253 result = m_Delegate->Slice(this, *p);
255 if ( result != RESULT_OK )
259 m_Delegate->Data(this, m_HBuf, 4);
270 if ( *p == 1 && m_ZeroCount > 1 )
271 m_State->Goto_START_HEADER();
279 if ( m_State->Test_START_HEADER() && run_len != 0 )
285 // flush the current run
286 m_Delegate->Data(this, run_pos, run_len);