Cleanups & speed improvements for the MPEG video parser. Patch by Steve Lhomme (see AUTHORS).

This commit is contained in:
Moritz Bunkus 2005-05-26 17:59:10 +00:00
parent 64f306d8eb
commit ac9c14b265
4 changed files with 55 additions and 69 deletions

View File

@ -128,20 +128,19 @@ int32_t M2VParser::InitParser(){
//Gotta find a sequence header now
MPEGChunk* chunk;
//MPEGChunk* seqHdrChunk;
for(int i = 0; i < chunks.size(); i++){
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...
MPEG2SequenceHeader seqHdr = ParseSequenceHeader(chunk);
m_seqHdr = seqHdr;
ParseSequenceHeader(chunk, m_seqHdr);
//Look for sequence extension to identify mpeg2
binary* pData = chunk->GetPointer();
for(int i = 3; i < chunk->GetSize() - 4; i++){
if(pData[i] == 0x00 && pData[i+1] == 0x00 && pData[i+2] == 0x01 && pData[i+3] == 0xb5 && ((pData[i+4] & 0xF0) == 0x10)){
for(size_t j = 3; i < chunk->GetSize() - 4; i++){
if(pData[j] == 0x00 && pData[j+1] == 0x00 && pData[j+2] == 0x01 && pData[j+3] == 0xb5 && ((pData[j+4] & 0xF0) == 0x10)){
mpegVersion = 2;
break;
}
@ -181,7 +180,7 @@ MediaTime M2VParser::GetFrameDuration(MPEG2PictureHeader picHdr){
}
}
int32_t M2VParser::GetState(){
MPEG2ParserState_e M2VParser::GetState(){
FillQueues();
if(!buffers.empty())
parserState = MPV_PARSER_STATE_FRAME;
@ -193,15 +192,16 @@ int32_t M2VParser::GetState(){
return parserState;
}
int32_t M2VParser::CountBFrames(){
MediaTime M2VParser::CountBFrames(){
//We count after the first chunk.
int32_t count = 0;
MediaTime count = 0;
if(m_eos) return 0;
if(notReachedFirstGOP) return 0;
for(int i = 1; i < chunks.size(); i++){
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);
MPEG2PictureHeader h;
ParsePictureHeader(c, h);
if(h.frameType == MPEG2_B_FRAME){
count += GetFrameDuration(h);
}else{
@ -249,9 +249,9 @@ int32_t M2VParser::QueueFrame(MPEGChunk* seqHdr, MPEGChunk* chunk, MediaTime tim
outBuf->firstRef = (MediaTime)(firstRef * (1000000000/(m_seqHdr.frameRate*2)));
outBuf->secondRef = (MediaTime)(secondRef * (1000000000/(m_seqHdr.frameRate*2)));
}
outBuf->rff = (bool) picHdr.repeatFirstField;
outBuf->tff = (bool) picHdr.topFieldFirst;
outBuf->progressive = (bool) picHdr.progressive;
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;
@ -285,7 +285,7 @@ int32_t M2VParser::FillQueues(){
if(chunk->GetType() == MPEG_VIDEO_SEQUENCE_START_CODE){
if (chunks.size() == 1) return -1;
if(seqHdr) delete seqHdr;
m_seqHdr = ParseSequenceHeader(chunk);
ParseSequenceHeader(chunk, m_seqHdr);
seqHdr = chunk;
}else{
delete chunk; //Skip all non picture, non seq headers
@ -298,8 +298,9 @@ int32_t M2VParser::FillQueues(){
}
chunk = chunks.front();
}
MPEG2PictureHeader picHdr = ParsePictureHeader(chunk);
int bcount;
MPEG2PictureHeader picHdr;
ParsePictureHeader(chunk, picHdr);
MediaTime bcount;
if(myTime == nextSkip){
myTime+=nextSkipDuration;
currentStampingTime=myTime;
@ -318,7 +319,7 @@ int32_t M2VParser::FillQueues(){
}
ShoveRef(myTime);
QueueFrame(seqHdr,chunk,myTime,picHdr);
if(notReachedFirstGOP) notReachedFirstGOP = false;
notReachedFirstGOP = false;
break;
case MPEG2_P_FRAME:
bcount = CountBFrames();

View File

@ -27,10 +27,12 @@
#include <stdio.h>
#include <queue>
#define MPV_PARSER_STATE_FRAME 0
#define MPV_PARSER_STATE_NEED_DATA 1
#define MPV_PARSER_STATE_EOS -1
#define MPV_PARSER_STATE_ERROR -2
enum MPEG2ParserState_e {
MPV_PARSER_STATE_FRAME,
MPV_PARSER_STATE_NEED_DATA,
MPV_PARSER_STATE_EOS,
MPV_PARSER_STATE_ERROR
};
class MPEGFrame {
public:
@ -68,13 +70,13 @@ private:
MediaTime nextSkipDuration;
MediaTime secondRef;
uint8_t mpegVersion;
int32_t parserState;
MPEG2ParserState_e parserState;
MPEGVideoBuffer * mpgBuf;
int32_t InitParser();
void DumpQueues();
int32_t FillQueues();
int32_t CountBFrames();
MediaTime CountBFrames();
void ShoveRef(MediaTime ref);
MediaTime GetFrameDuration(MPEG2PictureHeader picHdr);
int32_t QueueFrame(MPEGChunk* seqHdr, MPEGChunk* chunk, MediaTime timecode, MPEG2PictureHeader picHdr);
@ -94,7 +96,7 @@ public:
return seqHdrChunk;
}
uint8_t GetMPEGVersion(){
uint8_t GetMPEGVersion() const{
return mpegVersion;
}
@ -110,7 +112,7 @@ public:
int32_t WriteData(binary* data, uint32_t dataSize);
//Returns the current state of the parser
int32_t GetState();
MPEG2ParserState_e GetState();
//Sets "end of stream" status on the buffer, forces timestamping of frames waiting.
//Do not call this without good reason.

View File

@ -70,23 +70,14 @@ void MPEGVideoBuffer::UpdateState(){
if(test != -1) //We found a new startcode
chunkEnd = test;
}
if(chunkStart == -1){
if(chunkStart == -1 || chunkEnd == -1){
state = MPEG2_BUFFER_STATE_NEED_MORE_DATA;
return;
}else if(chunkEnd == -1){
state = MPEG2_BUFFER_STATE_NEED_MORE_DATA;
return;
}else{
assert(chunkStart >= 0 && chunkStart < chunkEnd && chunkEnd > 0);
state = MPEG2_BUFFER_STATE_CHUNK_READY;
return;
}
}
uint32_t MPEGVideoBuffer::GetState(){
return state;
}
MPEGChunk * MPEGVideoBuffer::ReadChunk(){
MPEGChunk* myChunk = NULL;
if(state == MPEG2_BUFFER_STATE_CHUNK_READY){
@ -121,14 +112,9 @@ int32_t MPEGVideoBuffer::Feed(binary* data, uint32_t numBytes){
return res;
}
MPEG2SequenceHeader ParseSequenceHeader(MPEGChunk* chunk){
MPEG2SequenceHeader hdr;
void ParseSequenceHeader(MPEGChunk* chunk, MPEG2SequenceHeader & hdr){
binary* pos = chunk->GetPointer();
uint8_t haveSeqExt = 0;
if(chunk->GetType() != MPEG_VIDEO_SEQUENCE_START_CODE){
//printf("Don't feed parse_sequence_header a chunk that isn't a sequence header!!!\n");
return hdr;
}
//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
@ -206,18 +192,15 @@ MPEG2SequenceHeader ParseSequenceHeader(MPEGChunk* chunk){
}else{
hdr.progressiveSequence = 0;
}
return hdr;
}
MPEG2GOPHeader ParseGOPHeader(MPEGChunk* chunk){
MPEG2GOPHeader hdr;
binary* pos = chunk->GetPointer();
uint32_t timecode;
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 hdr;
return false;
}
binary* pos = chunk->GetPointer();
uint32_t timecode;
pos+=4; //skip the startcode
//Parse GOP timecode structure
timecode = ((uint32_t)pos[0] << 24) |
@ -234,19 +217,17 @@ MPEG2GOPHeader ParseGOPHeader(MPEGChunk* chunk){
}else{
hdr.closedGOP = 0;
}
return hdr;
return true;
}
MPEG2PictureHeader ParsePictureHeader(MPEGChunk* chunk){
MPEG2PictureHeader hdr;
binary* pos = chunk->GetPointer();
// int i = 0;
int havePicExt = 0;
uint32_t temp = 0;
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 hdr;
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;
@ -284,5 +265,5 @@ MPEG2PictureHeader ParsePictureHeader(MPEGChunk* chunk){
hdr.progressive = (pos[0] & 0x80);
}
return hdr;
return true;
}

View File

@ -33,10 +33,12 @@
#define MPEG_VIDEO_GOP_START_CODE 0xb8
#define MPEG_VIDEO_USER_START_CODE 0xb2
#define MPEG2_BUFFER_STATE_NEED_MORE_DATA 1
#define MPEG2_BUFFER_STATE_CHUNK_READY 2
#define MPEG2_BUFFER_STATE_EMPTY 0
#define MPEG2_BUFFER_INVALID -1
enum MPEG2BufferState_e {
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
@ -90,11 +92,11 @@ public:
delete [] data;
}
uint8_t GetType(){
inline uint8_t GetType() const {
return type;
}
uint32_t GetSize(){
inline uint32_t GetSize() const{
return size;
}
@ -106,19 +108,19 @@ public:
return data[i];
}
binary * GetPointer(){
inline binary * GetPointer(){
return data;
}
};
MPEG2SequenceHeader ParseSequenceHeader(MPEGChunk* chunk);
MPEG2PictureHeader ParsePictureHeader(MPEGChunk* chunk);
MPEG2GOPHeader ParseGOPHeader(MPEGChunk* chunk);
void ParseSequenceHeader(MPEGChunk* chunk, MPEG2SequenceHeader & hdr);
bool ParsePictureHeader(MPEGChunk* chunk, MPEG2PictureHeader & hdr);
bool ParseGOPHeader(MPEGChunk* chunk, MPEG2GOPHeader & hdr);
class MPEGVideoBuffer{
private:
CircBuffer * myBuffer;
uint32_t state;
MPEG2BufferState_e state;
int32_t chunkStart;
int32_t chunkEnd;
void UpdateState();
@ -135,7 +137,7 @@ public:
delete myBuffer;
}
uint32_t GetState();
inline MPEG2BufferState_e GetState() const { return state; }
int32_t GetFreeBufferSpace(){
return (myBuffer->buf_capacity - myBuffer->bytes_in_buf);