wheee!
[asdcplib.git] / src / MPEG2_Parser.cpp
1 /*
2 Copyright (c) 2004, 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    MPEG2_Parser.cpp
28     \version $Id$
29     \brief   AS-DCP library, MPEG2 raw essence reader implementation
30 */
31
32 #include <FileIO.h>
33 #include <MPEG.h>
34
35 using namespace ASDCP;
36 using namespace ASDCP::MPEG2;
37
38 // data will be read from a VES file in chunks of this size
39 const ui32_t VESReadSize = 4096;
40
41
42 //------------------------------------------------------------------------------------------
43
44 //
45 enum ParserState_t {
46     ST_INIT,
47     ST_SEQ,
48     ST_PIC,
49     ST_GOP,
50     ST_EXT,
51     ST_SLICE,
52 };
53
54
55 //
56 class h__ParserState
57 {
58   ParserState_t m_State;
59   ASDCP_NO_COPY_CONSTRUCT(h__ParserState);
60
61  public:
62   h__ParserState() : m_State(::ST_INIT) {}
63   ~h__ParserState() {}
64
65   bool Test_SLICE() { return m_State == ST_SLICE; }
66   void Reset() { m_State = ST_INIT; }
67
68   //
69   inline Result_t Goto_SEQ()
70     {
71       switch ( m_State )
72         {
73         case ST_INIT:
74           m_State = ST_SEQ;
75           return RESULT_OK;
76         }
77       
78       return RESULT_STATE;
79     }
80
81
82   //
83   inline Result_t Goto_SLICE()
84     {
85       switch ( m_State )
86         {
87         case ST_PIC:
88         case ST_EXT:
89           m_State = ST_SLICE;
90           return RESULT_OK;
91         }
92       
93       return RESULT_STATE;
94     }
95
96
97   //
98   inline Result_t Goto_PIC()
99     {
100       switch ( m_State )
101         {
102         case ST_INIT:
103         case ST_SEQ:
104         case ST_GOP:
105         case ST_EXT:
106           m_State = ST_PIC;
107           return RESULT_OK;
108         }
109       
110       return RESULT_STATE;
111     }
112
113
114   //
115   inline Result_t Goto_GOP()
116     {
117       switch ( m_State )
118         {
119         case ST_EXT:
120         case ST_SEQ:
121           m_State = ST_GOP;
122           return RESULT_OK;
123         }
124       
125       return RESULT_STATE;
126     }
127
128   //
129   inline Result_t Goto_EXT()
130   {
131     switch ( m_State )
132       {
133         case ST_PIC:
134         case ST_EXT:
135         case ST_SEQ:
136         case ST_GOP:
137           m_State = ST_EXT;
138           return RESULT_OK;
139       }
140
141     return RESULT_STATE;
142   }
143 };
144
145 //------------------------------------------------------------------------------------------
146
147 // This is a parser delagate that reads the stream params from a
148 // sequence header and sequence extension header. The parser is
149 // commanded to return after processing the sequence extension
150 // header.
151 class StreamParams : public VESParserDelegate
152 {
153   h__ParserState m_State;
154
155   ASDCP_NO_COPY_CONSTRUCT(StreamParams);
156
157 public:
158   VideoDescriptor  m_VDesc;
159
160   StreamParams()
161   {
162     m_VDesc.ContainerDuration = 0;
163     m_VDesc.ComponentDepth = 8;
164   }
165
166   ~StreamParams() {}
167
168   //
169   Result_t Sequence(VESParser*, const byte_t* b, ui32_t s)
170   {
171     Result_t result = m_State.Goto_SEQ();
172
173     if ( ASDCP_FAILURE(result) )
174       return result;
175
176     Accessor::Sequence SEQ(b);
177     m_VDesc.AspectRatio = SEQ.AspectRatio();
178     m_VDesc.FrameRate = SEQ.FrameRate();
179     m_VDesc.StoredWidth = SEQ.HorizontalSize();
180     m_VDesc.StoredHeight = SEQ.VerticalSize();
181     m_VDesc.BitRate = SEQ.BitRate();
182     m_VDesc.EditRate = SEQ.Pulldown() ? Rational(SEQ.FrameRate() * 1000, 1001) : Rational(SEQ.FrameRate(), 1);
183     m_VDesc.SampleRate = m_VDesc.EditRate;
184     return RESULT_OK;
185   }
186
187   //
188   Result_t Extension(VESParser*, const byte_t* b, ui32_t s)
189   {
190     Result_t result = m_State.Goto_EXT();
191
192     if ( ASDCP_FAILURE(result) )
193       return result;
194
195     Accessor::SequenceEx SEQX(b);
196     m_VDesc.ProfileAndLevel = SEQX.ProfileAndLevel();
197     m_VDesc.FrameLayout = SEQX.Progressive() ? 0 : 1;
198     m_VDesc.CodedContentType = SEQX.Progressive() ? 1 : 2;
199     m_VDesc.LowDelay = SEQX.LowDelay();
200     m_VDesc.HorizontalSubsampling = SEQX.ChromaFormat() == 3 ? 1 : 2;
201     m_VDesc.VerticalSubsampling = SEQX.ChromaFormat() >= 3 ? 1 : 2;
202
203     if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 2 ) )
204       m_VDesc.ColorSiting = 3;  // 4:2:0
205
206     else if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 1 ) )
207       m_VDesc.ColorSiting = 4;  // 4:2:2
208
209     else if ( ( m_VDesc.HorizontalSubsampling == 1 ) && ( m_VDesc.VerticalSubsampling == 1 ) )
210       m_VDesc.ColorSiting = 0;  // 4:4:4
211
212     // TODO: get H&V size and bit rate extensions
213
214     return RESULT_FALSE;
215   }
216
217   Result_t GOP(VESParser*, const byte_t*, ui32_t)     { return RESULT_FALSE; }
218   Result_t Picture(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; }
219   Result_t Slice(VESParser*)                          { return RESULT_FALSE; }
220   Result_t Data(VESParser*, const byte_t*, ui32_t)    { return RESULT_OK; }
221 };
222
223
224 //------------------------------------------------------------------------------------------
225
226 // This is a parser delagate that reads a VES stream and sets public
227 // instance variables to indicate state. It is used below to read an
228 // entire frame into a buffer. The delegate retains metadata about the
229 // frame for later access.
230 //
231 class FrameParser : public VESParserDelegate
232 {
233   h__ParserState             m_State;
234   ASDCP_NO_COPY_CONSTRUCT(FrameParser);
235
236 public:
237   ui32_t       m_FrameSize;
238   bool         m_CompletePicture;
239   bool         m_HasGOP;
240   bool         m_ClosedGOP;
241   ui8_t        m_TemporalRef;
242   ui32_t       m_PlaintextOffset;
243   FrameType_t  m_FrameType;
244
245   FrameParser()
246   {
247     Reset();
248   }
249
250   ~FrameParser() {}
251   
252   void Reset()
253   {
254     m_FrameSize = 0;
255     m_HasGOP = m_ClosedGOP = false;
256     m_CompletePicture = false;
257     m_TemporalRef = 0;
258     m_PlaintextOffset = 0;
259     m_FrameType = FRAME_U;
260     m_State.Reset();
261   }
262
263   Result_t Sequence(VESParser*, const byte_t* b, ui32_t s)
264   {
265     if ( m_State.Test_SLICE() )
266       {
267         m_CompletePicture = true;
268         return RESULT_FALSE;
269       }
270
271     m_FrameSize += s;
272     return m_State.Goto_SEQ();
273   }
274
275   Result_t Picture(VESParser*, const byte_t* b, ui32_t s)
276   {
277     if ( m_State.Test_SLICE() )
278       {
279         m_CompletePicture = true;
280         return RESULT_FALSE;
281       }
282
283     Accessor::Picture PIC(b);
284     m_TemporalRef = PIC.TemporalRef();
285     m_FrameType = PIC.FrameType();
286     m_FrameSize += s;
287     return m_State.Goto_PIC();
288   }
289
290   Result_t Slice(VESParser*)
291   {
292     m_PlaintextOffset = m_FrameSize;
293     return m_State.Goto_SLICE();
294   }
295
296   Result_t Extension(VESParser*, const byte_t* b, ui32_t s)
297   {
298     m_FrameSize += s;
299     return m_State.Goto_EXT();
300   }
301
302   Result_t GOP(VESParser*, const byte_t* b, ui32_t s)
303   {
304     Accessor::GOP GOP(b);
305     m_ClosedGOP = GOP.Closed();
306     m_HasGOP = true;
307     m_FrameSize += s;
308     return m_State.Goto_GOP();
309   }
310
311   Result_t Data(VESParser*, const byte_t* b, ui32_t s)
312   {
313     m_FrameSize += s;
314     return RESULT_OK;
315   }
316 };
317
318 //------------------------------------------------------------------------------------------
319
320 // The following code assumes the following things:
321 // - each frame will begin with a picture header or a sequence header
322 // - any frame that begins with a sequence header is an I frame and is
323 //   assumed to contain a GOP header, a picture header and complete picture data
324 // - any frame that begins with a picture header is either an I, B or P frame
325 //   and is assumed to contain a complete picture header and picture data
326
327
328 class ASDCP::MPEG2::Parser::h__Parser
329 {
330   StreamParams     m_ParamsDelegate;
331   FrameParser      m_ParserDelegate;
332   VESParser        m_Parser;
333   FileReader       m_FileReader;
334   ui32_t           m_FrameNumber;
335   bool             m_EOF;
336   ASDCP::MPEG2::FrameBuffer  m_TmpBuffer;
337
338   ASDCP_NO_COPY_CONSTRUCT(h__Parser);
339
340 public:
341   h__Parser() : m_TmpBuffer(VESReadSize*2) {}
342   ~h__Parser() { Close(); }
343
344   Result_t OpenRead(const char* filename);
345   void     Close();
346   Result_t Reset();
347   Result_t ReadFrame(FrameBuffer&);
348   Result_t FillVideoDescriptor(VideoDescriptor&);
349 };
350
351
352 //
353 Result_t
354 ASDCP::MPEG2::Parser::h__Parser::Reset()
355 {
356   m_FrameNumber = 0;
357   m_EOF = false;
358   m_FileReader.Seek(0);
359   m_ParserDelegate.Reset();
360   return RESULT_OK;
361 }
362
363 //
364 void
365 ASDCP::MPEG2::Parser::h__Parser::Close()
366 {
367   m_FileReader.Close();
368 }
369
370 //
371 ASDCP::Result_t
372 ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename)
373 {
374   ASDCP_TEST_NULL_STR(filename)
375   ui32_t read_count = 0;
376
377   Result_t result = m_FileReader.OpenRead(filename);
378   
379   if ( ASDCP_SUCCESS(result) )
380     result = m_FileReader.Read(m_TmpBuffer.Data(), m_TmpBuffer.Capacity(), &read_count);
381   
382   if ( ASDCP_SUCCESS(result) )
383     {
384       const byte_t* p = m_TmpBuffer.RoData();
385
386       // the mxflib parser demanded the file start with a sequence header.
387       // Since no one complained and that's the easiest thing to implement,
388       // I have left it that way. Let me know if you want to be able to
389       // locate the first GOP in the stream.
390       if ( p[0] != 0 || p[1] != 0 || p[2] != 1 )
391         {
392           DefaultLogSink().Error("Frame buffer does not begin with a start code.\n");
393           return RESULT_RAW_FORMAT;
394         }
395
396       if ( ASDCP_SUCCESS(result) )
397         {
398           m_Parser.SetDelegate(&m_ParamsDelegate);
399           result = m_Parser.Parse(p, read_count);
400         }
401     }
402
403   if ( ASDCP_SUCCESS(result) )
404     {
405       m_Parser.SetDelegate(&m_ParserDelegate);
406       m_FileReader.Seek(0);
407     }
408
409   if ( ASDCP_FAILURE(result) )
410     {
411       DefaultLogSink().Error("Unable to identify a wrapping mode for the essence in file \"%s\"\n", filename);
412       m_FileReader.Close();
413     }
414
415   return result;
416 }
417
418 //
419 //
420 ASDCP::Result_t
421 ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
422 {
423   Result_t result = RESULT_OK;
424   ui32_t write_offset = 0;
425   ui32_t read_count = 0;
426
427   FB.Size(0);
428
429   if ( m_EOF )
430     return RESULT_ENDOFFILE;
431
432   // Data is read in VESReadSize chunks. Each chunk is parsed, and the
433   // process is stopped when a Sequence or Picture header is found or when
434   // the input file is exhausted. The partial next frame is cached for the
435   // next call.
436   m_ParserDelegate.Reset();
437
438   if ( m_TmpBuffer.Size() > 0 )
439     {
440       memcpy(FB.Data(), m_TmpBuffer.RoData(), m_TmpBuffer.Size());
441       result = m_Parser.Parse(FB.RoData(), m_TmpBuffer.Size());
442       write_offset = m_TmpBuffer.Size();
443       m_TmpBuffer.Size(0);
444     }
445
446   while ( ! m_ParserDelegate.m_CompletePicture && ASDCP_SUCCESS(result) )
447     {
448       if ( FB.Capacity() < ( write_offset + VESReadSize ) )
449         {
450           DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %lu\n",
451                                  FB.Capacity(), ( write_offset + VESReadSize ));
452           return RESULT_SMALLBUF;
453         }
454
455       result = m_FileReader.Read(FB.Data() + write_offset, VESReadSize, &read_count);
456
457       if ( result == RESULT_ENDOFFILE )
458         {
459           m_EOF = true;
460
461           if ( write_offset > 0 )
462             result = RESULT_OK;
463         }
464
465       if ( ASDCP_SUCCESS(result) )
466         {
467           result = m_Parser.Parse(FB.RoData() + write_offset, read_count);
468           write_offset += read_count;
469         }
470
471       if ( m_EOF )
472         break;
473     }
474
475   if ( ASDCP_SUCCESS(result)
476        && m_ParserDelegate.m_FrameSize < write_offset )
477     {
478       assert(m_TmpBuffer.Size() == 0);
479       ui32_t diff = write_offset - m_ParserDelegate.m_FrameSize;
480       assert(diff <= m_TmpBuffer.Capacity());
481       memcpy(m_TmpBuffer.Data(), FB.RoData() + m_ParserDelegate.m_FrameSize, diff);
482       m_TmpBuffer.Size(diff);
483     }
484
485 #ifndef NDEBUG
486   if ( ASDCP_SUCCESS(result) )
487     {
488       const byte_t* p = FB.RoData();
489       if ( p[0] != 0 || p[1] != 0 || p[2] != 1 )
490         {
491           DefaultLogSink().Error("Parsed frame buffer does not begin with a start code.\n");
492           return RESULT_RAW_FORMAT;
493         }
494     }
495 #endif
496
497   if ( ASDCP_SUCCESS(result) )
498     {
499       FB.Size(m_ParserDelegate.m_FrameSize);
500       FB.TemporalOffset(m_ParserDelegate.m_TemporalRef);
501       FB.FrameType(m_ParserDelegate.m_FrameType);
502       FB.PlaintextOffset(m_ParserDelegate.m_PlaintextOffset);
503       FB.FrameNumber(m_FrameNumber++);
504
505       if ( m_ParserDelegate.m_HasGOP )
506         {
507           FB.GOPStart(m_ParserDelegate.m_HasGOP);
508           FB.ClosedGOP(m_ParserDelegate.m_ClosedGOP);
509         }
510     }
511
512   return result;
513 }
514
515
516 // Fill a VideoDescriptor struct with the values from the file's header.
517 ASDCP::Result_t
518 ASDCP::MPEG2::Parser::h__Parser::FillVideoDescriptor(VideoDescriptor& VDesc)
519 {
520   VDesc = m_ParamsDelegate.m_VDesc;
521   return RESULT_OK;
522 }
523
524 //------------------------------------------------------------------------------------------
525
526 ASDCP::MPEG2::Parser::Parser()
527 {
528 }
529
530 ASDCP::MPEG2::Parser::~Parser()
531 {
532 }
533
534 // Opens the stream for reading, parses enough data to provide a complete
535 // set of stream metadata for the MXFWriter below.
536 ASDCP::Result_t
537 ASDCP::MPEG2::Parser::OpenRead(const char* filename) const
538 {
539   const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser = new h__Parser;
540
541   Result_t result = m_Parser->OpenRead(filename);
542
543   if ( ASDCP_FAILURE(result) )
544     const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser.release();
545
546   return result;
547 }
548
549 // Rewinds the stream to the beginning.
550 ASDCP::Result_t
551 ASDCP::MPEG2::Parser::Reset() const
552 {
553   if ( m_Parser.empty() )
554     return RESULT_INIT;
555
556   return m_Parser->Reset();
557 }
558
559 // Places a frame of data in the frame buffer. Fails if the buffer is too small
560 // or the stream is empty.
561 ASDCP::Result_t
562 ASDCP::MPEG2::Parser::ReadFrame(FrameBuffer& FB) const
563 {
564   if ( m_Parser.empty() )
565     return RESULT_INIT;
566
567   return m_Parser->ReadFrame(FB);
568 }
569
570 ASDCP::Result_t
571 ASDCP::MPEG2::Parser::FillVideoDescriptor(VideoDescriptor& VDesc) const
572 {
573   if ( m_Parser.empty() )
574     return RESULT_INIT;
575
576   return m_Parser->FillVideoDescriptor(VDesc);
577 }
578
579 //
580 // end AS_DCP_MPEG2_Parser.cpp
581 //