[Matroska-cvs] [matroska] r1291 - in trunk/DvdMenuXtractor/dmx: .
mpegparser
smssms at matroska.org
smssms at matroska.org
Sat Mar 10 22:45:49 CET 2007
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
===================================================================
--- 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 <spyder at matroska.org>
+
+ This program is free software ; you can redistribute it and/or modify
+ 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 = new binary[size];
+ read_ptr = m_buf;
+ write_ptr = m_buf;
+ buf_capacity = size;
+ bytes_in_buf = 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 = bytes_before_wrap_read(); //how many bytes we have before the buffer must be wrapped
+ if(bbw >= numBytes){
+ move_pointer(&read_ptr, numBytes);
+ bytes_in_buf -= numBytes;
+ }else{
+ if(bbw != 0){
+ numBytes -= bbw;
+ bytes_in_buf -= bbw;
+ move_pointer(&read_ptr, bbw); //Reset to beginning
+ }
+ bytes_in_buf -= 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 = bytes_before_wrap_read(); //how many bytes we have before the buffer must be wrapped
+ if(bbw >= numBytes){
+ memcpy(dest, read_ptr, numBytes);
+ move_pointer(&read_ptr, numBytes);
+ bytes_in_buf -= numBytes;
+ }else{
+ if(bbw != 0){
+ memcpy(dest,read_ptr,bbw);
+ numBytes -= bbw;
+ bytes_in_buf -= bbw;
+ dest += bbw;
+ move_pointer(&read_ptr, bbw); //Reset to beginning
+ }
+ memcpy(dest, read_ptr,numBytes);
+ bytes_in_buf -= 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 = bytes_before_wrap_write();
+ if(bbw >= length){
+ memcpy(write_ptr, data, length);
+ move_pointer(&write_ptr, length);
+ bytes_in_buf += length;
+ }else{
+ if(bbw != 0){
+ memcpy(write_ptr,data,bbw);
+ length -= bbw;
+ bytes_in_buf += bbw;
+ data += bbw;
+ move_pointer(&write_ptr, bbw); //Reset to beginning
+ }
+ memcpy(write_ptr, data,length);
+ bytes_in_buf += length;
+ move_pointer(&write_ptr, length);
+ }
+ return 0;
+ }else{
+ return -1;
+ }
+}
Added: trunk/DvdMenuXtractor/dmx/mpegparser/CircBuffer.h
===================================================================
--- 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 <spyder at matroska.org>
+
+ This program is free software ; you can redistribute it and/or modify
+ 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 = (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 = (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) >= numBytes;
+ }
+
+ inline bool can_read(uint32_t numBytes){
+ return (bytes_in_buf) >= numBytes;
+ }
+
+ inline void move_pointer(binary** ptr, uint32_t numBytes){
+ *ptr += numBytes;
+ if(((m_buf + buf_capacity) - *ptr) == 0){
+ *ptr = 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 = 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
===================================================================
--- trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.cpp 2007-03-10 21:41:34 UTC (rev 1290)
+++ trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.cpp 2007-03-10 21:45:28 UTC (rev 1291)
@@ -0,0 +1,396 @@
+/*****************************************************************************
+
+ MPEG Video Packetizer
+
+ Copyright(C) 2004 John Cannon <spyder at matroska.org>
+
+ This program is free software ; you can redistribute it and/or modify
+ 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 = (binary *)safemalloc(size);
+ memcpy(this->data, data, size);
+ }else{
+ this->data = data;
+ }
+ this->bCopy = bCopy;
+ this->size = size;
+ firstRef = -1;
+ secondRef = -1;
+ seqHdrData = NULL;
+ seqHdrDataSize = 0;
+}
+
+MPEGFrame::~MPEGFrame(){
+ safefree(data);
+ safefree(seqHdrData);
+}
+
+void M2VParser::SetEOS(){
+ MPEGChunk * c;
+ while((c = mpgBuf->ReadChunk())){
+ chunks.push_back(c);
+ }
+ mpgBuf->ForceFinal(); //Force the last frame out.
+ c = mpgBuf->ReadChunk();
+ if(c) chunks.push_back(c);
+ FillQueues();
+ m_eos = 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() == MPEG2_BUFFER_STATE_CHUNK_READY){
+ MPEGChunk * c = mpgBuf->ReadChunk();
+ if(c) chunks.push_back(c);
+ }
+
+ if(needInit){
+ if(InitParser() == 0)
+ needInit = false;
+ }
+
+ FillQueues();
+ if(buffers.empty()){
+ parserState = MPV_PARSER_STATE_NEED_DATA;
+ }else{
+ parserState = 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 == 24000/1001.0f)
+ return Fraction(24000,1001);
+ else if(frameRate == 30000/1001.0f)
+ return Fraction(30000,1001);
+ else
+ return Fraction(frameRate);
+ }*/
+
+M2VParser::M2VParser(){
+
+ mpgBuf = new MPEGVideoBuffer(BUFF_SIZE);
+
+ notReachedFirstGOP = true;
+ currentStampingTime = 0;
+ position = 0;
+ m_eos = false;
+ mpegVersion = 1;
+ needInit = true;
+ parserState = MPV_PARSER_STATE_NEED_DATA;
+ firstRef = -1;
+ secondRef = -1;
+ nextSkip = -1;
+ nextSkipDuration = -1;
+ seqHdrChunk = NULL;
+ gopChunk = NULL;
+ keepSeqHdrsInBitstream = true;
+}
+
+int32_t M2VParser::InitParser(){
+ //Gotta find a sequence header now
+ MPEGChunk* chunk;
+ //MPEGChunk* seqHdrChunk;
+ for(size_t i = 0; i < chunks.size(); i++){
+ chunk = chunks[i];
+ if(chunk->GetType() == 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 = new binary[chunk->GetSize()];
+ memcpy(hdrData, chunk->GetPointer(), chunk->GetSize());
+ seqHdrChunk = new MPEGChunk(hdrData, chunk->GetSize()); //Save this for adding as private data...
+ ParseSequenceHeader(chunk, m_seqHdr);
+
+ //Look for sequence extension to identify mpeg2
+ binary* pData = chunk->GetPointer();
+ for(size_t j = 3; j < chunk->GetSize() - 4; j++){
+ if(pData[j] == 0x00 && pData[j+1] == 0x00 && pData[j+2] == 0x01 && pData[j+3] == 0xb5 && ((pData[j+4] & 0xF0) == 0x10)){
+ mpegVersion = 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 != 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 = MPV_PARSER_STATE_FRAME;
+ else
+ parserState = MPV_PARSER_STATE_NEED_DATA;
+
+ if(m_eos && parserState == MPV_PARSER_STATE_NEED_DATA)
+ parserState = MPV_PARSER_STATE_EOS;
+ return parserState;
+}
+
+MediaTime M2VParser::CountBFrames(){
+ //We count after the first chunk.
+ MediaTime count = 0;
+ if(m_eos) return 0;
+ if(notReachedFirstGOP) return 0;
+ for(size_t i = 1; i < chunks.size(); i++){
+ MPEGChunk* c = chunks[i];
+ if(c->GetType() == MPEG_VIDEO_PICTURE_START_CODE){
+ MPEG2PictureHeader h;
+ ParsePictureHeader(c, h);
+ if(h.frameType == MPEG2_B_FRAME){
+ count += GetFrameDuration(h);
+ }else{
+ return count;
+ }
+ }
+ }
+ return -1;
+}
+
+int32_t M2VParser::QueueFrame(MPEGChunk* chunk, MediaTime timecode, MPEG2PictureHeader picHdr){
+ MPEGFrame* outBuf;
+ bool bCopy = true;
+ binary* pData = chunk->GetPointer();
+ uint32_t dataLen = chunk->GetSize();
+
+ if ((seqHdrChunk && keepSeqHdrsInBitstream &&
+ (MPEG2_I_FRAME == picHdr.frameType)) || gopChunk) {
+ uint32_t pos = 0;
+ bCopy = false;
+ dataLen +=
+ (seqHdrChunk && keepSeqHdrsInBitstream ? seqHdrChunk->GetSize() : 0) +
+ (gopChunk ? gopChunk->GetSize() : 0);
+ pData = (binary *)safemalloc(dataLen);
+ if (seqHdrChunk && keepSeqHdrsInBitstream &&
+ (MPEG2_I_FRAME == picHdr.frameType)) {
+ memcpy(pData, seqHdrChunk->GetPointer(), seqHdrChunk->GetSize());
+ pos += seqHdrChunk->GetSize();
+ delete seqHdrChunk;
+ seqHdrChunk = NULL;
+ }
+ if (gopChunk) {
+ memcpy(pData + pos, gopChunk->GetPointer(), gopChunk->GetSize());
+ pos += gopChunk->GetSize();
+ delete gopChunk;
+ gopChunk = NULL;
+ }
+ memcpy(pData + pos, chunk->GetPointer(), chunk->GetSize());
+ }
+
+ MediaTime duration = GetFrameDuration(picHdr);
+
+ outBuf = new MPEGFrame(pData, dataLen, bCopy);
+
+ if (seqHdrChunk && !keepSeqHdrsInBitstream &&
+ (MPEG2_I_FRAME == picHdr.frameType)) {
+ outBuf->seqHdrData = (binary *)safemalloc(seqHdrChunk->GetSize());
+ outBuf->seqHdrDataSize = seqHdrChunk->GetSize();
+ memcpy(outBuf->seqHdrData, seqHdrChunk->GetPointer(),
+ outBuf->seqHdrDataSize);
+ delete seqHdrChunk;
+ seqHdrChunk = NULL;
+ }
+
+ if(picHdr.frameType == MPEG2_I_FRAME){
+ outBuf->frameType = 'I';
+ }else if(picHdr.frameType == MPEG2_P_FRAME){
+ outBuf->frameType = 'P';
+ }else{
+ outBuf->frameType = 'B';
+ }
+
+ outBuf->timecode = (MediaTime)(timecode * (1000000000/(m_seqHdr.frameRate*2)));
+ outBuf->duration = (MediaTime)(duration * (1000000000/(m_seqHdr.frameRate*2)));
+
+ if(outBuf->frameType == 'P'){
+ outBuf->firstRef = (MediaTime)(firstRef * (1000000000/(m_seqHdr.frameRate*2)));
+ }else if(outBuf->frameType == 'B'){
+ outBuf->firstRef = (MediaTime)(firstRef * (1000000000/(m_seqHdr.frameRate*2)));
+ outBuf->secondRef = (MediaTime)(secondRef * (1000000000/(m_seqHdr.frameRate*2)));
+ }
+ outBuf->rff = (picHdr.repeatFirstField != 0);
+ outBuf->tff = (picHdr.topFieldFirst != 0);
+ outBuf->progressive = (picHdr.progressive != 0);
+ outBuf->pictureStructure = (uint8_t) picHdr.pictureStructure;
+ buffers.push(outBuf);
+ return 0;
+}
+
+void M2VParser::ShoveRef(MediaTime ref){
+ if(firstRef == -1){
+ firstRef = ref;
+ }else if(secondRef == -1){
+ secondRef = ref;
+ }else{
+ firstRef = secondRef;
+ secondRef = 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 = false;
+ while(!done){
+ MediaTime myTime = currentStampingTime;
+ MPEGChunk* chunk = chunks.front();
+ while (chunk->GetType() != MPEG_VIDEO_PICTURE_START_CODE) {
+ if (chunk->GetType() == MPEG_VIDEO_GOP_START_CODE) {
+ if (gopChunk)
+ delete gopChunk;
+ gopChunk = chunk;
+
+ } else if (chunk->GetType() == MPEG_VIDEO_SEQUENCE_START_CODE) {
+ if (seqHdrChunk)
+ delete seqHdrChunk;
+ ParseSequenceHeader(chunk, m_seqHdr);
+ seqHdrChunk = chunk;
+
+ }
+
+ chunks.erase(chunks.begin());
+ if (chunks.empty())
+ return -1;
+ chunk = chunks.front();
+ }
+ MPEG2PictureHeader picHdr;
+ ParsePictureHeader(chunk, picHdr);
+ MediaTime bcount;
+ if(myTime == nextSkip){
+ myTime+=nextSkipDuration;
+ currentStampingTime=myTime;
+ }
+ switch(picHdr.frameType){
+ case MPEG2_I_FRAME:
+ bcount = CountBFrames();
+ if(bcount > 0){ //..BBIBB..
+ myTime += bcount;
+ nextSkip = myTime;
+ nextSkipDuration = GetFrameDuration(picHdr);
+ }else{ //IPBB..
+ if(bcount == -1 && !m_eos)
+ return -1;
+ currentStampingTime += GetFrameDuration(picHdr);;
+ }
+ ShoveRef(myTime);
+ QueueFrame(chunk, myTime, picHdr);
+ notReachedFirstGOP = false;
+ break;
+ case MPEG2_P_FRAME:
+ bcount = CountBFrames();
+ if(firstRef == -1) break;
+ if(bcount > 0){ //..PBB..
+ myTime += bcount;
+ nextSkip = myTime;
+ nextSkipDuration = GetFrameDuration(picHdr);
+ }else{ //..PPBB..
+ if(bcount == -1 && !m_eos) return -1;
+ currentStampingTime+=GetFrameDuration(picHdr);
+ }
+ ShoveRef(myTime);
+ QueueFrame(chunk, myTime, picHdr);
+ break;
+ default: //B-frames
+ if(firstRef == -1 || secondRef == -1) break;
+ QueueFrame(chunk, myTime, picHdr);
+ currentStampingTime+=GetFrameDuration(picHdr);
+ }
+ chunks.erase(chunks.begin());
+ delete chunk;
+ if (chunks.empty())
+ return -1;
+ }
+ return 0;
+}
+
+MPEGFrame* M2VParser::ReadFrame(){
+ //if(m_eos) return NULL;
+ if(GetState() != MPV_PARSER_STATE_FRAME){
+ return NULL;
+ }
+ if(buffers.empty()){
+ return NULL; // OOPS!
+ }
+ MPEGFrame* frame = buffers.front();
+ buffers.pop();
+ return frame;
+}
+
Added: trunk/DvdMenuXtractor/dmx/mpegparser/M2VParser.h
===================================================================
--- 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 <spyder at matroska.org>
+
+ This program is free software ; you can redistribute it and/or modify
+ 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 them
+ 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 seq 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, MPEG2PictureHeader 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 frames waiting.
+ //Do not call this without good reason.
+ void SetEOS();
+
+ void SeparateSequenceHeaders() {
+ keepSeqHdrsInBitstream = false;
+ }
+};
+
+
+#endif //__M2VPARSER_H__
Added: trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.cpp
===================================================================
--- trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.cpp 2007-03-10 21:41:34 UTC (rev 1290)
+++ trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.cpp 2007-03-10 21:45:28 UTC (rev 1291)
@@ -0,0 +1,269 @@
+/*****************************************************************************
+
+ MPEG Video Packetizing Buffer
+
+ Copyright(C) 2004 John Cannon <spyder at matroska.org>
+
+ This program is free software ; you can redistribute it and/or modify
+ 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 = myBuffer->GetLength() - startPos;
+
+ if(window < 4) //Make sure we have enough bytes to search.
+ return -1;
+
+ for(unsigned int i = startPos; i < (window - 3); i++){
+ CircBuffer& buf = *myBuffer;
+ binary a,b,c,d;
+ a = buf[i];
+ b = buf[i+1];
+ c = buf[i+2];
+ d = buf[i+3];
+ if((a == 0x00) && (b == 0x00) && (c == 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 != NULL);
+ int32_t test = 0;
+ if(myBuffer->GetLength() == 0){
+ state = MPEG2_BUFFER_STATE_EMPTY;
+ return;
+ }
+ if(chunkStart == -1){
+ test = FindStartCode(0);
+ if(test != -1) //We found a new startcode
+ chunkStart = test;
+ }
+ if(chunkEnd == -1){
+ test = FindStartCode(chunkStart+4);
+ if(test != -1) //We found a new startcode
+ chunkEnd = test;
+ }
+ if(chunkStart == -1 || chunkEnd == -1){
+ state = MPEG2_BUFFER_STATE_NEED_MORE_DATA;
+ }else{
+ assert(chunkStart >= 0 && chunkStart < chunkEnd && chunkEnd > 0);
+ state = MPEG2_BUFFER_STATE_CHUNK_READY;
+ }
+}
+
+MPEGChunk * MPEGVideoBuffer::ReadChunk(){
+ MPEGChunk* myChunk = NULL;
+ if(state == MPEG2_BUFFER_STATE_CHUNK_READY){
+ assert(chunkStart < chunkEnd && chunkStart != -1 && chunkEnd != -1);
+ if(chunkStart != 0){ //we have to skip some bytes
+ myBuffer->Skip(chunkStart);
+ }
+ uint32_t chunkLength = chunkEnd - chunkStart;
+ binary* chunkData = new binary[chunkLength];
+ myBuffer->Read(chunkData, chunkLength);
+ chunkStart = 0; //we read up to the next start code
+ chunkEnd = -1;
+ UpdateState();
+ myChunk = new MPEGChunk(chunkData, chunkLength);
+ return myChunk;
+ }else{
+ return NULL;
+ }
+}
+
+void MPEGVideoBuffer::ForceFinal(){
+ if(state == MPEG2_BUFFER_STATE_NEED_MORE_DATA){
+ chunkStart = 0;
+ chunkEnd = chunkStart + myBuffer->GetLength();
+ UpdateState();
+ }
+}
+
+int32_t MPEGVideoBuffer::Feed(binary* data, uint32_t numBytes){
+ uint32_t res = myBuffer->Write(data, numBytes);
+ UpdateState();
+ return res;
+}
+
+void ParseSequenceHeader(MPEGChunk* chunk, MPEG2SequenceHeader & hdr){
+ binary* pos = chunk->GetPointer();
+ uint8_t haveSeqExt = 0;
+ //Parse out the resolution info, horizontal first
+ pos+=4; //Skip the start code
+ hdr.width = (((unsigned int)pos[0]) << 4) | (((unsigned int) pos[1])>>4); //xx x0 00
+ pos+=1;
+ hdr.height = (((unsigned int)pos[0] & 0x0F) << 8) | (((unsigned int) pos[1])); //00 0x xx
+ pos+=2;
+ switch(pos[0] & 0xF0){
+ case 0x10:
+ hdr.aspectRatio = 1.0f;
+ break;
+ case 0x20:
+ hdr.aspectRatio = (4.0f / 3.0f);
+ break;
+ case 0x30:
+ hdr.aspectRatio = (16.0f / 9.0f);
+ //playuing with AR
+ /*pos[0] &=0x0F;
+ pos[0] |= 0x20;*/
+ break;
+ case 0x40:
+ hdr.aspectRatio = 2.21f;
+ break;
+ default:
+ hdr.aspectRatio = -1.0f;
+ }
+ switch(pos[0] & 0x0F){
+ case 0x01:
+ hdr.frameRate = 24000.0f/1001.0f;//23.976
+ break;
+ case 0x02:
+ hdr.frameRate = 24.0f;//24
+ break;
+ case 0x03:
+ hdr.frameRate = 25.0f;//25
+ break;
+ case 0x04:
+ hdr.frameRate = 30000.0f/1001.0f;//29.97
+ //Let's play some more ;)
+ /*pos[0] &= 0xF0; //clear the framerate
+ pos[0] |= 0x01;
+ hdr->frameRate = 24000.0f/1001.0f;//23.976*/
+ break;
+ case 0x05:
+ hdr.frameRate = 30.0f;//30
+ break;
+ case 0x06:
+ hdr.frameRate = 50.0f;//50
+ break;
+ case 0x07:
+ hdr.frameRate = 60000.0f / 1001.0f;//59.94
+ break;
+ case 0x08:
+ hdr.frameRate = 60.0f;//60
+ break;
+ default:
+ hdr.frameRate = 0.0f;
+ }
+
+ //Seek to picturecoding extension
+ while(pos < (chunk->GetPointer() + chunk->GetSize() - 4)){
+ if((pos[0] == 0x00) && (pos[1] == 0x00) && (pos[2] == 0x01) && (pos[3] == MPEG_VIDEO_EXT_START_CODE)){
+ if((pos[4] & 0xF0) == 0x01){ //Picture coding extension
+ //printf("Found a picture_coding_extension\n");
+ haveSeqExt = 1;
+ pos+=4;
+ break;
+ }
+ }
+ pos++;
+ }
+
+ if(haveSeqExt){
+ pos++;
+ hdr.progressiveSequence = (pos[0] & 0x80);
+ }else{
+ hdr.progressiveSequence = 0;
+ }
+}
+
+bool ParseGOPHeader(MPEGChunk* chunk, MPEG2GOPHeader & hdr){
+ if(chunk->GetType() != MPEG_VIDEO_GOP_START_CODE){
+ //printf("Don't feed parse_gop_header a chunk that isn't a gop header!!!\n");
+ return false;
+ }
+ binary* pos = chunk->GetPointer();
+ uint32_t timecode;
+ pos+=4; //skip the startcode
+ //Parse GOP timecode structure
+ timecode = ((uint32_t)pos[0] << 24) |
+ ((uint32_t)pos[1] << 16) |
+ ((uint32_t)pos[2] << 8) |
+ ((uint32_t)pos[3]);
+ hdr.timeHours = (timecode << 1) >> 27;
+ hdr.timeMinutes= (timecode << 6)>>26;
+ hdr.timeSeconds = (timecode << 13) >> 26;
+ hdr.timeFrames = (timecode << 19) >> 26;
+ pos+=3;
+ if(pos[0] & 0x40){ //Check second bit
+ hdr.closedGOP = 1;
+ }else{
+ hdr.closedGOP = 0;
+ }
+ return true;
+}
+
+bool ParsePictureHeader(MPEGChunk* chunk, MPEG2PictureHeader & hdr){
+ if(chunk->GetType() != MPEG_VIDEO_PICTURE_START_CODE){
+ //printf("Don't feed parse_picture_header a chunk that isn't a picture!!!\n");
+ return false;
+ }
+ binary* pos = chunk->GetPointer();
+ int havePicExt = 0;
+ uint32_t temp = 0;
+ pos+=4;
+ temp = (((uint32_t)pos[0]) << 8) | (pos[1] & 0xC0);
+ hdr.temporalReference = temp >> 6;
+ pos+=1;
+ temp = ((uint32_t)(pos[0] & 0x38)) >> 3 ;
+ hdr.frameType = (uint8_t) temp;
+
+ //Seek to picturecoding extension
+ while(pos < (chunk->GetPointer() + chunk->GetSize() - 4)){
+ if((pos[0] == 0x00) && (pos[1] == 0x00) && (pos[2] == 0x01) && (pos[3] == MPEG_VIDEO_EXT_START_CODE)){
+ if((pos[4] & 0xF0) == 0x80){ //Picture coding extension
+ //printf("Found a picture_coding_extension\n");
+ havePicExt = 1;
+ break;
+ }
+ }
+ pos++;
+ }
+ if(!havePicExt){
+ hdr.pictureStructure = MPEG2_PICTURE_TYPE_FRAME;
+ hdr.repeatFirstField = 0;
+ hdr.topFieldFirst = 1;
+ hdr.progressive = 1;
+ }else{
+ pos+=4;//skip start code
+ pos+=2;//skip f_code shit
+ hdr.pictureStructure = (pos[0] & 0x03);
+ pos++;
+ hdr.topFieldFirst = (pos[0] & 0x80);
+ //pos++;
+ hdr.repeatFirstField = (pos[0] & 0x02);
+ /*//let's play ;)
+ hdr->repeatFirstField = (pos[0] = pos[0] & 0xFD) & 0x02;*/
+ pos++;
+ hdr.progressive = (pos[0] & 0x80);
+ }
+
+ return true;
+}
Added: trunk/DvdMenuXtractor/dmx/mpegparser/MPEGVideoBuffer.h
===================================================================
--- 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 <spyder at matroska.org>
+
+ This program is free software ; you can redistribute it and/or modify
+ 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 = data;
+ assert(dataSize > 4);
+ this->size = dataSize;
+ type = 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 = 0);
+public:
+ MPEGVideoBuffer(uint32_t size){
+ myBuffer = new CircBuffer(size);
+ state = MPEG2_BUFFER_STATE_EMPTY;
+ chunkStart = -1;
+ chunkEnd = -1;
+ }
+
+ ~MPEGVideoBuffer(){
+ delete myBuffer;
+ }
+
+ inline MPEG2BufferState GetState() const { return state; }
+
+ int32_t GetFreeBufferSpace(){
+ return (myBuffer->buf_capacity - myBuffer->bytes_in_buf);
+ }
+
+ void SetEndOfData(){
+ chunkEnd = 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
===================================================================
--- 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
===================================================================
--- 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
+}
More information about the Matroska-cvs
mailing list