/* 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(); rebuild(); mGenerator->start(); QSettings s; mWhen = s.value("ui/grabframe", "00:00:00").toString(); } FrameCache::~FrameCache(){ mGenerator->exit(); } 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); } void FrameCache::rebuild(){ QMutexLocker l(mCacheMx); QDir cdir(mGenerator->cacheDir()); QFileInfoList files = cdir.entryInfoList(QDir::Files); mFrameCache->clear(); foreach(QFileInfo fi, files){ QString base = fi.fileName(); base.chop(fi.suffix().size() + 1); // basename doesn't work, b/c we have filenames w spaces base.chop(7); //remove temp. extension QString when = base.right(8); base.chop(9); //remove when + _, leaving the orig. filename QPair key = qMakePair(base, when); if(mFrameCache->contains(key)){ QFile::remove(fi.absoluteFilePath()); }else{ mFrameCache->insert(key, fi.absoluteFilePath()); } } } FrameCacheGenerator::FrameCacheGenerator(QObject *parent) : QThread(parent), 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::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; }