[matroska] r1291 - in trunk/DvdMenuXtractor/dmx: . mpegparser
[email protected] Sun, 11 Mar 2007 00:45:49 +0300 (MSK)
| Newsgroups | gmane.comp.multimedia.matroska.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: smssms Date: 2007-03-11 00:45:28 +0300 (Sun, 11 Mar 2007) New Revision: 1291 Added: trunk/DvdMenuXtractor/dmx/mpegparser/ trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.cpp trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.h trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.cpp trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.h trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.cpp trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.h trunk/DvdMenuXtractor/dmx/mpegparser/Types.h trunk/DvdMenuXtractor/dmx/mpegparser/mpegparser.proj Log: Added: trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.cpp =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.cpp 2007-03-10 21:41:= 34 UTC (rev 1290) +++ trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.cpp 2007-03-10 21:45:= 28 UTC (rev 1291) @@ -0,0 +1,110 @@ +/***********************************************************************= ****** + + Circular/Ring Buffer + + Copyright(C) 2004 John Cannon <[email protected]> + + This program is free software ; you can redistribute it and/or modif= y + it under the terms of the GNU General Public License as published by + the Free Software Foundation ; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program ; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307= USA + + **/ + +#include "CircBuffer.h" +#include <string.h> +#include <cassert> + +CircBuffer::CircBuffer(uint32_t size){ + m_buf =3D new binary[size]; + read_ptr =3D m_buf; + write_ptr =3D m_buf; + buf_capacity =3D size; + bytes_in_buf =3D 0; +} + +CircBuffer::~CircBuffer(){ + if(m_buf) + delete [] m_buf; +} + +int32_t CircBuffer::Skip(uint32_t numBytes){ + assert(numBytes > 0); + if(can_read(numBytes)){ + uint32_t bbw =3D bytes_before_wrap_read(); //how many bytes we have = before the buffer must be wrapped + if(bbw >=3D numBytes){ + move_pointer(&read_ptr, numBytes); + bytes_in_buf -=3D numBytes; + }else{ + if(bbw !=3D 0){ + numBytes -=3D bbw; + bytes_in_buf -=3D bbw; + move_pointer(&read_ptr, bbw); //Reset to beginning + } + bytes_in_buf -=3D numBytes; + move_pointer(&read_ptr, numBytes); + } + return 0; + }else{ + return -1; + } +} + +int32_t CircBuffer::Read(binary* dest, uint32_t numBytes){ + if(can_read(numBytes)){ + uint32_t bbw =3D bytes_before_wrap_read(); //how many bytes we have = before the buffer must be wrapped + if(bbw >=3D numBytes){ + memcpy(dest, read_ptr, numBytes); + move_pointer(&read_ptr, numBytes); + bytes_in_buf -=3D numBytes; + }else{ + if(bbw !=3D 0){ + memcpy(dest,read_ptr,bbw); + numBytes -=3D bbw; + bytes_in_buf -=3D bbw; + dest +=3D bbw; + move_pointer(&read_ptr, bbw); //Reset to beginning + } + memcpy(dest, read_ptr,numBytes); + bytes_in_buf -=3D numBytes; + move_pointer(&read_ptr, numBytes); + } + return 0; + }else{ + return -1; + } +} + +int32_t CircBuffer::Write(binary* data, uint32_t length){ + if(can_write(length)){ + uint32_t bbw =3D bytes_before_wrap_write(); + if(bbw >=3D length){ + memcpy(write_ptr, data, length); + move_pointer(&write_ptr, length); + bytes_in_buf +=3D length; + }else{ + if(bbw !=3D 0){ + memcpy(write_ptr,data,bbw); + length -=3D bbw; + bytes_in_buf +=3D bbw; + data +=3D bbw; + move_pointer(&write_ptr, bbw); //Reset to beginning + } + memcpy(write_ptr, data,length); + bytes_in_buf +=3D length; + move_pointer(&write_ptr, length); + } + return 0; + }else{ + return -1; + } +} Added: trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.h 2007-03-10 21:41:34= UTC (rev 1290) +++ trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.h 2007-03-10 21:45:28= UTC (rev 1291) @@ -0,0 +1,114 @@ +/***********************************************************************= ****** + + Circular/Ring Buffer + + Copyright(C) 2004 John Cannon <[email protected]> + + This program is free software ; you can redistribute it and/or modif= y + it under the terms of the GNU General Public License as published by + the Free Software Foundation ; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program ; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307= USA + + **/ + +#ifndef __CIRC_BUFFER_H__ + +#define __CIRC_BUFFER_H__ + +#include "Types.h" + +class CircBuffer { +private: + binary *m_buf; + binary *read_ptr; + binary *write_ptr; + + + inline uint32_t bytes_left() { + return buf_capacity - bytes_in_buf; + } + + inline uint32_t bytes_before_wrap_read() { + int32_t a =3D (int32_t) ((m_buf + buf_capacity) - read_ptr); + if(a < 0) + return 0; + else + return a; + } + + inline uint32_t bytes_before_wrap_write() { + int32_t a =3D (int32_t)((m_buf + buf_capacity) - write_ptr); + if(a < 0) + return 0; + else + return a; + } + + inline bool can_write(uint32_t numBytes){ + return (buf_capacity - bytes_in_buf) >=3D numBytes; + } + + inline bool can_read(uint32_t numBytes){ + return (bytes_in_buf) >=3D numBytes; + } + + inline void move_pointer(binary** ptr, uint32_t numBytes){ + *ptr +=3D numBytes; + if(((m_buf + buf_capacity) - *ptr) =3D=3D 0){ + *ptr =3D m_buf; + //printf("Buffer wrapped\n"); + } + } + +public: + + uint32_t buf_capacity; + uint32_t bytes_in_buf; + + CircBuffer(uint32_t size); + ~CircBuffer(); + + const binary* GetReadPtr(){ + return read_ptr; + } + + const binary* GetWritePtr(){ + return read_ptr; + } + + const binary* GetBufPtr(){ + return read_ptr; + } + + binary& operator[](unsigned int i){ + if(i > bytes_in_buf){ + return read_ptr[0]; + } + uint32_t bbw =3D bytes_before_wrap_read(); + if(i < bbw) + return read_ptr[i]; + else + return m_buf[i - bbw]; + } + + int32_t Read(binary* dest, uint32_t numBytes); + int32_t Skip(uint32_t numBytes); + int32_t Write(binary* data, uint32_t numBytes); + + uint32_t GetLength(){ + return bytes_in_buf; + } + +}; + +#endif // __CIRC_BUFFER_H__ + Added: trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.cpp =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.cpp 2007-03-10 21:41:3= 4 UTC (rev 1290) +++ trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.cpp 2007-03-10 21:45:2= 8 UTC (rev 1291) @@ -0,0 +1,396 @@ +/***********************************************************************= ****** + + MPEG Video Packetizer + + Copyright(C) 2004 John Cannon <[email protected]> + + This program is free software ; you can redistribute it and/or modif= y + it under the terms of the GNU General Public License as published by + the Free Software Foundation ; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program ; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307= USA + + **/ + +#include "M2VParser.h" + +//#include "common.h" +#define safemalloc(x) malloc(x) +#define safefree(x) free(x) + +#define BUFF_SIZE 2*1024*1024 + +MPEGFrame::MPEGFrame(binary* data, uint32_t size, bool bCopy){ + if(bCopy){ + this->data =3D (binary *)safemalloc(size); + memcpy(this->data, data, size); + }else{ + this->data =3D data; + } + this->bCopy =3D bCopy; + this->size =3D size; + firstRef =3D -1; + secondRef =3D -1; + seqHdrData =3D NULL; + seqHdrDataSize =3D 0; +} + +MPEGFrame::~MPEGFrame(){ + safefree(data); + safefree(seqHdrData); +} + +void M2VParser::SetEOS(){ + MPEGChunk * c; + while((c =3D mpgBuf->ReadChunk())){ + chunks.push_back(c); + } + mpgBuf->ForceFinal(); //Force the last frame out. + c =3D mpgBuf->ReadChunk(); + if(c) chunks.push_back(c); + FillQueues(); + m_eos =3D true; +} + + +int32_t M2VParser::WriteData(binary* data, uint32_t dataSize){ + //If we are at EOS + if(m_eos) + return -1; + + if(mpgBuf->Feed(data, dataSize)){ + return -1; + } + + //Fill the chunks buffer + while(mpgBuf->GetState() =3D=3D MPEG2_BUFFER_STATE_CHUNK_READY){ + MPEGChunk * c =3D mpgBuf->ReadChunk(); + if(c) chunks.push_back(c); + } + + if(needInit){ + if(InitParser() =3D=3D 0) + needInit =3D false; + } + + FillQueues(); + if(buffers.empty()){ + parserState =3D MPV_PARSER_STATE_NEED_DATA; + }else{ + parserState =3D MPV_PARSER_STATE_FRAME; + } + + return 0; +} + +void M2VParser::DumpQueues(){ + while(!chunks.empty()){ + delete chunks.front(); + chunks.erase(chunks.begin()); + } + while(!buffers.empty()){ + delete buffers.front(); + buffers.pop(); + } +} + +/*Fraction ConvertFramerateToFraction(float frameRate){ + if(frameRate =3D=3D 24000/1001.0f) + return Fraction(24000,1001); + else if(frameRate =3D=3D 30000/1001.0f) + return Fraction(30000,1001); + else + return Fraction(frameRate); + }*/ + +M2VParser::M2VParser(){ + + mpgBuf =3D new MPEGVideoBuffer(BUFF_SIZE); + + notReachedFirstGOP =3D true; + currentStampingTime =3D 0; + position =3D 0; + m_eos =3D false; + mpegVersion =3D 1; + needInit =3D true; + parserState =3D MPV_PARSER_STATE_NEED_DATA; + firstRef =3D -1; + secondRef =3D -1; + nextSkip =3D -1; + nextSkipDuration =3D -1; + seqHdrChunk =3D NULL; + gopChunk =3D NULL; + keepSeqHdrsInBitstream =3D true; +} + +int32_t M2VParser::InitParser(){ + //Gotta find a sequence header now + MPEGChunk* chunk; + //MPEGChunk* seqHdrChunk; + for(size_t i =3D 0; i < chunks.size(); i++){ + chunk =3D chunks[i]; + if(chunk->GetType() =3D=3D MPEG_VIDEO_SEQUENCE_START_CODE){ + //Copy the header for later, we must copy because the actual chunk= will be deleted in a bit + binary * hdrData =3D new binary[chunk->GetSize()]; + memcpy(hdrData, chunk->GetPointer(), chunk->GetSize()); + seqHdrChunk =3D new MPEGChunk(hdrData, chunk->GetSize()); //Save t= his for adding as private data... + ParseSequenceHeader(chunk, m_seqHdr); + + //Look for sequence extension to identify mpeg2 + binary* pData =3D chunk->GetPointer(); + for(size_t j =3D 3; j < chunk->GetSize() - 4; j++){ + if(pData[j] =3D=3D 0x00 && pData[j+1] =3D=3D 0x00 && pData[j+2] = =3D=3D 0x01 && pData[j+3] =3D=3D 0xb5 && ((pData[j+4] & 0xF0) =3D=3D 0x10= )){ + mpegVersion =3D 2; + break; + } + } + return 0; + } + } + return -1; +} + +M2VParser::~M2VParser(){ + DumpQueues(); + delete seqHdrChunk; + delete gopChunk; + delete mpgBuf; +} + +const MediaTime M2VParser::GetPosition(){ + return position; +} + +MediaTime M2VParser::GetFrameDuration(MPEG2PictureHeader picHdr){ + if(m_seqHdr.progressiveSequence){ + if(!picHdr.topFieldFirst && picHdr.repeatFirstField) + return 4; + else if(picHdr.topFieldFirst && picHdr.repeatFirstField) + return 6; + else + return 2; + } + if(picHdr.pictureStructure !=3D MPEG2_PICTURE_TYPE_FRAME){ + return 1; + } + if(picHdr.progressive && picHdr.repeatFirstField){ //TODO: fix this to= support progressive sequences also. + return 3; + }else{ + return 2; + } +} + +MPEG2ParserState M2VParser::GetState(){ + FillQueues(); + if(!buffers.empty()) + parserState =3D MPV_PARSER_STATE_FRAME; + else + parserState =3D MPV_PARSER_STATE_NEED_DATA; + + if(m_eos && parserState =3D=3D MPV_PARSER_STATE_NEED_DATA) + parserState =3D MPV_PARSER_STATE_EOS; + return parserState; +} + +MediaTime M2VParser::CountBFrames(){ + //We count after the first chunk. + MediaTime count =3D 0; + if(m_eos) return 0; + if(notReachedFirstGOP) return 0; + for(size_t i =3D 1; i < chunks.size(); i++){ + MPEGChunk* c =3D chunks[i]; + if(c->GetType() =3D=3D MPEG_VIDEO_PICTURE_START_CODE){ + MPEG2PictureHeader h; + ParsePictureHeader(c, h); + if(h.frameType =3D=3D MPEG2_B_FRAME){ + count +=3D GetFrameDuration(h); + }else{ + return count; + } + } + } + return -1; +} + +int32_t M2VParser::QueueFrame(MPEGChunk* chunk, MediaTime timecode, MPEG= 2PictureHeader picHdr){ + MPEGFrame* outBuf; + bool bCopy =3D true; + binary* pData =3D chunk->GetPointer(); + uint32_t dataLen =3D chunk->GetSize(); + + if ((seqHdrChunk && keepSeqHdrsInBitstream && + (MPEG2_I_FRAME =3D=3D picHdr.frameType)) || gopChunk) { + uint32_t pos =3D 0; + bCopy =3D false; + dataLen +=3D + (seqHdrChunk && keepSeqHdrsInBitstream ? seqHdrChunk->GetSize() : = 0) + + (gopChunk ? gopChunk->GetSize() : 0); + pData =3D (binary *)safemalloc(dataLen); + if (seqHdrChunk && keepSeqHdrsInBitstream && + (MPEG2_I_FRAME =3D=3D picHdr.frameType)) { + memcpy(pData, seqHdrChunk->GetPointer(), seqHdrChunk->GetSize()); + pos +=3D seqHdrChunk->GetSize(); + delete seqHdrChunk; + seqHdrChunk =3D NULL; + } + if (gopChunk) { + memcpy(pData + pos, gopChunk->GetPointer(), gopChunk->GetSize()); + pos +=3D gopChunk->GetSize(); + delete gopChunk; + gopChunk =3D NULL; + } + memcpy(pData + pos, chunk->GetPointer(), chunk->GetSize()); + } + + MediaTime duration =3D GetFrameDuration(picHdr); + + outBuf =3D new MPEGFrame(pData, dataLen, bCopy); + + if (seqHdrChunk && !keepSeqHdrsInBitstream && + (MPEG2_I_FRAME =3D=3D picHdr.frameType)) { + outBuf->seqHdrData =3D (binary *)safemalloc(seqHdrChunk->GetSize()); + outBuf->seqHdrDataSize =3D seqHdrChunk->GetSize(); + memcpy(outBuf->seqHdrData, seqHdrChunk->GetPointer(), + outBuf->seqHdrDataSize); + delete seqHdrChunk; + seqHdrChunk =3D NULL; + } + + if(picHdr.frameType =3D=3D MPEG2_I_FRAME){ + outBuf->frameType =3D 'I'; + }else if(picHdr.frameType =3D=3D MPEG2_P_FRAME){ + outBuf->frameType =3D 'P'; + }else{ + outBuf->frameType =3D 'B'; + } + + outBuf->timecode =3D (MediaTime)(timecode * (1000000000/(m_seqHdr.fram= eRate*2))); + outBuf->duration =3D (MediaTime)(duration * (1000000000/(m_seqHdr.fram= eRate*2))); + + if(outBuf->frameType =3D=3D 'P'){ + outBuf->firstRef =3D (MediaTime)(firstRef * (1000000000/(m_seqHdr.fr= ameRate*2))); + }else if(outBuf->frameType =3D=3D 'B'){ + outBuf->firstRef =3D (MediaTime)(firstRef * (1000000000/(m_seqHdr.fr= ameRate*2))); + outBuf->secondRef =3D (MediaTime)(secondRef * (1000000000/(m_seqHdr.= frameRate*2))); + } + outBuf->rff =3D (picHdr.repeatFirstField !=3D 0); + outBuf->tff =3D (picHdr.topFieldFirst !=3D 0); + outBuf->progressive =3D (picHdr.progressive !=3D 0); + outBuf->pictureStructure =3D (uint8_t) picHdr.pictureStructure; + buffers.push(outBuf); + return 0; +} + +void M2VParser::ShoveRef(MediaTime ref){ + if(firstRef =3D=3D -1){ + firstRef =3D ref; + }else if(secondRef =3D=3D -1){ + secondRef =3D ref; + }else{ + firstRef =3D secondRef; + secondRef =3D ref; + } +} + +//Reads frames until it can timestamp them, usually reads until it has +//an I, P, B+, P...this way it can stamp them all and set references. +//NOTE: references aren't yet used by our system but they are kept up +//with in this plugin. +int32_t M2VParser::FillQueues(){ + if(chunks.empty()){ + return -1; + } + bool done =3D false; + while(!done){ + MediaTime myTime =3D currentStampingTime; + MPEGChunk* chunk =3D chunks.front(); + while (chunk->GetType() !=3D MPEG_VIDEO_PICTURE_START_CODE) { + if (chunk->GetType() =3D=3D MPEG_VIDEO_GOP_START_CODE) { + if (gopChunk) + delete gopChunk; + gopChunk =3D chunk; + + } else if (chunk->GetType() =3D=3D MPEG_VIDEO_SEQUENCE_START_CODE)= { + if (seqHdrChunk) + delete seqHdrChunk; + ParseSequenceHeader(chunk, m_seqHdr); + seqHdrChunk =3D chunk; + + } + + chunks.erase(chunks.begin()); + if (chunks.empty()) + return -1; + chunk =3D chunks.front(); + } + MPEG2PictureHeader picHdr; + ParsePictureHeader(chunk, picHdr); + MediaTime bcount; + if(myTime =3D=3D nextSkip){ + myTime+=3DnextSkipDuration; + currentStampingTime=3DmyTime; + } + switch(picHdr.frameType){ + case MPEG2_I_FRAME: + bcount =3D CountBFrames(); + if(bcount > 0){ //..BBIBB.. + myTime +=3D bcount; + nextSkip =3D myTime; + nextSkipDuration =3D GetFrameDuration(picHdr); + }else{ //IPBB.. + if(bcount =3D=3D -1 && !m_eos) + return -1; + currentStampingTime +=3D GetFrameDuration(picHdr);; + } + ShoveRef(myTime); + QueueFrame(chunk, myTime, picHdr); + notReachedFirstGOP =3D false; + break; + case MPEG2_P_FRAME: + bcount =3D CountBFrames(); + if(firstRef =3D=3D -1) break; + if(bcount > 0){ //..PBB.. + myTime +=3D bcount; + nextSkip =3D myTime; + nextSkipDuration =3D GetFrameDuration(picHdr); + }else{ //..PPBB.. + if(bcount =3D=3D -1 && !m_eos) return -1; + currentStampingTime+=3DGetFrameDuration(picHdr); + } + ShoveRef(myTime); + QueueFrame(chunk, myTime, picHdr); + break; + default: //B-frames + if(firstRef =3D=3D -1 || secondRef =3D=3D -1) break; + QueueFrame(chunk, myTime, picHdr); + currentStampingTime+=3DGetFrameDuration(picHdr); + } + chunks.erase(chunks.begin()); + delete chunk; + if (chunks.empty()) + return -1; + } + return 0; +} + +MPEGFrame* M2VParser::ReadFrame(){ + //if(m_eos) return NULL; + if(GetState() !=3D MPV_PARSER_STATE_FRAME){ + return NULL; + } + if(buffers.empty()){ + return NULL; // OOPS! + } + MPEGFrame* frame =3D buffers.front(); + buffers.pop(); + return frame; +} + Added: trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.h 2007-03-10 21:41:34 = UTC (rev 1290) +++ trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.h 2007-03-10 21:45:28 = UTC (rev 1291) @@ -0,0 +1,130 @@ +/***********************************************************************= ****** + + MPEG Video Packetizer + + Copyright(C) 2004 John Cannon <[email protected]> + + This program is free software ; you can redistribute it and/or modif= y + it under the terms of the GNU General Public License as published by + the Free Software Foundation ; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program ; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307= USA + + **/ + +#ifndef __M2VPARSER_H__ +#define __M2VPARSER_H__ + +#include "MPEGVideoBuffer.h" +#include <stdio.h> +#include <queue> + +enum MPEG2ParserState { + MPV_PARSER_STATE_FRAME, + MPV_PARSER_STATE_NEED_DATA, + MPV_PARSER_STATE_EOS, + MPV_PARSER_STATE_ERROR +}; + +class MPEGFrame { +public: + binary *data; + uint32_t size; + binary *seqHdrData; + uint32_t seqHdrDataSize; + MediaTime duration; + char frameType; + MediaTime timecode; + MediaTime firstRef; + MediaTime secondRef; + bool rff; + bool tff; + bool progressive; + uint8_t pictureStructure; + bool bCopy; + + MPEGFrame(binary* data, uint32_t size, bool bCopy); + ~MPEGFrame(); +}; + +class M2VParser { +private: + std::vector<MPEGChunk*> chunks; //Hold the chunks until we can stamp t= hem + std::queue<MPEGFrame*> buffers; //Holds stamped buffers until they are= requested. + MediaTime position; + //Added to allow reading the header's raw data, contains first found s= eq hdr. + MPEGChunk* seqHdrChunk, *gopChunk; + MPEG2SequenceHeader m_seqHdr; //current sequence header + MediaTime currentStampingTime; + MediaTime firstRef; + bool needInit; + bool m_eos; + bool notReachedFirstGOP; + bool keepSeqHdrsInBitstream; + MediaTime nextSkip; + MediaTime nextSkipDuration; + MediaTime secondRef; + uint8_t mpegVersion; + MPEG2ParserState parserState; + MPEGVideoBuffer * mpgBuf; + + int32_t InitParser(); + void DumpQueues(); + int32_t FillQueues(); + MediaTime CountBFrames(); + void ShoveRef(MediaTime ref); + MediaTime GetFrameDuration(MPEG2PictureHeader picHdr); + int32_t QueueFrame(MPEGChunk* chunk, MediaTime timecode, MPEG2PictureH= eader picHdr); +public: + M2VParser(); + virtual ~M2VParser(); + + //Returns the current media position + virtual const MediaTime GetPosition(); + + MPEG2SequenceHeader GetSequenceHeader(){ + return m_seqHdr; + } + + //BE VERY CAREFUL WITH THIS CALL + MPEGChunk * GetRealSequenceHeader(){ + return seqHdrChunk; + } + + uint8_t GetMPEGVersion() const{ + return mpegVersion; + } + + //Returns a pointer to a frame that has been read + virtual MPEGFrame * ReadFrame(); + + //Returns the max amount of data that can be written to the buffer + int32_t GetFreeBufferSpace(){ + return mpgBuf->GetFreeBufferSpace(); + } + + //Writes data to the internal buffer. + int32_t WriteData(binary* data, uint32_t dataSize); + + //Returns the current state of the parser + MPEG2ParserState GetState(); + + //Sets "end of stream" status on the buffer, forces timestamping of fr= ames waiting. + //Do not call this without good reason. + void SetEOS(); + + void SeparateSequenceHeaders() { + keepSeqHdrsInBitstream =3D false; + } +}; + + +#endif //__M2VPARSER_H__ Added: trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.cpp =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.cpp 2007-03-10 2= 1:41:34 UTC (rev 1290) +++ trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.cpp 2007-03-10 2= 1:45:28 UTC (rev 1291) @@ -0,0 +1,269 @@ +/***********************************************************************= ****** + + MPEG Video Packetizing Buffer + + Copyright(C) 2004 John Cannon <[email protected]> + + This program is free software ; you can redistribute it and/or modif= y + it under the terms of the GNU General Public License as published by + the Free Software Foundation ; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program ; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307= USA + + **/ + +#include "MPEGVideoBuffer.h" +#include <stddef.h> + +int32_t MPEGVideoBuffer::FindStartCode(uint32_t startPos){ + //How many bytes can we look through? + uint32_t window =3D myBuffer->GetLength() - startPos; + + if(window < 4) //Make sure we have enough bytes to search. + return -1; + + for(unsigned int i =3D startPos; i < (window - 3); i++){ + CircBuffer& buf =3D *myBuffer; + binary a,b,c,d; + a =3D buf[i]; + b =3D buf[i+1]; + c =3D buf[i+2]; + d =3D buf[i+3]; + if((a =3D=3D 0x00) && (b =3D=3D 0x00) && (c =3D=3D 0x01)){ + switch(d){ + case MPEG_VIDEO_SEQUENCE_START_CODE: + case MPEG_VIDEO_GOP_START_CODE: + case MPEG_VIDEO_PICTURE_START_CODE: + return i; //Return our position if we found + //one of the codes we want + + } + } + } + + //If we get here we have no _wanted_ start code found. + return -1; +} + +void MPEGVideoBuffer::UpdateState(){ + assert(myBuffer !=3D NULL); + int32_t test =3D 0; + if(myBuffer->GetLength() =3D=3D 0){ + state =3D MPEG2_BUFFER_STATE_EMPTY; + return; + } + if(chunkStart =3D=3D -1){ + test =3D FindStartCode(0); + if(test !=3D -1) //We found a new startcode + chunkStart =3D test; + } + if(chunkEnd =3D=3D -1){ + test =3D FindStartCode(chunkStart+4); + if(test !=3D -1) //We found a new startcode + chunkEnd =3D test; + } + if(chunkStart =3D=3D -1 || chunkEnd =3D=3D -1){ + state =3D MPEG2_BUFFER_STATE_NEED_MORE_DATA; + }else{ + assert(chunkStart >=3D 0 && chunkStart < chunkEnd && chunkEnd > 0); + state =3D MPEG2_BUFFER_STATE_CHUNK_READY; + } +} + +MPEGChunk * MPEGVideoBuffer::ReadChunk(){ + MPEGChunk* myChunk =3D NULL; + if(state =3D=3D MPEG2_BUFFER_STATE_CHUNK_READY){ + assert(chunkStart < chunkEnd && chunkStart !=3D -1 && chunkEnd !=3D = -1); + if(chunkStart !=3D 0){ //we have to skip some bytes + myBuffer->Skip(chunkStart); + } + uint32_t chunkLength =3D chunkEnd - chunkStart; + binary* chunkData =3D new binary[chunkLength]; + myBuffer->Read(chunkData, chunkLength); + chunkStart =3D 0; //we read up to the next start code + chunkEnd =3D -1; + UpdateState(); + myChunk =3D new MPEGChunk(chunkData, chunkLength); + return myChunk; + }else{ + return NULL; + } +} + +void MPEGVideoBuffer::ForceFinal(){ + if(state =3D=3D MPEG2_BUFFER_STATE_NEED_MORE_DATA){ + chunkStart =3D 0; + chunkEnd =3D chunkStart + myBuffer->GetLength(); + UpdateState(); + } +} + +int32_t MPEGVideoBuffer::Feed(binary* data, uint32_t numBytes){ + uint32_t res =3D myBuffer->Write(data, numBytes); + UpdateState(); + return res; +} + +void ParseSequenceHeader(MPEGChunk* chunk, MPEG2SequenceHeader & hdr){ + binary* pos =3D chunk->GetPointer(); + uint8_t haveSeqExt =3D 0; + //Parse out the resolution info, horizontal first + pos+=3D4; //Skip the start code + hdr.width =3D (((unsigned int)pos[0]) << 4) | (((unsigned int) pos[1])= >>4); //xx x0 00 + pos+=3D1; + hdr.height =3D (((unsigned int)pos[0] & 0x0F) << 8) | (((unsigned int)= pos[1])); //00 0x xx + pos+=3D2; + switch(pos[0] & 0xF0){ + case 0x10: + hdr.aspectRatio =3D 1.0f; + break; + case 0x20: + hdr.aspectRatio =3D (4.0f / 3.0f); + break; + case 0x30: + hdr.aspectRatio =3D (16.0f / 9.0f); + //playuing with AR + /*pos[0] &=3D0x0F; + pos[0] |=3D 0x20;*/ + break; + case 0x40: + hdr.aspectRatio =3D 2.21f; + break; + default: + hdr.aspectRatio =3D -1.0f; + } + switch(pos[0] & 0x0F){ + case 0x01: + hdr.frameRate =3D 24000.0f/1001.0f;//23.976 + break; + case 0x02: + hdr.frameRate =3D 24.0f;//24 + break; + case 0x03: + hdr.frameRate =3D 25.0f;//25 + break; + case 0x04: + hdr.frameRate =3D 30000.0f/1001.0f;//29.97 + //Let's play some more ;) + /*pos[0] &=3D 0xF0; //clear the framerate + pos[0] |=3D 0x01; + hdr->frameRate =3D 24000.0f/1001.0f;//23.976*/ + break; + case 0x05: + hdr.frameRate =3D 30.0f;//30 + break; + case 0x06: + hdr.frameRate =3D 50.0f;//50 + break; + case 0x07: + hdr.frameRate =3D 60000.0f / 1001.0f;//59.94 + break; + case 0x08: + hdr.frameRate =3D 60.0f;//60 + break; + default: + hdr.frameRate =3D 0.0f; + } + + //Seek to picturecoding extension + while(pos < (chunk->GetPointer() + chunk->GetSize() - 4)){ + if((pos[0] =3D=3D 0x00) && (pos[1] =3D=3D 0x00) && (pos[2] =3D=3D 0x= 01) && (pos[3] =3D=3D MPEG_VIDEO_EXT_START_CODE)){ + if((pos[4] & 0xF0) =3D=3D 0x01){ //Picture coding extension + //printf("Found a picture_coding_extension\n"); + haveSeqExt =3D 1; + pos+=3D4; + break; + } + } + pos++; + } + + if(haveSeqExt){ + pos++; + hdr.progressiveSequence =3D (pos[0] & 0x80); + }else{ + hdr.progressiveSequence =3D 0; + } +} + +bool ParseGOPHeader(MPEGChunk* chunk, MPEG2GOPHeader & hdr){ + if(chunk->GetType() !=3D MPEG_VIDEO_GOP_START_CODE){ + //printf("Don't feed parse_gop_header a chunk that isn't a gop heade= r!!!\n"); + return false; + } + binary* pos =3D chunk->GetPointer(); + uint32_t timecode; + pos+=3D4; //skip the startcode + //Parse GOP timecode structure + timecode =3D ((uint32_t)pos[0] << 24) | + ((uint32_t)pos[1] << 16) | + ((uint32_t)pos[2] << 8) | + ((uint32_t)pos[3]); + hdr.timeHours =3D (timecode << 1) >> 27; + hdr.timeMinutes=3D (timecode << 6)>>26; + hdr.timeSeconds =3D (timecode << 13) >> 26; + hdr.timeFrames =3D (timecode << 19) >> 26; + pos+=3D3; + if(pos[0] & 0x40){ //Check second bit + hdr.closedGOP =3D 1; + }else{ + hdr.closedGOP =3D 0; + } + return true; +} + +bool ParsePictureHeader(MPEGChunk* chunk, MPEG2PictureHeader & hdr){ + if(chunk->GetType() !=3D MPEG_VIDEO_PICTURE_START_CODE){ + //printf("Don't feed parse_picture_header a chunk that isn't a pictu= re!!!\n"); + return false; + } + binary* pos =3D chunk->GetPointer(); + int havePicExt =3D 0; + uint32_t temp =3D 0; + pos+=3D4; + temp =3D (((uint32_t)pos[0]) << 8) | (pos[1] & 0xC0); + hdr.temporalReference =3D temp >> 6; + pos+=3D1; + temp =3D ((uint32_t)(pos[0] & 0x38)) >> 3 ; + hdr.frameType =3D (uint8_t) temp; + + //Seek to picturecoding extension + while(pos < (chunk->GetPointer() + chunk->GetSize() - 4)){ + if((pos[0] =3D=3D 0x00) && (pos[1] =3D=3D 0x00) && (pos[2] =3D=3D 0x= 01) && (pos[3] =3D=3D MPEG_VIDEO_EXT_START_CODE)){ + if((pos[4] & 0xF0) =3D=3D 0x80){ //Picture coding extension + //printf("Found a picture_coding_extension\n"); + havePicExt =3D 1; + break; + } + } + pos++; + } + if(!havePicExt){ + hdr.pictureStructure =3D MPEG2_PICTURE_TYPE_FRAME; + hdr.repeatFirstField =3D 0; + hdr.topFieldFirst =3D 1; + hdr.progressive =3D 1; + }else{ + pos+=3D4;//skip start code + pos+=3D2;//skip f_code shit + hdr.pictureStructure =3D (pos[0] & 0x03); + pos++; + hdr.topFieldFirst =3D (pos[0] & 0x80); + //pos++; + hdr.repeatFirstField =3D (pos[0] & 0x02); + /*//let's play ;) + hdr->repeatFirstField =3D (pos[0] =3D pos[0] & 0xFD) & 0x02;*/ + pos++; + hdr.progressive =3D (pos[0] & 0x80); + } + + return true; +} Added: trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.h 2007-03-10 21:= 41:34 UTC (rev 1290) +++ trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.h 2007-03-10 21:= 45:28 UTC (rev 1291) @@ -0,0 +1,155 @@ +/***********************************************************************= ****** + + MPEG Video Packetizing Buffer + + Copyright(C) 2004 John Cannon <[email protected]> + + This program is free software ; you can redistribute it and/or modif= y + it under the terms of the GNU General Public License as published by + the Free Software Foundation ; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY ; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program ; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307= USA + + **/ + +#ifndef __MPEGVIDEOBUFFER_H__ +#define __MPEGVIDEOBUFFER_H__ + +#include "Types.h" +#include "CircBuffer.h" +#include <cassert> + +#define MPEG_VIDEO_PICTURE_START_CODE 0x00 +#define MPEG_VIDEO_SEQUENCE_START_CODE 0xb3 +#define MPEG_VIDEO_EXT_START_CODE 0xb5 +#define MPEG_VIDEO_GOP_START_CODE 0xb8 +#define MPEG_VIDEO_USER_START_CODE 0xb2 + +enum MPEG2BufferState { + MPEG2_BUFFER_STATE_NEED_MORE_DATA, + MPEG2_BUFFER_STATE_CHUNK_READY, + MPEG2_BUFFER_STATE_EMPTY, + MPEG2_BUFFER_INVALID +}; + +#define MPEG2_I_FRAME 1 +#define MPEG2_P_FRAME 2 +#define MPEG2_B_FRAME 3 + +typedef struct MPEG2SequenceHeader{ + uint32_t width; + uint32_t height; + float aspectRatio; + float frameRate; + uint8_t progressiveSequence; +}MPEG2SequenceHeader; + +typedef struct MPEG2GOPHeader{ + uint8_t closedGOP; + uint32_t timeFrames; + uint32_t timeSeconds; + uint32_t timeMinutes; + uint32_t timeHours; +}MPEG2GOPHeader; + +#define MPEG2_PICTURE_TYPE_FRAME 0x03 +#define MPEG2_PICTURE_TYPE_TOP_FIELD 0x01 +#define MPEG2_PICTURE_TYPE_BOTTOM_FIELD 0x02 + +typedef struct MPEG2PictureHeader{ + uint32_t temporalReference; + uint8_t frameType; + uint32_t pictureStructure; + uint8_t repeatFirstField; + uint8_t topFieldFirst; + uint8_t progressive; +}MPEG2PictureHeader; + +class MPEGChunk{ +private: + binary * data; + uint32_t size; + uint8_t type; +public: + MPEGChunk(binary* data, uint32_t dataSize){ + assert(data); + this->data =3D data; + assert(dataSize > 4); + this->size =3D dataSize; + type =3D data[3]; + } + + ~MPEGChunk(){ + if(data) + delete [] data; + } + + inline uint8_t GetType() const { + return type; + } + + inline uint32_t GetSize() const{ + return size; + } + + binary & operator[](unsigned int i){ + return data[i]; + } + + binary & at(unsigned int i) { + return data[i]; + } + + inline binary * GetPointer(){ + return data; + } +}; + +void ParseSequenceHeader(MPEGChunk* chunk, MPEG2SequenceHeader & hdr); +bool ParsePictureHeader(MPEGChunk* chunk, MPEG2PictureHeader & hdr); +bool ParseGOPHeader(MPEGChunk* chunk, MPEG2GOPHeader & hdr); + +class MPEGVideoBuffer{ +private: + CircBuffer * myBuffer; + MPEG2BufferState state; + int32_t chunkStart; + int32_t chunkEnd; + void UpdateState(); + int32_t FindStartCode(uint32_t startPos =3D 0); +public: + MPEGVideoBuffer(uint32_t size){ + myBuffer =3D new CircBuffer(size); + state =3D MPEG2_BUFFER_STATE_EMPTY; + chunkStart =3D -1; + chunkEnd =3D -1; + } + + ~MPEGVideoBuffer(){ + delete myBuffer; + } + + inline MPEG2BufferState GetState() const { return state; } + + int32_t GetFreeBufferSpace(){ + return (myBuffer->buf_capacity - myBuffer->bytes_in_buf); + } + + void SetEndOfData(){ + chunkEnd =3D myBuffer->GetLength() - 1; + } + + void ForceFinal(); //prepares the remaining data as a chunk + MPEGChunk * ReadChunk(); + int32_t Feed(binary* data, uint32_t numBytes); +}; + +#endif //__MPEGVIDEOBUFFER_H__ Added: trunk/DvdMenuXtractor/dmx/mpegparser/Types.h =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/DvdMenuXtractor/dmx/mpegparser/Types.h 2007-03-10 21:41:34 UTC = (rev 1290) +++ trunk/DvdMenuXtractor/dmx/mpegparser/Types.h 2007-03-10 21:45:28 UTC = (rev 1291) @@ -0,0 +1,9 @@ +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#include <stdint.h> + +typedef int64_t MediaTime; +typedef uint8_t binary; + +#endif // __TYPES_H__ Added: trunk/DvdMenuXtractor/dmx/mpegparser/mpegparser.proj =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --- trunk/DvdMenuXtractor/dmx/mpegparser/mpegparser.proj 2007-03-10 21:41= :34 UTC (rev 1290) +++ trunk/DvdMenuXtractor/dmx/mpegparser/mpegparser.proj 2007-03-10 21:45= :28 UTC (rev 1291) @@ -0,0 +1,11 @@ +GROUP mpegparser +{ + SOURCE CircBuffer.cpp + SOURCE M2VParser.cpp + SOURCE MPEGVideoBuffer.cpp + + HEADER CircBuffer.h + HEADER M2VParser.h + HEADER MPEGVideoBuffer.h + HEADER Types.h +}