/* 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; }