You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdemultimedia/mpeglib/lib/mpegplay/videoDecoder.cpp

477 lines
10 KiB

/*
mpeg I video decoder (derived from mpeg_play)
Copyright (C) 2000 Martin Vogt
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Library General Public License as published by
the Free Software Foundation.
For more information look at the file COPYRIGHT in this package
*/
#include "videoDecoder.h"
using namespace std;
VideoDecoder::VideoDecoder(MpegVideoStream* inputStream,
MpegVideoHeader* initSequence) {
/* Check for legal buffer length. */
init_tables();
/* Initialize fields that used to be global */
mpegVideoStream=inputStream;
decoderClass=new DecoderClass(this,mpegVideoStream);
recon=new Recon();
motionVector=new MotionVector();
slice=new Slice();
group=new GOP();
mpegVideoHeader=new MpegVideoHeader();
picture=new Picture();
macroBlock=new MacroBlock(this);
// init this stream with the init sequence
initSequence->copyTo(mpegVideoHeader);
syncState=SYNC_TO_CLOSED_GOP;
extension=new MpegExtension();
frameCounter=0;
}
VideoDecoder::~VideoDecoder() {
delete mpegVideoHeader;
delete picture;
delete decoderClass;
delete recon;
delete motionVector;
delete slice;
delete group;
delete extension;
delete macroBlock;
}
/*
*--------------------------------------------------------------
*
* mpegVidRsrc --
*
* Parses bit stream until MB_QUANTUM number of
* macroblocks have been decoded or current slice or
* picture ends, whichever comes first. If the start
* of a frame is encountered, the frame is time stamped
* with the value passed in time_stamp. If the value
* passed in buffer is not null, the video stream buffer
* is set to buffer and the length of the buffer is
* expected in value passed in through length. The current
* video stream is set to vid_stream. If vid_stream
* is passed as NULL, a new VideoDecoder structure is created
* and initialized and used as the current video stream.
*
* Results:
* A pointer to the video stream structure used.
*
* Side effects:
* Bit stream is irreversibly parsed. If a picture is completed,
* a function is called to display the frame at the correct time.
*
*--------------------------------------------------------------
*/
int VideoDecoder::mpegVidRsrc(PictureArray* pictureArray) {
int back=_SYNC_TO_NONE;
unsigned int data;
int i;
/*
* If called for the first time, find start code, make sure it is a
* sequence start code.
*/
/* Get next 32 bits (size of start codes). */
data=mpegVideoStream->showBits(32);
/*
* Process according to start code (or parse macroblock if not a start code
* at all).
*/
switch (data) {
/*
case PACK_START_CODE:
case SYSTEM_HEADER_START_CODE:
cout << "Packet in Loop **************"<<endl;
mpegVideoStream->flushBits(32);
packet->read_sys(data,vid_stream->bufferReader);
*/
case SEQ_END_CODE:
case ISO_11172_END_CODE: /* handle ISO_11172_END_CODE too */
/* Display last frame. */
// removed!
/* Sequence done. Do the right thing. For right now, exit. */
cout << "******** flushin end code"<<endl;
mpegVideoStream->flushBits(32);
goto done;
break;
case EXT_START_CODE:
cout << "found EXT_START_CODE skipping"<<endl;
mpegVideoStream->flushBits(32);
/* Goto next start code. */
mpegVideoStream->next_start_code();
break;
case USER_START_CODE:
mpegVideoStream->flushBits(32);
/* Goto next start code. */
mpegVideoStream->next_start_code();
break;
case SEQ_START_CODE:
/* Sequence start code. Parse sequence header. */
if (ParseSeqHead() == false) {
printf("SEQ_START_CODE 1-error\n");
goto error;
}
goto done;
case GOP_START_CODE:
/* Group of Pictures start code. Parse gop header. */
if (ParseGOP() == false) {
printf("GOP_START_CODE 1-error\n");
goto error;
}
goto done;
case PICTURE_START_CODE:
/* Picture start code. Parse picture header and first slice header. */
back=ParsePicture();
if (back != _SYNC_TO_NONE) {
//cout << "skip B Frame we are late"<<endl;
return back;
}
// parse ok
if (ParseSlice() == false) {
printf("PICTURE_START_CODE 2-error\n");
goto error;
}
break;
case SEQUENCE_ERROR_CODE:
mpegVideoStream->flushBits(32);
mpegVideoStream->next_start_code();
goto done;
default:
/* Check for slice start code. */
if ((data >= SLICE_MIN_START_CODE) && (data <= SLICE_MAX_START_CODE)) {
/* Slice start code. Parse slice header. */
if (ParseSlice() == false) {
printf("default 1-error\n");
goto error;
}
}
break;
}
/* Parse next MB_QUANTUM macroblocks. */
for (i = 0; i < MB_QUANTUM; i++) {
/* Check to see if actually a startcode and not a macroblock. */
data=mpegVideoStream->showBits(23);
if (data != 0x0) {
/* Not start code. Parse Macroblock. fill yuv pictures*/
if (macroBlock->processMacroBlock(pictureArray) == false) {
goto error;
}
} else {
/* Not macroblock, actually start code. Get start code. */
mpegVideoStream->next_start_code();
/*
* If start code is outside range of slice start codes, frame is
* complete, display frame.
*/
data=mpegVideoStream->showBits(32);
if (((data < SLICE_MIN_START_CODE) || (data > SLICE_MAX_START_CODE)) &&
(data != SEQUENCE_ERROR_CODE)) {
doPictureDisplay(pictureArray);
}
goto done;
}
}
data=mpegVideoStream->showBits(23);
/* Check if we just finished a picture on the MB_QUANTUM macroblock */
if (data == 0x0) {
mpegVideoStream->next_start_code();
data=mpegVideoStream->showBits(32);
if ((data < SLICE_MIN_START_CODE) || (data > SLICE_MAX_START_CODE)) {
doPictureDisplay(pictureArray);
}
}
/* Return pointer to video stream structure. */
goto done;
error:
init_tables();
back=_SYNC_TO_GOP;
mpegVideoHeader->init_quanttables();
goto done;
done:
return back;
}
int VideoDecoder::ParseSeqHead() {
int back;
/* Flush off sequence start code. */
mpegVideoStream->flushBits(32);
back=mpegVideoHeader->parseSeq(mpegVideoStream);
return back;
}
int VideoDecoder::ParseGOP() {
if (syncState==SYNC_TO_CLOSED_GOP) {
syncState=SYNC_HAS_CLOSED_GOP;
}
return group->processGOP(mpegVideoStream);
}
int VideoDecoder::ParsePicture() {
int back;
back=picture->processPicture(mpegVideoStream);
macroBlock->resetPastMacroBlock();
if (back == false) {
return _SYNC_TO_GOP;
}
return _SYNC_TO_NONE;
}
int VideoDecoder::ParseSlice() {
slice->parseSlice(mpegVideoStream);
macroBlock->resetMacroBlock();
decoderClass->resetDCT();
return true;
}
/**
After a seek we can only start with an I frame
*/
void VideoDecoder::resyncToI_Frame() {
syncState=SYNC_TO_CLOSED_GOP;
}
void VideoDecoder::doPictureDisplay(PictureArray* pictureArray) {
// insert end timestamp to current picture
YUVPicture* pic=pictureArray->getCurrent();
unsigned int code_type=picture->getCodeType();
TimeStamp* startTimeStamp=picture->getStartOfPicStamp();
pic->setStartTimeStamp(startTimeStamp);
float rate=mpegVideoHeader->getPictureRate();
pictureArray->setPicturePerSecond(rate);
pic->setMpegPictureType(code_type);
if (syncState < SYNC_HAS_CLOSED_GOP) {
return;
}
if (syncState < SYNC_HAS_I_FRAME_SYNC) {
if (code_type != I_TYPE) {
return;
}
}
if (code_type == I_TYPE) {
YUVPicture* past=pictureArray->getPast();
YUVPicture* future=pictureArray->getFuture();
YUVPicture* current=pictureArray->getCurrent();
YUVPicture* tmp=past;
past = future;
future = current;
current = tmp;
pic=past;
pictureArray->setPast(past);
pictureArray->setCurrent(current);
pictureArray->setFuture(future);
if (syncState < SYNC_HAS_I_FRAME_SYNC) {
syncState=SYNC_HAS_I_FRAME_SYNC;
return;
}
if (syncState == SYNC_HAS_P_FRAME_SYNC) {
syncState=SYNC_HAS_FRAME_SYNC;
return;
}
if (syncState == SYNC_HAS_I_FRAME_SYNC) {
syncState=SYNC_HAS_P_FRAME_SYNC;
return;
}
}
if (code_type == P_TYPE) {
YUVPicture* past=pictureArray->getPast();
YUVPicture* future=pictureArray->getFuture();
YUVPicture* current=pictureArray->getCurrent();
YUVPicture* tmp=past;
past = future;
future = current;
current = tmp;
pic = past;
pictureArray->setPast(past);
pictureArray->setCurrent(current);
pictureArray->setFuture(future);
if (syncState < SYNC_HAS_P_FRAME_SYNC) {
syncState=SYNC_HAS_P_FRAME_SYNC;
return;
}
}
if (code_type == B_TYPE) {
if (syncState == SYNC_HAS_P_FRAME_SYNC) {
syncState=SYNC_HAS_FRAME_SYNC;
YUVPicture* past=pictureArray->getPast();
pic=past;
}
/**
Now check for PTS timeStamp error. It seems some encoders
handles this different.
If the P frame has a timeStamp earlier than our B stamp
we swap them.
*/
YUVPicture* pframe=pictureArray->getFuture();
YUVPicture* bframe=pictureArray->getCurrent();
TimeStamp* pTime=pframe->getStartTimeStamp();
TimeStamp* bTime=bframe->getStartTimeStamp();
double pPTS=pTime->getPTSTimeStamp();
double bPTS=bTime->getPTSTimeStamp();
if (pPTS < bPTS) {
//cout << "********P/B Frame PTS error -> enable swap and pray"<<endl;
bTime->copyTo(pTime);
}
// we display the current picture
// (already set)
}
if (pic == NULL) {
cout << "pic NULL"<<endl;
exit(0);
return;
}
if (syncState < SYNC_HAS_FRAME_SYNC) {
return;
}
double val=pictureArray->getPicturePerSecond();
pic->setPicturePerSecond(val);
TimeStamp* currentStamp=pic->getStartTimeStamp();
frameCounter++;
if (currentStamp->getPTSFlag()==true) {
frameCounter=0;
}
currentStamp->setVideoFrameCounter(frameCounter);
// let plugin "rip" the picture
pictureArray->setYUVPictureCallback(pic);
}