diff options
Diffstat (limited to 'asdcplib/src/MPEG2_Parser.cpp')
| -rwxr-xr-x | asdcplib/src/MPEG2_Parser.cpp | 639 |
1 files changed, 0 insertions, 639 deletions
diff --git a/asdcplib/src/MPEG2_Parser.cpp b/asdcplib/src/MPEG2_Parser.cpp deleted file mode 100755 index 3b81f5a9..00000000 --- a/asdcplib/src/MPEG2_Parser.cpp +++ /dev/null @@ -1,639 +0,0 @@ -/* -Copyright (c) 2004-2011, John Hurst -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. -2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. -3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -/*! \file MPEG2_Parser.cpp - \version $Id: MPEG2_Parser.cpp,v 1.11 2011/08/30 17:04:25 jhurst Exp $ - \brief AS-DCP library, MPEG2 raw essence reader implementation -*/ - -#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 = 4 * Kumu::Kilobyte; - - -//------------------------------------------------------------------------------------------ - -// -enum ParserState_t { - ST_INIT, - ST_SEQ, - ST_PIC, - ST_GOP, - ST_EXT, - 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 -{ - ParserState_t m_State; - ASDCP_NO_COPY_CONSTRUCT(h__ParserState); - - public: - h__ParserState() : m_State(ST_INIT) {} - ~h__ParserState() {} - - 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; - case ST_SEQ: - case ST_PIC: - case ST_GOP: - case ST_SLICE: - /* Keep gcc quiet */ - break; - } - - DefaultLogSink().Error("SEQ follows %s\n", StringParserState(m_State)); - return RESULT_STATE; - } - - - // - inline Result_t Goto_SLICE() - { - switch ( m_State ) - { - case ST_PIC: - case ST_EXT: - m_State = ST_SLICE; - return RESULT_OK; - case ST_INIT: - case ST_SEQ: - case ST_GOP: - case ST_SLICE: - /* Keep gcc quiet */ - break; - } - - DefaultLogSink().Error("Slice follows %s\n", StringParserState(m_State)); - return RESULT_STATE; - } - - - // - inline Result_t Goto_PIC() - { - switch ( m_State ) - { - case ST_INIT: - case ST_SEQ: - case ST_GOP: - case ST_EXT: - m_State = ST_PIC; - return RESULT_OK; - case ST_PIC: - case ST_SLICE: - /* Keep gcc quiet */ - break; - } - - 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; - case ST_INIT: - case ST_PIC: - case ST_GOP: - case ST_SLICE: - /* Keep gcc quiet */ - break; - } - - DefaultLogSink().Error("GOP follows %s\n", StringParserState(m_State)); - return RESULT_STATE; - } - - // - inline Result_t Goto_EXT() - { - switch ( m_State ) - { - case ST_PIC: - case ST_EXT: - case ST_SEQ: - case ST_GOP: - m_State = ST_EXT; - return RESULT_OK; - case ST_INIT: - case ST_SLICE: - /* Keep gcc quiet */ - break; - } - - DefaultLogSink().Error("EXT follows %s\n", StringParserState(m_State)); - return RESULT_STATE; - } -}; - -//------------------------------------------------------------------------------------------ - -// This is a parser delagate that reads the stream params from a -// sequence header and sequence extension header. The parser is -// commanded to return after processing the sequence extension -// header. -class StreamParams : public VESParserDelegate -{ - h__ParserState m_State; - - ASDCP_NO_COPY_CONSTRUCT(StreamParams); - -public: - VideoDescriptor m_VDesc; - - StreamParams() - { - m_VDesc.ContainerDuration = 0; - m_VDesc.ComponentDepth = 8; - } - - ~StreamParams() {} - - // - Result_t Sequence(VESParser*, const byte_t* b, ui32_t) - { - Result_t result = m_State.Goto_SEQ(); - - if ( ASDCP_FAILURE(result) ) - return result; - - Accessor::Sequence SEQ(b); - m_VDesc.AspectRatio = SEQ.AspectRatio(); - m_VDesc.FrameRate = SEQ.FrameRate(); - m_VDesc.StoredWidth = SEQ.HorizontalSize(); - m_VDesc.StoredHeight = SEQ.VerticalSize(); - m_VDesc.BitRate = SEQ.BitRate(); - m_VDesc.EditRate = SEQ.Pulldown() ? Rational(SEQ.FrameRate() * 1000, 1001) : Rational(SEQ.FrameRate(), 1); - m_VDesc.SampleRate = m_VDesc.EditRate; - return RESULT_OK; - } - - // - Result_t Extension(VESParser*, const byte_t* b, ui32_t) - { - Result_t result = m_State.Goto_EXT(); - - if ( ASDCP_FAILURE(result) ) - return result; - - Accessor::SequenceEx SEQX(b); - m_VDesc.ProfileAndLevel = SEQX.ProfileAndLevel(); - m_VDesc.FrameLayout = SEQX.Progressive() ? 0 : 1; - m_VDesc.CodedContentType = SEQX.Progressive() ? 1 : 2; - m_VDesc.LowDelay = SEQX.LowDelay(); - m_VDesc.HorizontalSubsampling = SEQX.ChromaFormat() == 3 ? 1 : 2; - m_VDesc.VerticalSubsampling = SEQX.ChromaFormat() >= 3 ? 1 : 2; - - if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 2 ) ) - m_VDesc.ColorSiting = 3; // 4:2:0 - - else if ( ( m_VDesc.HorizontalSubsampling == 2 ) && ( m_VDesc.VerticalSubsampling == 1 ) ) - m_VDesc.ColorSiting = 4; // 4:2:2 - - else if ( ( m_VDesc.HorizontalSubsampling == 1 ) && ( m_VDesc.VerticalSubsampling == 1 ) ) - m_VDesc.ColorSiting = 0; // 4:4:4 - - // TODO: get H&V size and bit rate extensions - - return RESULT_FALSE; - } - - 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*, byte_t) { return RESULT_FALSE; } - Result_t Data(VESParser*, const byte_t*, i32_t) { return RESULT_OK; } -}; - - -//------------------------------------------------------------------------------------------ - -// This is a parser delagate that reads a VES stream and sets public -// instance variables to indicate state. It is used below to read an -// entire frame into a buffer. The delegate retains metadata about the -// frame for later access. -// -class FrameParser : public VESParserDelegate -{ - h__ParserState m_State; - ASDCP_NO_COPY_CONSTRUCT(FrameParser); - -public: - ui32_t m_FrameSize; - bool m_CompletePicture; - bool m_HasGOP; - bool m_ClosedGOP; - ui8_t m_TemporalRef; - ui32_t m_PlaintextOffset; - FrameType_t m_FrameType; - - FrameParser() - { - Reset(); - } - - ~FrameParser() {} - - void Reset() - { - m_FrameSize = 0; - m_HasGOP = m_ClosedGOP = false; - m_CompletePicture = false; - m_TemporalRef = 0; - m_PlaintextOffset = 0; - m_FrameType = FRAME_U; - m_State.Reset(); - } - - Result_t Sequence(VESParser*, const byte_t*, ui32_t s) - { - if ( m_State.Test_SLICE() ) - { - m_CompletePicture = true; - return RESULT_FALSE; - } - - m_FrameSize += s; - return m_State.Goto_SEQ(); - } - - Result_t Picture(VESParser*, const byte_t* b, ui32_t s) - { - if ( m_State.Test_SLICE() ) - { - m_CompletePicture = true; - return RESULT_FALSE; - } - - Accessor::Picture pic(b); - m_TemporalRef = pic.TemporalRef(); - m_FrameType = pic.FrameType(); - m_FrameSize += s; - return m_State.Goto_PIC(); - } - - Result_t Slice(VESParser*, byte_t slice_id) - { - 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*, ui32_t s) - { - m_FrameSize += s; - return m_State.Goto_EXT(); - } - - Result_t GOP(VESParser*, const byte_t* b, ui32_t s) - { - Accessor::GOP GOP(b); - m_ClosedGOP = GOP.Closed(); - m_HasGOP = true; - m_FrameSize += s; - return m_State.Goto_GOP(); - } - - Result_t Data(VESParser*, const byte_t*, i32_t s) - { - m_FrameSize += s; - return RESULT_OK; - } -}; - -//------------------------------------------------------------------------------------------ - -// The following code assumes the following things: -// - each frame will begin with a picture header or a sequence header -// - any frame that begins with a sequence header is an I frame and is -// assumed to contain a GOP header, a picture header and complete picture data -// - 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; - 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*8) {} - ~h__Parser() { Close(); } - - Result_t OpenRead(const char* filename); - void Close(); - Result_t Reset(); - Result_t ReadFrame(FrameBuffer&); - Result_t FillVideoDescriptor(VideoDescriptor&); -}; - - -// -Result_t -ASDCP::MPEG2::Parser::h__Parser::Reset() -{ - m_FrameNumber = 0; - m_EOF = false; - m_FileReader.Seek(0); - m_ParserDelegate.Reset(); - return RESULT_OK; -} - -// -void -ASDCP::MPEG2::Parser::h__Parser::Close() -{ - m_FileReader.Close(); -} - -// -ASDCP::Result_t -ASDCP::MPEG2::Parser::h__Parser::OpenRead(const char* filename) -{ - ASDCP_TEST_NULL_STR(filename) - ui32_t read_count = 0; - - Result_t result = m_FileReader.OpenRead(filename); - - if ( ASDCP_SUCCESS(result) ) - result = m_FileReader.Read(m_TmpBuffer.Data(), m_TmpBuffer.Capacity(), &read_count); - - if ( ASDCP_SUCCESS(result) ) - { - const byte_t* p = m_TmpBuffer.RoData(); - - // the mxflib parser demanded the file start with a sequence header. - // 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. - 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 PIC or SEQ start code.\n"); - return RESULT_RAW_FORMAT; - } - - if ( ASDCP_SUCCESS(result) ) - { - m_Parser.SetDelegate(&m_ParamsDelegate); - result = m_Parser.Parse(p, read_count); - } - } - - 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); - m_FileReader.Close(); - } - - return result;} - - -// -// -ASDCP::Result_t -ASDCP::MPEG2::Parser::h__Parser::ReadFrame(FrameBuffer& FB) -{ - Result_t result = RESULT_OK; - ui32_t write_offset = 0; - ui32_t read_count = 0; - - FB.Size(0); - - if ( m_EOF ) - return RESULT_ENDOFFILE; - - // Data is read in VESReadSize chunks. Each chunk is parsed, and the - // process is stopped when a Sequence or Picture header is found or when - // 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 ) - { - memcpy(FB.Data(), m_TmpBuffer.RoData(), m_TmpBuffer.Size()); - result = m_Parser.Parse(FB.RoData(), m_TmpBuffer.Size()); - write_offset = m_TmpBuffer.Size(); - m_TmpBuffer.Size(0); - } - - while ( ! m_ParserDelegate.m_CompletePicture && result == RESULT_OK ) - { - if ( FB.Capacity() < ( write_offset + VESReadSize ) ) - { - 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 || read_count == 0 ) - { - m_EOF = true; - - if ( write_offset > 0 ) - result = RESULT_OK; - } - - if ( ASDCP_SUCCESS(result) ) - { - result = m_Parser.Parse(FB.RoData() + write_offset, read_count); - write_offset += read_count; - } - - 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); - } - - if ( ASDCP_SUCCESS(result) ) - { - const byte_t* p = FB.RoData(); - 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; - } - } - - if ( ASDCP_SUCCESS(result) ) - { - FB.Size(m_ParserDelegate.m_FrameSize); - FB.TemporalOffset(m_ParserDelegate.m_TemporalRef); - FB.FrameType(m_ParserDelegate.m_FrameType); - FB.PlaintextOffset(m_ParserDelegate.m_PlaintextOffset); - FB.FrameNumber(m_FrameNumber++); - FB.GOPStart(m_ParserDelegate.m_HasGOP); - FB.ClosedGOP(m_ParserDelegate.m_ClosedGOP); - } - - return result; -} - - -// Fill a VideoDescriptor struct with the values from the file's header. -ASDCP::Result_t -ASDCP::MPEG2::Parser::h__Parser::FillVideoDescriptor(VideoDescriptor& VDesc) -{ - VDesc = m_ParamsDelegate.m_VDesc; - return RESULT_OK; -} - -//------------------------------------------------------------------------------------------ - -ASDCP::MPEG2::Parser::Parser() -{ -} - -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 -{ - const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser = new h__Parser; - - Result_t result = m_Parser->OpenRead(filename); - - if ( ASDCP_FAILURE(result) ) - const_cast<ASDCP::MPEG2::Parser*>(this)->m_Parser.release(); - - return result; -} - -// Rewinds the stream to the beginning. -ASDCP::Result_t -ASDCP::MPEG2::Parser::Reset() const -{ - if ( m_Parser.empty() ) - return RESULT_INIT; - - return m_Parser->Reset(); -} - -// Places a frame of data in the frame buffer. Fails if the buffer is too small -// or the stream is empty. -ASDCP::Result_t -ASDCP::MPEG2::Parser::ReadFrame(FrameBuffer& FB) const -{ - if ( m_Parser.empty() ) - return RESULT_INIT; - - return m_Parser->ReadFrame(FB); -} - -ASDCP::Result_t -ASDCP::MPEG2::Parser::FillVideoDescriptor(VideoDescriptor& VDesc) const -{ - if ( m_Parser.empty() ) - return RESULT_INIT; - - return m_Parser->FillVideoDescriptor(VDesc); -} - -// -// end MPEG2_Parser.cpp -// |
