diff options
-rw-r--r-- | filestreewidget.cpp | 1 | ||||
-rw-r--r-- | fileview.cpp | 1 | ||||
-rw-r--r-- | framecache.cpp | 170 | ||||
-rw-r--r-- | framecache.h | 72 | ||||
-rw-r--r-- | newmoviewizard.cpp | 1 | ||||
-rw-r--r-- | shemov.pro | 6 | ||||
-rw-r--r-- | smglobals.cpp | 126 | ||||
-rw-r--r-- | smglobals.h | 27 |
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; @@ -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; |