o Fixed *all* Partiton version numbers in AS-02 files
[asdcplib.git] / src / MPEG2_Parser.cpp
index f6e0089edd3f0d3b09d94ae4f1e7c2aadcaafab0..89abbfd30a790cdcdc498fc30990b6829b0bd551 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004, John Hurst
+Copyright (c) 2004-2011, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -29,14 +29,17 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     \brief   AS-DCP library, MPEG2 raw essence reader implementation
 */
 
-#include <FileIO.h>
+#include <KM_fileio.h>
 #include <MPEG.h>
 
+#include <KM_log.h>
+using Kumu::DefaultLogSink;
+
 using namespace ASDCP;
 using namespace ASDCP::MPEG2;
 
 // data will be read from a VES file in chunks of this size
-const ui32_t VESReadSize = 4096;
+const ui32_t VESReadSize = 4 * Kumu::Kilobyte;
 
 
 //------------------------------------------------------------------------------------------
@@ -51,6 +54,23 @@ enum ParserState_t {
     ST_SLICE,
 };
 
+const char*
+StringParserState(ParserState_t state)
+{
+  switch ( state )
+    {
+    case ST_INIT:  return "INIT";
+    case ST_SEQ:   return "SEQ";
+    case ST_PIC:   return "PIC";
+    case ST_GOP:   return "GOP";
+    case ST_EXT:   return "EXT";
+    case ST_SLICE: return "SLICE";
+    }
+
+  return "*UNKNOWN*";
+}
+
+
 
 //
 class h__ParserState
@@ -59,11 +79,11 @@ class h__ParserState
   ASDCP_NO_COPY_CONSTRUCT(h__ParserState);
 
  public:
-  h__ParserState() : m_State(::ST_INIT) {}
+  h__ParserState() : m_State(ST_INIT) {}
   ~h__ParserState() {}
 
-  bool Test_SLICE() { return m_State == ST_SLICE; }
-  void Reset() { m_State = ST_INIT; }
+  inline bool Test_SLICE() { return m_State == ST_SLICE; }
+  inline void Reset() { m_State = ST_INIT; }
 
   //
   inline Result_t Goto_SEQ()
@@ -71,10 +91,12 @@ class h__ParserState
       switch ( m_State )
        {
        case ST_INIT:
+       case ST_EXT:
          m_State = ST_SEQ;
          return RESULT_OK;
        }
       
+      DefaultLogSink().Error("SEQ follows %s\n", StringParserState(m_State));
       return RESULT_STATE;
     }
 
@@ -90,6 +112,7 @@ class h__ParserState
          return RESULT_OK;
        }
       
+      DefaultLogSink().Error("Slice follows %s\n", StringParserState(m_State));
       return RESULT_STATE;
     }
 
@@ -107,23 +130,25 @@ class h__ParserState
          return RESULT_OK;
        }
       
+      DefaultLogSink().Error("PIC follows %s\n", StringParserState(m_State));
       return RESULT_STATE;
     }
 
 
   //
   inline Result_t Goto_GOP()
-    {
-      switch ( m_State )
-       {
-       case ST_EXT:
-       case ST_SEQ:
-         m_State = ST_GOP;
-         return RESULT_OK;
-       }
-      
-      return RESULT_STATE;
-    }
+  {
+    switch ( m_State )
+      {
+      case ST_EXT:
+      case ST_SEQ:
+       m_State = ST_GOP;
+       return RESULT_OK;
+      }
+    
+    DefaultLogSink().Error("GOP follows %s\n", StringParserState(m_State));
+    return RESULT_STATE;
+  }
 
   //
   inline Result_t Goto_EXT()
@@ -138,6 +163,7 @@ class h__ParserState
          return RESULT_OK;
       }
 
+    DefaultLogSink().Error("EXT follows %s\n", StringParserState(m_State));
     return RESULT_STATE;
   }
 };
@@ -216,8 +242,8 @@ public:
 
   Result_t GOP(VESParser*, const byte_t*, ui32_t)     { return RESULT_FALSE; }
   Result_t Picture(VESParser*, const byte_t*, ui32_t) { return RESULT_FALSE; }
-  Result_t Slice(VESParser*)                          { return RESULT_FALSE; }
-  Result_t Data(VESParser*, const byte_t*, ui32_t)    { return RESULT_OK; }
+  Result_t Slice(VESParser*, byte_t)                  { return RESULT_FALSE; }
+  Result_t Data(VESParser*, const byte_t*, i32_t)     { return RESULT_OK; }
 };
 
 
@@ -258,7 +284,7 @@ public:
     m_PlaintextOffset = 0;
     m_FrameType = FRAME_U;
     m_State.Reset();
 }
+ }
 
   Result_t Sequence(VESParser*, const byte_t* b, ui32_t s)
   {
@@ -280,17 +306,22 @@ public:
        return RESULT_FALSE;
       }
 
-    Accessor::Picture PIC(b);
-    m_TemporalRef = PIC.TemporalRef();
-    m_FrameType = PIC.FrameType();
+    Accessor::Picture pic(b);
+    m_TemporalRef = pic.TemporalRef();
+    m_FrameType = pic.FrameType();
     m_FrameSize += s;
     return m_State.Goto_PIC();
   }
 
-  Result_t Slice(VESParser*)
+  Result_t Slice(VESParser*, byte_t slice_id)
   {
-    m_PlaintextOffset = m_FrameSize;
-    return m_State.Goto_SLICE();
+    if ( slice_id == FIRST_SLICE )
+      {
+       m_PlaintextOffset = m_FrameSize;
+       return m_State.Goto_SLICE();
+      }
+
+    return m_State.Test_SLICE() ? RESULT_OK : RESULT_FAIL;
   }
 
   Result_t Extension(VESParser*, const byte_t* b, ui32_t s)
@@ -308,7 +339,7 @@ public:
     return m_State.Goto_GOP();
   }
 
-  Result_t Data(VESParser*, const byte_t* b, ui32_t s)
+  Result_t Data(VESParser*, const byte_t* b, i32_t s)
   {
     m_FrameSize += s;
     return RESULT_OK;
@@ -324,13 +355,12 @@ public:
 // - any frame that begins with a picture header is either an I, B or P frame
 //   and is assumed to contain a complete picture header and picture data
 
-
 class ASDCP::MPEG2::Parser::h__Parser
 {
   StreamParams     m_ParamsDelegate;
   FrameParser      m_ParserDelegate;
   VESParser        m_Parser;
-  FileReader       m_FileReader;
+  Kumu::FileReader m_FileReader;
   ui32_t           m_FrameNumber;
   bool             m_EOF;
   ASDCP::MPEG2::FrameBuffer  m_TmpBuffer;
@@ -338,10 +368,10 @@ class ASDCP::MPEG2::Parser::h__Parser
   ASDCP_NO_COPY_CONSTRUCT(h__Parser);
 
 public:
-  h__Parser() : m_TmpBuffer(VESReadSize*2) {}
+  h__Parser() : m_TmpBuffer(VESReadSize*8) {}
   ~h__Parser() { Close(); }
 
-  Result_t OpenRead(const char* filename);
+  Result_t OpenRead(const std::string& filename);
   void     Close();
   Result_t Reset();
   Result_t ReadFrame(FrameBuffer&);
@@ -369,9 +399,8 @@ ASDCP::MPEG2::Parser::h__Parser::Close()
 
 //
 ASDCP::Result_t
-ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename)
+ASDCP::MPEG2::Parser::h__Parser::OpenRead(const std::string& filename)
 {
-  ASDCP_TEST_NULL_STR(filename)
   ui32_t read_count = 0;
 
   Result_t result = m_FileReader.OpenRead(filename);
@@ -387,9 +416,12 @@ ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename)
       // Since no one complained and that's the easiest thing to implement,
       // I have left it that way. Let me know if you want to be able to
       // locate the first GOP in the stream.
-      if ( p[0] != 0 || p[1] != 0 || p[2] != 1 )
+      ui32_t i = 0;
+      while ( p[i] == 0 ) i++;
+
+      if ( i < 2 || p[i] != 1 || ! ( p[i+1] == SEQ_START || p[i+1] == PIC_START ) )
        {
-         DefaultLogSink().Error("Frame buffer does not begin with a start code.\n");
+         DefaultLogSink().Error("Frame buffer does not begin with a PIC or SEQ start code.\n");
          return RESULT_RAW_FORMAT;
        }
 
@@ -402,18 +434,20 @@ ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename)
 
   if ( ASDCP_SUCCESS(result) )
     {
+      ui64_t tmp = m_FileReader.Size() / 65536; // a gross approximation
+      m_ParamsDelegate.m_VDesc.ContainerDuration = (ui32_t) tmp;
       m_Parser.SetDelegate(&m_ParserDelegate);
       m_FileReader.Seek(0);
     }
 
   if ( ASDCP_FAILURE(result) )
     {
-      DefaultLogSink().Error("Unable to identify a wrapping mode for the essence in file \"%s\"\n", filename);
+      DefaultLogSink().Error("Unable to identify a wrapping mode for the essence in file \"%s\"\n", filename.c_str());
       m_FileReader.Close();
     }
 
-  return result;
-}
+  return result;}
+
 
 //
 //
@@ -434,6 +468,7 @@ ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
   // the input file is exhausted. The partial next frame is cached for the
   // next call.
   m_ParserDelegate.Reset();
+  m_Parser.Reset();
 
   if ( m_TmpBuffer.Size() > 0 )
     {
@@ -443,18 +478,18 @@ ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
       m_TmpBuffer.Size(0);
     }
 
-  while ( ! m_ParserDelegate.m_CompletePicture && ASDCP_SUCCESS(result) )
+  while ( ! m_ParserDelegate.m_CompletePicture && result == RESULT_OK )
     {
       if ( FB.Capacity() < ( write_offset + VESReadSize ) )
        {
-         DefaultLogSink().Error("FrameBuf.Capacity: %lu FrameLength: %lu\n",
+         DefaultLogSink().Error("FrameBuf.Capacity: %u FrameLength: %u\n",
                                 FB.Capacity(), ( write_offset + VESReadSize ));
          return RESULT_SMALLBUF;
        }
 
       result = m_FileReader.Read(FB.Data() + write_offset, VESReadSize, &read_count);
 
-      if ( result == RESULT_ENDOFFILE )
+      if ( result == RESULT_ENDOFFILE || read_count == 0 )
        {
          m_EOF = true;
 
@@ -471,6 +506,7 @@ ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
       if ( m_EOF )
        break;
     }
+  assert(m_ParserDelegate.m_FrameSize <= write_offset);
 
   if ( ASDCP_SUCCESS(result)
        && m_ParserDelegate.m_FrameSize < write_offset )
@@ -478,21 +514,20 @@ ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB)
       assert(m_TmpBuffer.Size() == 0);
       ui32_t diff = write_offset - m_ParserDelegate.m_FrameSize;
       assert(diff <= m_TmpBuffer.Capacity());
+
       memcpy(m_TmpBuffer.Data(), FB.RoData() + m_ParserDelegate.m_FrameSize, diff);
       m_TmpBuffer.Size(diff);
     }
 
-#ifndef NDEBUG
   if ( ASDCP_SUCCESS(result) )
     {
       const byte_t* p = FB.RoData();
-      if ( p[0] != 0 || p[1] != 0 || p[2] != 1 )
-       {
-         DefaultLogSink().Error("Parsed frame buffer does not begin with a start code.\n");
-         return RESULT_RAW_FORMAT;
-       }
+      if ( p[0] != 0 || p[1] != 0 || p[2] != 1 || ! ( p[3] == SEQ_START || p[3] == PIC_START ) )
+        {
+          DefaultLogSink().Error("Frame buffer does not begin with a PIC or SEQ start code.\n");
+          return RESULT_RAW_FORMAT;
+        }
     }
-#endif
 
   if ( ASDCP_SUCCESS(result) )
     {
@@ -530,7 +565,7 @@ ASDCP::MPEG2::Parser::~Parser()
 // Opens the stream for reading, parses enough data to provide a complete
 // set of stream metadata for the MXFWriter below.
 ASDCP::Result_t
-ASDCP::MPEG2::Parser::OpenRead(const char* filename) const
+ASDCP::MPEG2::Parser::OpenRead(const std::string& filename) const
 {
   const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser = new h__Parser;
 
@@ -573,5 +608,5 @@ ASDCP::MPEG2::Parser::FillVideoDescriptor(VideoDescriptor& VDesc) const
 }
 
 //
-// end AS_DCP_MPEG2_Parser.cpp
+// end MPEG2_Parser.cpp
 //