summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--filestreewidget.cpp1
-rw-r--r--fileview.cpp1
-rw-r--r--framecache.cpp170
-rw-r--r--framecache.h72
-rw-r--r--newmoviewizard.cpp1
-rw-r--r--shemov.pro6
-rw-r--r--smglobals.cpp126
-rw-r--r--smglobals.h27
8 files changed, 254 insertions, 150 deletions
diff --git a/filestreewidget.cpp b/filestreewidget.cpp
index 3635ac2..3f03636 100644
--- a/filestreewidget.cpp
+++ b/filestreewidget.cpp
@@ -43,6 +43,7 @@
#include "hoverwindow.h"
#include "seriestreemodel.h"
#include "seriesmetadatamodel.h"
+#include "framecache.h"
FilesTreeWidget::FilesTreeWidget(QWidget *parent) : QWidget(parent), mSelectedSize(0){
//the view
diff --git a/fileview.cpp b/fileview.cpp
index 6b34f6d..7797d73 100644
--- a/fileview.cpp
+++ b/fileview.cpp
@@ -32,6 +32,7 @@
#include "smglobals.h"
#include "filesystemfileproxy.h"
#include "smdirmodel.h"
+#include "framecache.h"
FileView::FileView(QWidget *parent) : QTreeView(parent), mDeleteA(0) {
setAttribute(Qt::WA_Hover);
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;
+}
diff --git a/framecache.h b/framecache.h
new file mode 100644
index 0000000..509fea6
--- /dev/null
+++ b/framecache.h
@@ -0,0 +1,72 @@
+/*
+ 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.
+*/
+
+#ifndef FRAMECACHE_H
+#define FRAMECACHE_H
+
+#include <QThread>
+#include <QPair>
+#include <QHash>
+#include <QPair>
+#include <QQueue>
+#include <QString>
+
+class QSemaphore;
+class QMutex;
+class FrameCacheGenerator;
+
+class FrameCache : public QObject {
+ Q_OBJECT
+ public:
+ explicit FrameCache(QObject *parent = 0);
+ ~FrameCache();
+ const QPixmap entry(const QString &sourcePath, const QString &when = QString());
+ const QString entryPath(const QString &sourcePath, const QString &when);
+
+ private:
+ QHash<QPair<QString, QString>, QString> *mFrameCache;
+ QQueue<QPair<QString, QString> > *mDataQueue;
+ QString mCacheFile;
+ qint32 mMagic;
+ QSemaphore *mSemFree;
+ QSemaphore *mSemUsed;
+ QMutex *mCacheMx;
+ FrameCacheGenerator *mGenerator;
+ QString mWhen;
+};
+
+class FrameCacheGenerator : public QThread {
+ Q_OBJECT
+ public:
+ explicit FrameCacheGenerator(QObject *parent = 0);
+ void init(QSemaphore *set, QSemaphore *get, QMutex *cachemx, QQueue<QPair<QString, QString> > *data, QHash<QPair<QString, QString>, QString> *cache);
+ const QString cacheFile() const { return mCacheFile; }
+ qint32 magic() const { return mMagic; };
+ void readConfig();
+ void readCache();
+
+ public slots:
+ void run();
+
+ private:
+ bool grabFrame(const QString &sourceFile, QString when);
+ QSemaphore *mSemFree;
+ QSemaphore *mSemUsed;
+ QMutex *mCacheMx;
+ QQueue<QPair<QString, QString> > *mDataQueue;
+ QHash<QPair<QString, QString>, QString> *mFrameCache;
+ QString mCacheDir;
+ QString mCacheFile;
+ //QString mWhen;
+ QString mFfMpegPath;
+ const qint32 mMagic;
+ const QString mCacheSubDir;
+ const QString mCacheFileName;
+
+};
+
+#endif // FRAMECACHE_H
diff --git a/newmoviewizard.cpp b/newmoviewizard.cpp
index 18fe68d..d098f87 100644
--- a/newmoviewizard.cpp
+++ b/newmoviewizard.cpp
@@ -38,6 +38,7 @@
#include "filestreemodel.h"
#include "helper.h"
#include "pictureviewer2.h"
+#include "framecache.h"
NewMovieWizard::NewMovieWizard(QWidget *parent) : QWizard(parent){
mInfoPage = new MovieInfoPage;
diff --git a/shemov.pro b/shemov.pro
index d98d469..d62cd41 100644
--- a/shemov.pro
+++ b/shemov.pro
@@ -41,7 +41,8 @@ SOURCES = main.cpp \
pictureviewer2.cpp \
picfilesmodel.cpp \
smdirwatcher.cpp \
- smdirmodel.cpp
+ smdirmodel.cpp \
+ framecache.cpp
HEADERS = \
filesystemdirproxy.h \
filesystemwidget.h \
@@ -78,6 +79,7 @@ HEADERS = \
pictureviewer2.h \
picfilesmodel.h \
smdirwatcher.h \
- smdirmodel.h
+ smdirmodel.h \
+ framecache.h
LIBS += -lmagic -lXfixes -lX11
RESOURCES = shemov.qrc
diff --git a/smglobals.cpp b/smglobals.cpp
index 6ef4ceb..9240bf2 100644
--- a/smglobals.cpp
+++ b/smglobals.cpp
@@ -29,6 +29,7 @@
#include "pictureviewer2.h"
#include "picfilesmodel.h"
#include "configurationdialog.h"
+#include "framecache.h"
SmGlobals *SmGlobals::mInstance = 0;
@@ -115,9 +116,9 @@ PictureViewer2 *SmGlobals::pictureViewer() {
return mPictureViewer;
}
-SmGlobals::FrameCache *SmGlobals::frameCache() {
+FrameCache *SmGlobals::frameCache() {
if(!mFrameCache){
- mFrameCache = new SmGlobals::FrameCache;
+ mFrameCache = new FrameCache;
}
return mFrameCache;
}
@@ -170,124 +171,3 @@ SmGlobals::SmGlobals() : mPictureViewer(0), mFrameCache(0){
mIcons.insert("Clean tampon", ":/clean_tampon.png");
mDvdSize = Q_INT64_C(4707319808) - 20 * 1024 *1024;
}
-
-//FrameCache
-SmGlobals::FrameCache::FrameCache(QObject *parent) : QObject(parent), mMagic(0xDEADBEEF), mCacheSubDir(".frameCache"), mCacheFileName("cache") {
- readConfig();
- readCache();
-}
-
-SmGlobals::FrameCache::~FrameCache(){
- 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();
-}
-
-void SmGlobals::FrameCache::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);
- mWhen = s.value("ui/grabframe", "00:00:00").toString();
- mFfMpegPath = s.value("paths/ffmpeg").toString();
-}
-
-const QPixmap SmGlobals::FrameCache::entry(const QString &sourcePath, const QString &when){
- const QPair<QString, QString> source = prepFrame(sourcePath, when);
- return QPixmap(mFrameCache.value(source));
-}
-
-const QString SmGlobals::FrameCache::entryPath(const QString &sourcePath, const QString &when){
- const QPair<QString, QString> source = prepFrame(sourcePath, when);
- return mFrameCache.value(source);
-}
-
-const QPair<QString, QString> SmGlobals::FrameCache::prepFrame(const QString &sourceFile, QString when){
- if(when.isEmpty()){
- when = mWhen;
- }
- const QString fileName = QFileInfo(sourceFile).fileName();
- const QPair<QString, QString> source(fileName, when);
- if(!mFrameCache.contains(source)){
- grabFrame(sourceFile, when);
- }
- return source;
-}
-
-void SmGlobals::FrameCache::grabFrame(const QString &sourceFile, QString when){
- QFileInfo sourceInfo(sourceFile);
- if(!sourceInfo.exists()){
- return;
- }
- 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;
- }
- ffmpeg.waitForFinished();
- QPair<QString, QString> pair(sourceInfo.fileName(), when);
- mFrameCache.insert(pair, tempFile.fileName());
- }
-}
-
-void SmGlobals::FrameCache::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);
- }
- cleanup();
-}
-
-/* cleanup function for fucked up framecache:
- Key was full path until recently, not just the filename, so
- duplicates showed up when hovering over the same in different
- paths, eg, archive or filesystem. Should be removed in the future
-*/
-void SmGlobals::FrameCache::cleanup(){
- QHash<QPair<QString, QString>, QString> newFrameCache;
- QHash<QPair<QString, QString>, QString>::const_iterator it = mFrameCache.constBegin();
- while(it != mFrameCache.constEnd()){
- QPair<QString, QString> key = it.key();
- QFileInfo fi(key.first);
- QPair<QString, QString> newEntry(fi.fileName(), key.second);
- if(!newFrameCache.contains(newEntry)){
- newFrameCache.insert(newEntry, it.value());
- }else{
- QFile::remove(it.value());
- }
- ++it;
- }
- mFrameCache = newFrameCache;
-}
diff --git a/smglobals.h b/smglobals.h
index a91d162..3c2b36c 100644
--- a/smglobals.h
+++ b/smglobals.h
@@ -19,34 +19,11 @@ class QAbstractItemModel;
class PictureViewer2;
class QPixmap;
class SeriesTreeWidget;
+class FrameCache;
class SmGlobals : public QObject {
Q_OBJECT
public:
- class FrameCache : public QObject {
- public:
- FrameCache(QObject *parent = 0);
- ~FrameCache();
- void readConfig();
- const QPixmap entry(const QString &sourcePath, const QString &when = QString());
- const QString entryPath(const QString &sourcePath, const QString &when = QString());
-
- private:
- void readCache();
- void grabFrame(const QString &sourceFile, QString when);
- void cleanup();
- const QPair<QString, QString> prepFrame(const QString &sourceFile, QString when);
- QHash<QPair<QString, QString>, QString> mFrameCache;
- const qint32 mMagic;
- const QString mCacheSubDir;
- const QString mCacheFileName;
- QString mCacheDir;
- QString mCacheFile;
- QString mWhen;
- QString mFfMpegPath;
-
- };
-
~SmGlobals();
static SmGlobals *instance();
QAbstractItemModel *model(const QString &which);
@@ -68,7 +45,7 @@ class SmGlobals : public QObject {
QHash<QString, QAbstractItemModel*> mModels;
PictureViewer2 *mPictureViewer;
SeriesTreeWidget *mSeriesTreeWidget;
- SmGlobals::FrameCache *mFrameCache;
+ FrameCache *mFrameCache;
QSize mCursorSize;
QHash<QString, QString> mIcons;
qint64 mDvdSize;