cache frame indexes to disk
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "filefunctions.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QStandardPaths>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#ifndef FILEFUNCTIONS_H
|
||||
#define FILEFUNCTIONS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
QString GetUniqueFileIdentifier(const QString& filename);
|
||||
|
||||
QString GetMediaIndexLocation();
|
||||
|
||||
QString GetMediaIndexFilename(const QString& filename);
|
||||
|
||||
#endif // FILEFUNCTIONS_H
|
||||
@@ -25,11 +25,12 @@ extern "C" {
|
||||
#include <libavcodec/avcodec.h>
|
||||
}
|
||||
|
||||
#include <QStatusBar>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QString>
|
||||
#include <QtMath>
|
||||
#include <QDebug>
|
||||
|
||||
#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<frame_index_.size();i++) {
|
||||
if (frame_index_.at(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<int>(static_cast<size_t>(index_file.size()) / sizeof(int64_t)));
|
||||
|
||||
// Read frame index into vector
|
||||
index_file.read(reinterpret_cast<char*>(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<const char*>(frame_index_.constData()),
|
||||
frame_index_.size() * static_cast<int>(sizeof(int64_t)));
|
||||
|
||||
index_file.close();
|
||||
} else {
|
||||
qWarning() << tr("Failed to save index for %1").arg(stream()->footage()->filename());
|
||||
}
|
||||
}
|
||||
|
||||
int FFmpegDecoder::GetFrame()
|
||||
|
||||
@@ -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_;
|
||||
|
||||
Reference in New Issue
Block a user