Fix enum switch/case statements with unhandled values.
[asdcplib-cth.git] / src / MPEG.cpp
1 /*
2 Copyright (c) 2005-2009, 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: MPEG.cpp,v 1.4 2009/04/09 19:16:49 msheby Exp $
29     \brief   MPEG2 VES parser
30 */
31
32 #include <MPEG.h>
33 #include <KM_log.h>
34 using Kumu::DefaultLogSink;
35
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
38 // of start code
39 ASDCP::Result_t
40 ASDCP::MPEG2::FindVESStartCode(const byte_t* buf, ui32_t buf_len, StartCode_t* sc, const byte_t** new_pos)
41 {
42   ASDCP_TEST_NULL(buf);
43   ASDCP_TEST_NULL(new_pos);
44
45   ui32_t zero_i = 0;
46   const byte_t* p = buf;
47   const byte_t* end_p = buf + buf_len;
48
49   for ( ; p < end_p; p++ )
50     {
51       if ( *p == 0 )
52         zero_i++;
53
54       else if ( *p == 1 && zero_i > 1 )
55         {
56           // 2 or more 0 bytes followed by a 1, start code is next
57           if ( ++p == end_p )
58             return RESULT_FAIL;
59
60           *new_pos = p - 3;
61           *sc = (StartCode_t)*p;
62           return RESULT_OK;
63         }
64       else
65         zero_i = 0;
66     }
67
68   *new_pos = buf + buf_len;
69   return  RESULT_FAIL;
70 }
71
72
73 //------------------------------------------------------------------------------------------
74
75 //
76 ASDCP::Rational
77 ASDCP::MPEG2::Accessor::Sequence::AspectRatio()
78 {
79   switch ( m_p[3] & 0xf0 )
80     {
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);
85     }
86
87   DefaultLogSink().Error("Unknown AspectRatio value: %02x\n", m_p[3]);
88   return Rational(0,0);
89 }
90
91 //------------------------------------------------------------------------------------------
92
93 enum State_t {
94     ST_IDLE,
95     ST_START_HEADER,
96     ST_IN_HEADER,
97 };
98
99
100 //
101 class ASDCP::MPEG2::VESParser::h__StreamState
102 {
103 public:
104   State_t m_State;
105   h__StreamState() : m_State(ST_IDLE) {}
106   ~h__StreamState() {}
107
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; }
114 };
115
116 //------------------------------------------------------------------------------------------
117
118
119 ASDCP::MPEG2::VESParser::VESParser() :
120   m_Delegate(0), m_HBufLen(0), m_ZeroCount(0)
121 {
122   m_State = new h__StreamState;
123 }
124
125 ASDCP::MPEG2::VESParser::~VESParser()
126 {
127 }
128
129
130 //
131 void
132 ASDCP::MPEG2::VESParser::SetDelegate(VESParserDelegate* Delegate)
133 {
134   m_Delegate = Delegate;
135 }
136
137 //
138 void
139 ASDCP::MPEG2::VESParser::Reset()
140 {
141   m_State->Goto_IDLE();
142   m_HBufLen = 0;
143   m_ZeroCount = 0;
144 }
145
146 //
147 ASDCP::Result_t
148 ASDCP::MPEG2::VESParser::Parse(const byte_t* buf, ui32_t buf_len)
149 {
150   ASDCP_TEST_NULL(buf);
151   ASDCP_TEST_NULL(m_Delegate);
152
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;
157
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++ )
161     {
162       if ( m_State->Test_IN_HEADER() )
163         {
164           assert(run_len==0);
165           m_HBuf[m_HBufLen++] = *p;
166           assert(m_HBufLen < VESHeaderBufSize);
167         }
168       else
169         {
170           run_len++;
171         }
172
173       if ( m_State->Test_START_HEADER() ) // *p is a start code
174         {
175           if ( m_HBufLen == 0) // not already collecting a header
176             {
177               m_HBuf[0] = m_HBuf[1] = 0; m_HBuf[2] = 1; m_HBuf[3] = *p;
178
179               // is this one we want?
180               if ( *p == PIC_START || *p == SEQ_START || *p == EXT_START || *p == GOP_START )
181                 {
182                   m_HBufLen = 4;
183                   m_State->Goto_IN_HEADER();
184
185                   switch ( run_len )
186                     {
187                     case 1: // we suppressed writing 001 when exiting from the last call
188                     case 4: // we have exactly 001x
189                       break;
190                     case 2: // we have 1x
191                     case 3: // we have 01x
192                       m_Delegate->Data(this, run_pos, (run_len == 2 ? -2 : -1));
193                       break;
194
195                     default:
196                       m_Delegate->Data(this, run_pos, run_len - 4);
197                     }
198
199                   run_len = 0;
200                 }
201               else
202                 {
203                   m_State->Goto_IDLE();
204
205                   if ( run_len == 1 ) // did we suppress writing 001 when exiting from the last call?
206                     {
207                       m_Delegate->Data(this, m_HBuf, 4);
208                       run_len = 0;
209                     }
210                 }
211             }
212           else // currently collecting a header, requires a flush before handling
213             {
214               m_HBufLen -= 3; // remove the current partial start code
215
216               // let the delegate handle the header
217               switch( m_HBuf[3] )
218                 {
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;
223
224                 default:
225                   DefaultLogSink().Error("Unexpected start code: %02x at byte %u\n",
226                                          m_HBuf[3], (ui32_t)(p - buf));
227                   result = RESULT_RAW_FORMAT;
228                 }
229
230               // Parser handlers return RESULT_FALSE to teriminate without error
231               if ( result != RESULT_OK )
232                 {
233                   m_State->Goto_IDLE();
234                   return result;
235                 }
236               
237               m_HBuf[0] = m_HBuf[1] = 0; m_HBuf[2] = 1; m_HBuf[3] = *p; // 001x
238               run_len = 0;
239
240               // is this a header we want?
241               if ( *p == PIC_START || *p == SEQ_START || *p == EXT_START || *p == GOP_START )
242                 {
243                   m_HBufLen = 4;
244                   m_State->Goto_IN_HEADER();
245                 }
246               else
247                 {
248                   m_HBufLen = 0;
249                   m_State->Goto_IDLE();
250
251                   if ( *p >= FIRST_SLICE && *p <= LAST_SLICE )
252                     {
253                       result = m_Delegate->Slice(this, *p);
254
255                       if ( result != RESULT_OK )
256                         return result;
257                     }
258
259                   m_Delegate->Data(this, m_HBuf, 4);
260                   run_pos = p+1;
261                 }
262             }
263         }
264       else if ( *p == 0 )
265         {
266           m_ZeroCount++;
267         }
268       else
269         {
270           if ( *p == 1 && m_ZeroCount > 1 )
271             m_State->Goto_START_HEADER();
272
273           m_ZeroCount = 0;
274         }
275     }
276
277   if ( run_len > 0 )
278     {
279       if ( m_State->Test_START_HEADER() && run_len != 0 )
280         {
281           assert(run_len > 2);
282           run_len -= 3;
283         }
284
285       // flush the current run
286       m_Delegate->Data(this, run_pos, run_len);
287     }
288
289   return RESULT_OK;
290 }
291
292
293 //
294 // end MPEG.cpp
295 //