diff options
Diffstat (limited to 'mpeglib/lib/mpegplay/videoDecoder.cpp')
-rw-r--r-- | mpeglib/lib/mpegplay/videoDecoder.cpp | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/mpeglib/lib/mpegplay/videoDecoder.cpp b/mpeglib/lib/mpegplay/videoDecoder.cpp new file mode 100644 index 00000000..b1971795 --- /dev/null +++ b/mpeglib/lib/mpegplay/videoDecoder.cpp @@ -0,0 +1,476 @@ +/* + 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); +} + + + + + |