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/mpegVideoLength.cpp

425 lines
9.2 KiB

/*
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 <iostream>
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"<<endl;
}
realLength=input->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"<<endl;
// we can't find an upper end, thus we are ready
input->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"<<endl;
if (startGOP->substract(endGOP,lengthGOP) == true) {
cout << "this stream counts the time backwards"<<endl;
} else {
cout << "couldnt determine stream length"<<endl;
GOP dummy;
dummy.copyTo(lengthGOP);
}
}
// ok now we have the length but we must calculate
// the length of the whole stream
// we do not jump ofer 600 MB because this makes problems
// on some cdrom.
// here we calculate the real length if upperEnd != realEnd
int hour=lengthGOP->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]"<<endl;
}
// we return always true
// if we really have the start, fine, otherwithe we can
// nothing do about it, nor yet, nor in future, thus
// we have even success (in terms of failure)
return true;
}
int MpegVideoLength::seekToEnd() {
int success;
if (lSysLayer == true) {
success=parseToPTS(endGOP);
} else {
mpegVideoStream->hasBytes(100);
success=parseToGOP(endGOP);
}
if (success == false) {
cout << "picture endcode not found [END]"<<endl;
}
return true;
}
int MpegVideoLength::parseToGOP(GOP* dest) {
int success;
// we try ten attempts
// and the diff between each must be (less) than one second
int successCnt=0;
long maxArea=0;
long area=0;
GOP lastGOP;
GOP currentGOP;
GOP diffGOP;
while(successCnt < 4) {
if (mpegVideoStream->eof()) {
return false;
}
if (input->eof() == true) {
cout << "abort"<<endl;
return false;
}
if (maxArea > 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"<<endl;
}
/*
currentGOP.print("current");
lastGOP.print("last");
diffGOP.print("diff");
*/
if (diffGOP.getHour() != 0) {
successCnt=0;
continue;
}
if (diffGOP.getMinutes() != 0) {
successCnt=0;
continue;
}
if (diffGOP.getSeconds() > 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"<<area<<endl;
return false;
}
}
return true;
}
int MpegVideoLength::parseToPTS(GOP* dest) {
// we try ten attempts
// and the diff between each must be (less) than one second
int successCnt=0;
long startArea=input->getBytePosition();
long maxArea=0;
double lastPTS=0;
double currentPTS=0;
double diffPTS=0;
while(successCnt < 4) {
if (input->eof() == true) {
cout << "abort"<<endl;
return false;
}
maxArea=input->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;
}