From 1bcbb69e31090cf71e913419299b52dd4d094bef Mon Sep 17 00:00:00 2001 From: Arno Date: Fri, 29 Mar 2013 12:55:36 +0100 Subject: 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. --- framecache.cpp | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 framecache.cpp (limited to 'framecache.cpp') 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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, QString>(); + mDataQueue = new QQueue >(); + 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, 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 key = qMakePair(fi.fileName(), w); + mCacheMx->lock(); + QPixmap retval; + if(!mFrameCache->contains(key)){ + QString nullPath = mFrameCache->value(qMakePair(key.first, "00:00:00")); + if(!nullPath.isEmpty()){ + mCacheMx->unlock(); + return QPixmap(nullPath); + } + mCacheMx->unlock(); + mSemFree->acquire(); + mDataQueue->enqueue(qMakePair(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 data = qMakePair(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 > *data, QHash, 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 pair(source, pos); + mFrameCache->insert(pair, cacheFile); + } +} + +void FrameCacheGenerator::run(){ + forever{ + mSemUsed->acquire(); + QPair 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 pair(sourceInfo.fileName(), when); + mCacheMx->lock(); + mFrameCache->insert(pair, tempFile.fileName()); + mCacheMx->unlock(); + return true; + } + return false; +} -- cgit v1.2.3-70-g09d2