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.
mplayerthumbs/src/videopreview.cpp

263 lines
8.9 KiB

/***************************************************************************
Copyright (C) 2006
by Marco Gulino <marco@kmobiletools.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
***************************************************************************/
#include "videopreview.h"
#include <tqfile.h>
#include <tqpixmap.h>
#include <tqimage.h>
#include <kstandarddirs.h>
#include <kpixmapsplitter.h>
#include <kmimetype.h>
#include <tqpainter.h>
#include <krandomsequence.h>
#include <tqdatetime.h>
#include <tqregexp.h>
#include <tqprocess.h>
#include <kdebug.h>
#include <ktempdir.h>
#include <kurl.h>
#include <math.h>
#include <tqfileinfo.h>
#include <cstdlib>
#include <unistd.h>
#include "mplayerthumbs.h"
#include "config.h"
extern "C"
{
KDE_EXPORT ThumbCreator *new_creator()
{
return new VideoPreview;
}
}
VideoPreview::VideoPreview()
: m_splitter(0),
m_data(0),
m_dataSize(0)
{
}
VideoPreview::~VideoPreview()
{
delete m_splitter;
delete [] m_data;
delete tmpdir;
delete rand;
delete mplayerprocess;
}
bool VideoPreview::create(const TQString &path, int width, int height, TQImage &img)
{
MPlayerThumbsCfg *cfg=new MPlayerThumbsCfg();
TQFileInfo fi(path);
kdDebug() << "videopreview: file extension=\"" << fi.extension().stripWhiteSpace() << "\"\n";
if( fi.extension().stripWhiteSpace().length() && !cfg->noextensions().grep(fi.extension().stripWhiteSpace(), false)
.isEmpty() )
{
delete cfg;
kdDebug() << "videopreview: matched extension " << fi.extension().prepend('.') << "; exiting.\n";
return false;
}
playerBin=cfg->mplayerbin();
customargs=TQStringList::split(" ", cfg->customargs() );
kdDebug() << "videopreview: customargs=" << cfg->customargs() << " ;;;; " << customargs << endl;
delete cfg;
if(playerBin.length()) kdDebug() << "videopreview: found playerbin from config: " << playerBin << endl;
else
{
playerBin=TDEStandardDirs::findExe("mplayer-bin");
if(!playerBin.length()) playerBin=TDEStandardDirs::findExe("mplayer");
if(!playerBin.length())
{
kdDebug() << "videopreview: mplayer not found, exiting. Run mplayerthumbsconfig to setup mplayer path manually.\n";
return false;
}
kdDebug() << "videopreview: found playerbin from path: " << playerBin << endl;
}
fileinfo.seconds=0;
fileinfo.fps=0;
tmpdir=new KTempDir();
if(tmpdir->name().isNull() ) return false;
rand=new KRandomSequence(TQDateTime::currentDateTime().toTime_t());
mplayerprocess=new TQProcess();
int flags=0;
KURL furl(path);
kdDebug() << "videopreview: url=" << furl << "; local:" << furl.isLocalFile() << endl;
fileinfo.towidth=width;
fileinfo.toheight=height;
TQPixmap pix;
// if(furl.isLocalFile())
// {
flags=framerandom;
TQStringList args;
args << playerBin << path << "-nocache" << "-identify" << "-vo" << "null" << "-frames" << "0"/* << "-nosound" */<< "-ao" << "null";
args+= customargs;
mplayerprocess->setArguments(args);
mplayerprocess->setCommunication( TQProcess::Stdout );
mplayerprocess->start();
TQString lineout;
TQRegExp findsecs("^ID_LENGTH=([\\d]*)");
TQRegExp findfps("^ID_VIDEO_FPS=([\\d]*)");
while (mplayerprocess->isRunning() ) usleep (10);
do {
lineout=mplayerprocess->readLineStdout();
if(findsecs.search( lineout) != -1)
fileinfo.seconds =findsecs.cap(1).toInt();
if(findfps.search( lineout) != -1)
fileinfo.fps=findfps.cap(1).toInt();
if( fileinfo.fps!=0 && fileinfo.seconds!=0 ) break;
} while (lineout != TQString::null );
kdDebug() << "videopreview: find length=" << fileinfo.seconds << ", fps=" << fileinfo.fps << endl;
/* } else
{
flags=frameend;
}*/
#define LASTTRY 3
for(int i=0; i<=LASTTRY; i++)
{
kdDebug() << "videopreview: try " << i << endl;
pix=getFrame(path, ((i<LASTTRY) ? flags : framestart ) );
if(!pix.isNull()) {
uint variance=imageVariance(pix.convertToImage()/*.bits(),( (width+ 7) & ~0x7), width, height, 1 */);
kdDebug() << "videopreview: " << TQFileInfo(path).fileName() << " frame variance: " << variance << "; " <<
((variance<=40 && ( i!=LASTTRY-1))? "!!!DROPPING!!!" : "GOOD :-)" ) << endl;
if(variance>40 || i==LASTTRY-1 ) break;
}
}
if(pix.isNull() )
{
if(tmpdir) tmpdir->unlink();
return false;
}
/** From videocreator.cpp - xine_artsplugin
Copyright (C) 2002 Simon MacMullen
Copyright (C) 2003 Ewald Snel <ewald@rambo.its.tudelft.nl>
* */
// TQPixmap pix( createThumbnail( &frame, width, height ) );
#ifdef STRIPS_SUPPORT
TQPainter painter( &pix );
TQPixmap sprocket;
if (pix.height() < 60)
sprocket = TQPixmap(locate( "data", "videopreview/sprocket-small.png" ));
else if (pix.height() < 90)
sprocket = TQPixmap(locate( "data", "videopreview/sprocket-medium.png" ));
else
sprocket = TQPixmap(locate( "data", "videopreview/sprocket-large.png" ));
for (int y = 0; y < pix.height() + sprocket.height(); y += sprocket.height()) {
painter.drawPixmap( 0, y, sprocket );
}
// End of xine-artsplugin code
#endif
img = pix.convertToImage();
if(tmpdir) tmpdir->unlink();
return true;
}
TQPixmap VideoPreview::getFrame(const TQString &path, int flags)
{
TQStringList args;
kdDebug() << "videopreview: using flags " << flags << endl;
#define START ((fileinfo.seconds*15)/100)
#define END ((fileinfo.seconds*70)/100)
mplayerprocess->setCommunication( 0 );
mplayerprocess->clearArguments();
args.clear();
args << playerBin << path;
if(fileinfo.towidth>fileinfo.toheight) fileinfo.toheight=-2; else fileinfo.towidth=-2;
// switch( flags ){
// case random
// }
if( flags & framerandom )
{
kdDebug() << "videopreview: framerandom\n";
unsigned long start=(unsigned long)(START+(rand->getDouble() * (END - START) ) );
args << "-ss" << TQString::number( start )
<< "-frames" << "4";
} else if (flags & frameend )
{
kdDebug() << "videopreview: frameend\n";
args << "-ss" << TQString::number( fileinfo.seconds - 10 )
<< "-frames" << "4";
} else if (flags & framestart)
{
kdDebug() << "videopreview: framestart\n";
if(!fileinfo.fps) fileinfo.fps=25; // if we've not autodetected a fps rate, let's assume 25fps.. even if it's wrong it shouldn't hurt.
// If we can't skip to a random frame, let's try playing 10 seconds.
args << "-frames" << TQString::number( fileinfo.fps*10 );
}
args << "-nocache" << "-idx" /*@TODO check if it's too slow..*/ << "-ao" << "null"/*"-nosound" << */<< "-speed" << "99" /*<< "-sstep" << "5"*/
<< "-vo" << TQString("jpeg:outdir=%1").arg(tmpdir->name() ) << "-vf" << TQString("scale=%1:%2").arg(fileinfo.towidth).arg(fileinfo.toheight);
args+=customargs;
kdDebug() << "videopreview: Starting MPlayer with" << args.join( " ") << endl;
// return false;
mplayerprocess->setArguments( args);
mplayerprocess->start();
while (mplayerprocess->isRunning() ) usleep (10);
if (tmpdir->qDir()->entryList( "*.jpg" ).isEmpty() ) return 0;
TQString lastframe=tmpdir->qDir()->entryList( "*.jpg" ).last();
kdDebug() << "videopreview: LastFrame==" << lastframe << endl;
TQPixmap retpix(tmpdir->name().append( lastframe ));
return retpix;
}
ThumbCreator::Flags VideoPreview::flags() const
{
return (Flags)(DrawFrame);
}
uint VideoPreview::imageVariance(TQImage image )
{
uint delta=0;
uint avg=0;
uint bytes=image.numBytes();
uint STEPS=bytes/2;
uchar pivot[STEPS];
kdDebug() << "Using " << STEPS << " steps\n";
uchar *bits=image.bits();
// First pass: get pivots and taking average
for( uint i=0; i<STEPS ; i++ ){
pivot[i]=bits[i*(bytes/STEPS)];
avg+=pivot[i];
}
avg=avg/STEPS;
// Second Step: calculate delta (average?)
for (uint i=0; i<STEPS; i++)
{
int curdelta=abs(int(avg-pivot[i]));
delta+=curdelta;
}
return delta/STEPS;
}
#include "videopreview.moc"