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.
425 lines
9.2 KiB
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;
|
|
}
|
|
|
|
|