/* mpg I video/audio player plugin Copyright (C) 1999 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 "mpegVideoLength.h" #include "../mpegplay/mpegVideoStream.h" #include "../mpegplay/gop.h" #include using namespace std; #define SEARCH_SIZE 1024*1024*6 #define SEEKWINDOW 1024*1024 MpegVideoLength::MpegVideoLength(InputStream* input) { this->input=input; this->mpegVideoStream=new MpegVideoStream(input); startGOP=new GOP(); endGOP=new GOP(); lengthGOP=new GOP(); mpegVideoHeader=new MpegVideoHeader(); lHasStart=false; lHasEnd=false; lHasStream=false; lHasResync=false; lHasSystemStream=false; lHasRawStream=false; lSysLayer=false; mpegSystemStream=new MpegSystemStream(input); mpegSystemHeader=new MpegSystemHeader(); lCanSeek=input->seek(0); if (lCanSeek == false) { cout << "mpegVideoLength: stream does not support seek"<getByteLength(); upperEnd=realLength; if (realLength > 1024*1024*600) { upperEnd=1024*1024*600; } } MpegVideoLength::~MpegVideoLength() { delete startGOP; delete endGOP; delete lengthGOP; delete mpegVideoStream; delete mpegVideoHeader; delete mpegSystemHeader; delete mpegSystemStream; } /** This long and ugly functions initialize a reader from where to get the time informations. All these switches deal with the problem, that we really should have never a while loop, because this makes the decoder thread unresponsive and can lead to deadlocks. */ int MpegVideoLength::firstInitialize() { // no seek means no length detection if (lCanSeek==false) { // no detection possible, initialized ready input->seek(0); return true; } // do we have already a reader from where to get time? if (lHasStream == false) { // still init system ? if (lHasSystemStream == false) { if (mpegSystemStream->firstInitialize(mpegSystemHeader) == true) { lHasSystemStream=true; // if non system, reset everything and work on raw stream if (mpegSystemHeader->getLayer() == _PACKET_SYSLAYER) { lSysLayer=true; } if (lSysLayer == false) { input->seek(0); } } return false; } // if working on syslayer level we dont need the raw stream // set it to true if (lSysLayer == true) { lHasRawStream=true; } if (lHasRawStream == false) { if (mpegVideoStream->firstInitialize(mpegVideoHeader) == true) { lHasRawStream=true; } return false; } lHasStream=true; return false; } if (lHasStart == false) { if (seekToStart() == true) { lHasStart=true; } // clear and jump near the end mpegVideoStream->clear(); int success=input->seek(upperEnd-SEARCH_SIZE); if (success == false) { cout << "mpegVideoStreamStream does not support seek"<seek(0); return true; } return false; } if (lHasResync==false) { if (lSysLayer) { if (mpegSystemStream->nextPacket(mpegSystemHeader) == false) { return false; } } else { if (mpegVideoStream->nextGOP()==false) { return false; } } lHasResync=true; return false; } if (lHasEnd == false) { if (seekToEnd() == true) { lHasEnd=true; if (endGOP->substract(startGOP,lengthGOP) == false) { cout << "substract error in final length detection"<substract(endGOP,lengthGOP) == true) { cout << "this stream counts the time backwards"<getHour(); int minute=lengthGOP->getMinutes(); long seconds=lengthGOP->getSeconds(); seconds=seconds+minute*60+hour*60*60; if (upperEnd > 1) { if (realLength > upperEnd) { float ratio=realLength/upperEnd; float realSeconds=seconds*ratio; hour=(int)(realSeconds/(float)(60*60)); realSeconds=realSeconds-hour*60*60; minute=(int)(realSeconds/60.0); realSeconds=realSeconds-minute*60; seconds=(int)realSeconds; lengthGOP->setHour(hour); lengthGOP->setMinute(minute); lengthGOP->setSecond(seconds); } } } } input->seek(0); return true; } long MpegVideoLength::getLength() { long back=0; back=lengthGOP->getHour()*60*60; back+=lengthGOP->getMinutes()*60; back+=lengthGOP->getSeconds(); // length in second return back; } long MpegVideoLength::getSeekPos(int seconds) { // calculate from seconds to predicted position in stream long back=0; double ratio; ratio=(double)realLength*(double)seconds; ratio=ratio/((double)getLength()+1.0); back=(long)ratio; return back; } // We try to search the first SEARCH_SIZE KB for a valid picture start. int MpegVideoLength::seekToStart() { int success; /** If we are a system Layer we calculate the startGOP by the system Packets */ if (lSysLayer == true) { success=parseToPTS(startGOP); } else { mpegVideoStream->hasBytes(100); success=parseToGOP(startGOP); } if (success == false) { cout << "picture startcode not found [START]"<hasBytes(100); success=parseToGOP(endGOP); } if (success == false) { cout << "picture endcode not found [END]"<eof()) { return false; } if (input->eof() == true) { cout << "abort"< SEARCH_SIZE) { return false; } success=seekValue(GOP_START_CODE,area); maxArea+=area; if (success == false) { continue; } currentGOP.copyTo(&lastGOP); // currentGOP.print("current"); successCnt++; currentGOP.processGOP(mpegVideoStream); if (currentGOP.substract(&lastGOP,&diffGOP) == false) { cout << "substract error"< 8) { successCnt=0; continue; } } currentGOP.copyTo(dest); return true; } int MpegVideoLength::seekValue(unsigned int ,long& valueSeeked) { long start=input->getBytePosition(); long cnt=0; long end=start+SEEKWINDOW; long area=0; // if we have not enough space we skip // because of the mpeg frame garbage "mb_stuffing,etc..." if (end > upperEnd-SEEKWINDOW){ valueSeeked=SEEKWINDOW; return false; } area=end-start; while(mpegVideoStream->nextGOP() == false) { if (mpegVideoStream->eof()) { return false; } cnt++; if (cnt >= area) { valueSeeked=cnt; cout << "nothing found"<getBytePosition(); long maxArea=0; double lastPTS=0; double currentPTS=0; double diffPTS=0; while(successCnt < 4) { if (input->eof() == true) { cout << "abort"<getBytePosition()-startArea; if (maxArea > SEARCH_SIZE) { return false; } if (mpegSystemStream->nextPacket(mpegSystemHeader) == false) { continue; } if (mpegSystemHeader->getPTSFlag()==false) { continue; } // we have a packet lastPTS=currentPTS; currentPTS=mpegSystemHeader->getPTSTimeStamp(); diffPTS=currentPTS-lastPTS; successCnt++; if (diffPTS > 1.0) { successCnt=0; continue; } } // now put it into the gop structure // this is the interface for the time. // a little hack here and there.... unsigned int hour=((long)currentPTS) / 3600; currentPTS=currentPTS-hour*3600; unsigned int minute=((long)currentPTS) / 60; currentPTS=currentPTS-minute*60; unsigned int seconds=((long)currentPTS); dest->setHour(hour); dest->setMinute(minute); dest->setSecond(seconds); return true; }