From 410c1ff06cd6ef0a0d53b3c3dae1e3079af94643 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Wed, 7 Aug 2019 12:14:48 +1000 Subject: [PATCH] cache frame indexes to disk --- app/common/CMakeLists.txt | 2 + app/common/filefunctions.cpp | 43 +++++++++++++ app/common/filefunctions.h | 12 ++++ app/decoder/ffmpeg/ffmpegdecoder.cpp | 95 ++++++++++++++++++++++------ app/decoder/ffmpeg/ffmpegdecoder.h | 6 +- 5 files changed, 139 insertions(+), 19 deletions(-) create mode 100644 app/common/filefunctions.cpp create mode 100644 app/common/filefunctions.h diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index 13ba02e59..b5c3c0a38 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -18,6 +18,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} common/channellayout.h common/clamp.h + common/filefunctions.h + common/filefunctions.cpp common/lerp.h common/rational.h common/rational.cpp diff --git a/app/common/filefunctions.cpp b/app/common/filefunctions.cpp new file mode 100644 index 000000000..926729d8d --- /dev/null +++ b/app/common/filefunctions.cpp @@ -0,0 +1,43 @@ +#include "filefunctions.h" + +#include +#include +#include +#include +#include + +QString GetUniqueFileIdentifier(const QString &filename) +{ + QFileInfo info(filename); + + if (!info.exists()) { + return QString(); + } + + QCryptographicHash hash(QCryptographicHash::Sha1); + + hash.addData(info.absoluteFilePath().toUtf8()); + + hash.addData(info.lastModified().toString().toUtf8()); + + QByteArray result = hash.result(); + + return QString(result.toHex()); +} + +QString GetMediaIndexLocation() +{ + QDir local_appdata_dir(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); + + QDir media_index_dir = local_appdata_dir.filePath("mediaindex"); + + // Attempt to ensure this folder exists + media_index_dir.mkpath("mediaindex"); + + return media_index_dir.absolutePath(); +} + +QString GetMediaIndexFilename(const QString &filename) +{ + return QDir(GetMediaIndexLocation()).filePath(filename); +} diff --git a/app/common/filefunctions.h b/app/common/filefunctions.h new file mode 100644 index 000000000..d006cb955 --- /dev/null +++ b/app/common/filefunctions.h @@ -0,0 +1,12 @@ +#ifndef FILEFUNCTIONS_H +#define FILEFUNCTIONS_H + +#include + +QString GetUniqueFileIdentifier(const QString& filename); + +QString GetMediaIndexLocation(); + +QString GetMediaIndexFilename(const QString& filename); + +#endif // FILEFUNCTIONS_H diff --git a/app/decoder/ffmpeg/ffmpegdecoder.cpp b/app/decoder/ffmpeg/ffmpegdecoder.cpp index 02f58a3e3..590f7fddc 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.cpp +++ b/app/decoder/ffmpeg/ffmpegdecoder.cpp @@ -25,11 +25,12 @@ extern "C" { #include } -#include +#include +#include #include #include -#include +#include "common/filefunctions.h" #include "render/pixelservice.h" FFmpegDecoder::FFmpegDecoder() : @@ -192,13 +193,10 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt int64_t target_ts = qFloor(timecode.toDouble() * rational(avstream_->time_base).flipped().toDouble()); // Index now if we haven't already - if (frame_index_.isEmpty()) { - qDebug() << "No index exists... starting index"; + if (frame_index_.isEmpty() && !LoadIndex()) { Index(); } - qDebug() << "Graph requested timecode:" << target_ts; - // Use index to find closest frame in file for (int i=1;i target_ts) { @@ -207,28 +205,32 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt } } - qDebug() << " The closest in stream is:" << target_ts; - - // Seek to it - avcodec_flush_buffers(codec_ctx_); - av_seek_frame(fmt_ctx_, avstream_->index, target_ts, AVSEEK_FLAG_BACKWARD); + int ret = 0; // Allocate and init a packet for reading encoded data AVPacket pkt; av_init_packet(&pkt); // Cache FFmpeg error code returns - int ret = 0; + ret = 0; + + // Set up seeking loop + int64_t seek_ts = target_ts; + int64_t second_ts = qRound(rational(avstream_->time_base).flipped().toDouble()); // FFmpeg frame retrieve loop while (ret >= 0 && frame_->pts != target_ts) { + + // If the frame timestamp is too large, we need to seek back a little + if (frame_->pts > target_ts || frame_->pts == AV_NOPTS_VALUE) { + avcodec_flush_buffers(codec_ctx_); + av_seek_frame(fmt_ctx_, avstream_->index, seek_ts, AVSEEK_FLAG_BACKWARD); + seek_ts -= second_ts; + } + ret = GetFrame(); - - qDebug() << " Read frame" << frame_->pts; } - qDebug() << " The frame we have is:" << frame_->pts; - // Handle any errors received during the frame retrieve process if (ret < 0) { qWarning() << tr("Failed to retrieve frame from FFmpeg decoder: %1").arg(ret); @@ -256,7 +258,6 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt &dst_data, &dst_linesize); - Q_UNUSED(timecode) Q_UNUSED(length) // Close(); @@ -422,6 +423,11 @@ void FFmpegDecoder::Error(const QString &s) void FFmpegDecoder::Index() { + if (!open_) { + qWarning() << tr("Indexing function tried to run while decoder was closed"); + return; + } + // This should be unnecessary, but just in case... frame_index_.clear(); @@ -437,9 +443,62 @@ void FFmpegDecoder::Index() break; } else { frame_index_.append(frame_->pts); - qDebug() << " Indexed" << frame_->pts; } } + + // Save index to file + SaveIndex(); +} + +QString FFmpegDecoder::GetIndexFilename() +{ + if (!open_) { + qWarning() << tr("GetIndexFilename tried to run while decoder was closed"); + return QString(); + } + + return GetMediaIndexFilename(GetUniqueFileIdentifier(stream()->footage()->filename())) + .append(QString::number(avstream_->index)); +} + +bool FFmpegDecoder::LoadIndex() +{ + // Load index from file + QFile index_file(GetIndexFilename()); + + if (!index_file.exists()) { + return false; + } + + if (index_file.open(QFile::ReadOnly)) { + // Resize based on filesize + frame_index_.resize(static_cast(static_cast(index_file.size()) / sizeof(int64_t))); + + // Read frame index into vector + index_file.read(reinterpret_cast(frame_index_.data()), + index_file.size()); + + index_file.close(); + + return true; + } + + return false; +} + +void FFmpegDecoder::SaveIndex() +{ + // Save index to file + QFile index_file(GetIndexFilename()); + if (index_file.open(QFile::WriteOnly)) { + // Write index in binary + index_file.write(reinterpret_cast(frame_index_.constData()), + frame_index_.size() * static_cast(sizeof(int64_t))); + + index_file.close(); + } else { + qWarning() << tr("Failed to save index for %1").arg(stream()->footage()->filename()); + } } int FFmpegDecoder::GetFrame() diff --git a/app/decoder/ffmpeg/ffmpegdecoder.h b/app/decoder/ffmpeg/ffmpegdecoder.h index 543f9e400..7fb4a6e80 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.h +++ b/app/decoder/ffmpeg/ffmpegdecoder.h @@ -49,9 +49,13 @@ private: void FFmpegErr(int error_code); void Error(const QString& s); - void Index(); int GetFrame(); + void Index(); + QString GetIndexFilename(); + bool LoadIndex(); + void SaveIndex(); + AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt); AVFormatContext* fmt_ctx_;