wheee!
[asdcplib.git] / src / MPEG.cpp
1 /*
2 Copyright (c) 2005, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
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.
15
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.
26 */
27 /*! \file    MPEG.cpp
28     \version $Id$
29     \brief   MPEG2 VES parser
30 */
31
32 #include <MPEG.h>
33
34 // walk a buffer stopping at the end of the buffer or the end of a VES
35 // start code '00 00 01'.  If successful, returns address of first byte
36 // of start code
37 ASDCP::Result_t
38 ASDCP::MPEG2::FindVESStartCode(const byte_t* buf, ui32_t buf_len, StartCode_t* sc, const byte_t** new_pos)
39 {
40   ASDCP_TEST_NULL(buf);
41   ASDCP_TEST_NULL(new_pos);
42
43   ui32_t zero_i = 0;
44   const byte_t* p = buf;
45   const byte_t* end_p = buf + buf_len;
46
47   for ( ; p < end_p; p++ )
48     {
49       if ( *p == 0 )
50         zero_i++;
51
52       else if ( *p == 1 && zero_i > 1 )
53         {
54           // 2 or more 0 bytes followed by a 1, start code is next
55           if ( ++p == end_p )
56             return RESULT_FAIL;
57
58           *new_pos = p - 3;
59           *sc = (StartCode_t)*p;
60           return RESULT_OK;
61         }
62       else
63         zero_i = 0;
64     }
65
66   *new_pos = buf + buf_len;
67   return  RESULT_FAIL;
68 }
69
70
71 //------------------------------------------------------------------------------------------
72
73 //
74 ASDCP::Rational
75 ASDCP::MPEG2::Accessor::Sequence::AspectRatio()
76 {
77   switch ( m_p[3] & 0xf0 )
78     {
79     case 0x10: return Rational(1,1);
80     case 0x20: return Rational(4,3);
81     case 0x30: return Rational(16,9);
82     case 0x40: return Rational(221,100);
83     }
84
85   DefaultLogSink().Error("Unknown AspectRatio value: %02x\n", m_p[3]);
86   return Rational(0,0);
87 }
88
89 //------------------------------------------------------------------------------------------
90
91 enum State_t {
92     ST_IDLE,
93     ST_START_HEADER,
94     ST_IN_HEADER,
95 };
96
97
98 //
99 class ASDCP::MPEG2::VESParser::h__StreamState
100 {
101 public:
102   State_t m_State;
103   h__StreamState() : m_State(ST_IDLE) {}
104   ~h__StreamState() {}
105
106   void Goto_START_HEADER() {    m_State = ST_START_HEADER;  }
107   void Goto_IN_HEADER()    {    m_State = ST_IN_HEADER;  }
108   void Goto_IDLE()         {    m_State = ST_IDLE;  }
109   bool Test_IDLE()         { return m_State == ST_IDLE; }
110   bool Test_START_HEADER() { return m_State == ST_START_HEADER; }
111   bool Test_IN_HEADER()    { return m_State == ST_IN_HEADER; }
112 };
113
114 //------------------------------------------------------------------------------------------
115
116
117 ASDCP::MPEG2::VESParser::VESParser() :
118   m_Delegate(0), m_HBufLen(0), m_ZeroCount(0), m_Partial(false)
119 {
120   m_State = new h__StreamState;
121 }
122
123 ASDCP::MPEG2::VESParser::~VESParser()
124 {
125 }
126
127
128 //
129 void
130 ASDCP::MPEG2::VESParser::SetDelegate(VESParserDelegate* Delegate)
131 {
132   m_Delegate = Delegate;
133 }
134
135 //
136 void
137 ASDCP::MPEG2::VESParser::Reset()
138 {
139   m_State->Goto_IDLE();
140   m_HBufLen = 0;
141   m_ZeroCount = 0;
142   m_Partial = false;
143 }
144
145 //
146 ASDCP::Result_t
147 ASDCP::MPEG2::VESParser::Parse(const byte_t* buf, ui32_t buf_len)
148 {
149   ASDCP_TEST_NULL(buf);
150   ASDCP_TEST_NULL(m_Delegate);
151
152   Result_t result;
153   const byte_t* end_p = buf + buf_len;
154   const byte_t* run_pos = buf; // track runs of uninteresting data as a position and count
155   ui32_t  run_len = 0;
156
157   // search for MPEG2 headers
158   // copy interesting data to a buffer and pass to delegate for processing
159   for ( const byte_t* p = buf; p < end_p; p++ )
160     {
161       if ( m_State->Test_IN_HEADER() )
162         {
163           m_HBuf[m_HBufLen++] = *p;
164           assert(m_HBufLen < VESHeaderBufSize);
165         }
166       else
167         {
168           run_len++;
169         }
170
171       if ( m_State->Test_START_HEADER() ) // *p is a start code
172         {
173           // Do we already have a header? We need to flush it...
174           if ( m_HBufLen > 0)
175             {
176               m_HBufLen -= 3; // remove the current partial start code
177
178               // let the delegate handle the header
179               switch( m_HBuf[3] )
180                 {
181                 case PIC_START:   result = m_Delegate->Picture(this, m_HBuf, m_HBufLen);   break;         
182                 case EXT_START:   result = m_Delegate->Extension(this, m_HBuf, m_HBufLen); break;
183                 case SEQ_START:   result = m_Delegate->Sequence(this, m_HBuf, m_HBufLen);  break;
184                 case GOP_START:   result = m_Delegate->GOP(this, m_HBuf, m_HBufLen);       break;
185
186                 default:
187                   DefaultLogSink().Error("Unexpected start code: %02x at byte %lu\n",
188                                          m_HBuf[3], (ui32_t)(p - buf));
189                   result = RESULT_RAW_FORMAT;
190                 }
191
192               // the next run starts with the start code that got us here
193               run_len = 4;
194               run_pos = p-3;
195               m_HBufLen = 0;
196
197               // Parser handlers return RESULT_FALSE to teriminate without error
198               if ( result != RESULT_OK )
199                 {
200                   m_State->Goto_IDLE();
201                   return result;
202                 }
203             }
204
205           // all headers start with this same start code: 00 00 01 xx
206           m_HBuf[0] = m_HBuf[1] = 0; m_HBuf[2] = 1; m_HBuf[3] = *p;
207
208           // is this a header we want?
209           if ( *p == PIC_START || *p == SEQ_START || *p == EXT_START || *p == GOP_START )
210             {
211               // we're starting a new header, flush the current run
212               if ( run_len > 4 )
213                 {
214                   m_Delegate->Data(this, run_pos, run_len - 4);
215                   run_len = 0;
216                 }
217
218               m_Partial = false;
219               m_HBufLen = 4;
220               m_State->Goto_IN_HEADER();
221             }
222           else
223             {
224               if ( *p == FIRST_SLICE )
225                 result = m_Delegate->Slice(this);
226
227               if ( m_Partial )
228                 {
229                   m_Partial = false;
230                   m_Delegate->Data(this, m_HBuf, 3);
231                 }
232               
233               m_State->Goto_IDLE();
234             }
235         }
236       else if ( *p == 0 )
237         {
238           m_ZeroCount++;
239         }
240       else
241         {
242           if ( *p == 1 && m_ZeroCount > 1 )
243             m_State->Goto_START_HEADER();
244
245           m_ZeroCount = 0;
246         }
247     }
248
249   if ( run_len > 0 )
250     {
251       if ( m_State->Test_START_HEADER() )
252         {
253           m_Partial = true; // 'partial' means we have a partial header in progress
254           run_len -= 3;
255         }
256
257       // flush the current run
258       m_Delegate->Data(this, run_pos, run_len);
259     }
260
261   return RESULT_OK;
262 }
263
264
265 //
266 // end MPEG.cpp
267 //