summaryrefslogtreecommitdiffstats
path: root/framecache.cpp
diff options
context:
space:
mode:
authorArno <am@disconnect.de>2013-03-29 12:55:36 +0100
committerArno <am@disconnect.de>2013-03-29 12:55:36 +0100
commit1bcbb69e31090cf71e913419299b52dd4d094bef (patch)
tree07fc7ed7ff38b68ed996ba398e564219e85758f5 /framecache.cpp
parent110271a7db1ef05769bcfb5c936caad82bc0e498 (diff)
downloadSheMov-1bcbb69e31090cf71e913419299b52dd4d094bef.tar.gz
SheMov-1bcbb69e31090cf71e913419299b52dd4d094bef.tar.bz2
SheMov-1bcbb69e31090cf71e913419299b52dd4d094bef.zip
Make FrameCache threaded
create snapshot pics in a separate thread. Also use the first frame available if the clip isn't long enough for the configured frame.
Diffstat (limited to 'framecache.cpp')
-rw-r--r--framecache.cpp170
1 files changed, 170 insertions, 0 deletions
diff --git a/framecache.cpp b/framecache.cpp
new file mode 100644
index 0000000..ef5329a
--- /dev/null
+++ b/framecache.cpp
@@ -0,0 +1,170 @@
+/*
+ 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.
+*/
+
+#include <QFile>
+#include <QDataStream>
+#include <QSettings>
+#include <QDir>
+#include <QSemaphore>
+#include <QMutex>
+#include <QProcess>
+#include <QTemporaryFile>
+#include <QPixmap>
+
+#include "framecache.h"
+
+FrameCache::FrameCache(QObject *parent) : QObject(parent){
+ mSemFree = new QSemaphore(128); // 128 queued requests for a frame, should be enough
+ mSemUsed = new QSemaphore;
+ mFrameCache = new QHash<QPair<QString, QString>, QString>();
+ mDataQueue = new QQueue<QPair<QString, QString> >();
+ mCacheMx = new QMutex;
+ mGenerator = new FrameCacheGenerator(this);
+ mGenerator->init(mSemFree, mSemUsed, mCacheMx, mDataQueue, mFrameCache);
+ mGenerator->readConfig();
+ mGenerator->readCache();
+ mCacheFile = mGenerator->cacheFile();
+ mMagic = mGenerator->magic();
+ mGenerator->start();
+ QSettings s;
+ mWhen = s.value("ui/grabframe", "00:00:00").toString();
+}
+
+FrameCache::~FrameCache(){
+ mGenerator->exit();
+ QFile outfile(mCacheFile);
+ outfile.open(QIODevice::WriteOnly | QIODevice::Truncate);
+ QDataStream ds(&outfile);
+ ds << (qint32)mMagic;
+ for(QHash<QPair<QString, QString>, QString>::const_iterator it = mFrameCache->constBegin(); it != mFrameCache->constEnd(); ++it){
+ ds << it.key().first << it.key().second << it.value();
+ }
+ outfile.close();
+}
+
+const QPixmap FrameCache::entry(const QString &sourcePath, const QString &when){
+ QFileInfo fi(sourcePath);
+ QString w = when.isEmpty() ? mWhen : when;
+ QPair<QString, QString> key = qMakePair<QString, QString>(fi.fileName(), w);
+ mCacheMx->lock();
+ QPixmap retval;
+ if(!mFrameCache->contains(key)){
+ QString nullPath = mFrameCache->value(qMakePair<QString, QString>(key.first, "00:00:00"));
+ if(!nullPath.isEmpty()){
+ mCacheMx->unlock();
+ return QPixmap(nullPath);
+ }
+ mCacheMx->unlock();
+ mSemFree->acquire();
+ mDataQueue->enqueue(qMakePair<QString, QString>(sourcePath, when));
+ mSemUsed->release();
+ return QPixmap();
+ }
+ retval = QPixmap(mFrameCache->value(key));
+ mCacheMx->unlock();
+ return retval;
+
+}
+
+const QString FrameCache::entryPath(const QString &sourcePath, const QString &when){
+ QFileInfo fi(sourcePath);
+ if(!fi.exists()){
+ return QString();
+ }
+ QPair<QString, QString> data = qMakePair<QString, QString>(fi.fileName(), when);
+ QMutexLocker l(mCacheMx);
+ return mFrameCache->value(data);
+}
+
+FrameCacheGenerator::FrameCacheGenerator(QObject *parent) : QThread(parent), mMagic(0xDEADBEEF), mCacheSubDir(".frameCache"), mCacheFileName("cache") {}
+
+void FrameCacheGenerator::init(QSemaphore *set, QSemaphore *get, QMutex *cachemx, QQueue<QPair<QString, QString> > *data, QHash<QPair<QString, QString>, QString> *cache){
+ mSemFree = set;
+ mSemUsed = get;
+ mDataQueue = data;
+ mCacheMx = cachemx;
+ mFrameCache = cache;
+}
+
+void FrameCacheGenerator::readConfig(){
+ QSettings s;
+ QString archive = s.value("paths/archivedir").toString();
+ if(archive.isEmpty()){
+ return;
+ }
+ QDir archiveDir(archive);
+ if(!archiveDir.exists(mCacheSubDir)){
+ archiveDir.mkdir(mCacheSubDir);
+ }
+ mCacheDir = QString("%1/%2").arg(archive).arg(mCacheSubDir);
+ mCacheFile = QString("%1/%2").arg(mCacheDir).arg(mCacheFileName);
+
+ mFfMpegPath = s.value("paths/ffmpeg").toString();
+}
+
+void FrameCacheGenerator::readCache(){
+ QFile cache(mCacheFile);
+ cache.open(QIODevice::ReadOnly);
+ QDataStream ds(&cache);
+ qint32 magic;
+ ds >> magic;
+ if(magic != mMagic){
+ return;
+ }
+ while(!ds.atEnd()){
+ QString source, pos, cacheFile;
+ ds >> source >> pos >> cacheFile;
+ QFileInfo fi(cacheFile);
+ if(fi.size() == 0){
+ QFile::remove(fi.absoluteFilePath());
+ continue;
+ }
+ QPair<QString, QString> pair(source, pos);
+ mFrameCache->insert(pair, cacheFile);
+ }
+}
+
+void FrameCacheGenerator::run(){
+ forever{
+ mSemUsed->acquire();
+ QPair<QString, QString> cur = mDataQueue->dequeue();
+ if(!grabFrame(cur.first, cur.second)){
+ grabFrame(cur.first, "00:00:00");
+ }
+ mSemFree->release();
+ }
+}
+
+bool FrameCacheGenerator::grabFrame(const QString &sourceFile, QString when){
+ QFileInfo sourceInfo(sourceFile);
+ if(!sourceInfo.exists()){
+ return false;
+ }
+ QString tmpTemplate = QString("%1/%2_%3-XXXXXX.png").arg(mCacheDir).arg(sourceInfo.fileName()).arg(when);
+ QTemporaryFile tempFile(tmpTemplate);
+ tempFile.setAutoRemove(false);
+ if(tempFile.open()){
+ QStringList ffMpegArgs = QStringList() << "-vframes" << "1" << "-ss" << when << "-i" << sourceFile << "-y" << tempFile.fileName();
+ QProcess ffmpeg;
+ ffmpeg.start(mFfMpegPath, ffMpegArgs);
+ if(!ffmpeg.waitForStarted()){
+ return false;
+ }
+ ffmpeg.waitForFinished();
+ QFileInfo tfi(tempFile.fileName());
+ if(tfi.size() == 0){
+ tempFile.remove();
+ return false;
+ }
+ QPair<QString, QString> pair(sourceInfo.fileName(), when);
+ mCacheMx->lock();
+ mFrameCache->insert(pair, tempFile.fileName());
+ mCacheMx->unlock();
+ return true;
+ }
+ return false;
+}