/*
-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
\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;
//------------------------------------------------------------------------------------------
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
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()
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;
}
return RESULT_OK;
}
+ DefaultLogSink().Error("Slice follows %s\n", StringParserState(m_State));
return RESULT_STATE;
}
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()
return RESULT_OK;
}
+ DefaultLogSink().Error("EXT follows %s\n", StringParserState(m_State));
return RESULT_STATE;
}
};
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; }
};
m_PlaintextOffset = 0;
m_FrameType = FRAME_U;
m_State.Reset();
- }
+ }
Result_t Sequence(VESParser*, const byte_t* b, ui32_t s)
{
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)
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;
// - 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;
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&);
//
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);
// 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;
}
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;}
+
//
//
// 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 )
{
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;
if ( m_EOF )
break;
}
+ assert(m_ParserDelegate.m_FrameSize <= write_offset);
if ( ASDCP_SUCCESS(result)
&& m_ParserDelegate.m_FrameSize < write_offset )
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) )
{
// 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;
}
//
-// end AS_DCP_MPEG2_Parser.cpp
+// end MPEG2_Parser.cpp
//