[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