diff --git a/app/decoder/ffmpeg/ffmpegdecoder.cpp b/app/decoder/ffmpeg/ffmpegdecoder.cpp index 5f448295d..e3f6e3368 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.cpp +++ b/app/decoder/ffmpeg/ffmpegdecoder.cpp @@ -218,8 +218,8 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt if (got_frame && (frame_->pts > target_ts || frame_->pts == AV_NOPTS_VALUE)) { // If we already tried seeking to 0 though, there's nothing we can do so we error here if (last_backtrack) { - Error(tr("FFmpeg failed to seek to the correct location")); - return nullptr; + // Must be the earliest frame in the file + break; } // We can't seek earlier than 0, so if this is a 0-seek, don't try any more times after this attempt @@ -597,6 +597,14 @@ int64_t FFmpegDecoder::GetClosestTimestampInIndex(const int64_t &ts) Index(); } + if (frame_index_.isEmpty()) { + return -1; + } + + if (ts <= 0) { + return 0; + } + // Use index to find closest frame in file for (int i=1;i ClipBlock::RunDependencies(NodeOutput *output, const rational &time) +{ + QList deps; + + if (output == texture_output() && texture_input_->IsConnected()) { + deps.append(NodeDependency(texture_input_->get_connected_output(), SequenceToMediaTime(time))); + } + + return deps; +} diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index f611c1f6c..ae5005216 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -44,6 +44,8 @@ public: virtual void InvalidateCache(NodeInput *from, const rational &start_range, const rational &end_range) override; + virtual QList RunDependencies(NodeOutput *output, const rational &time) override; + protected: virtual QVariant Value(NodeOutput* output, const rational& time) override; diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 75873f12b..309e1c15d 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -97,12 +97,13 @@ void MediaInput::SetFootage(Footage *f) footage_input_->set_value(PtrToValue(f)); } -void MediaInput::Hash(QCryptographicHash *hash, const rational &time) +void MediaInput::Hash(QCryptographicHash *hash, NodeOutput *from, const rational &time) { - Node::Hash(hash, time); + Node::Hash(hash, from, time); // Use frame value from Decoder - if (SetupDecoder()) { + if (from == texture_output_ && SetupDecoder()) { + qDebug() << "[MediaInput] Hashing pts" << decoder_->GetTimestampFromTime(time); hash->addData(QString::number(decoder_->GetTimestampFromTime(time)).toUtf8()); // FIXME: Add OCIO data // FIXME: Add alpha association value diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index f0278a2f0..e0240130b 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -51,7 +51,7 @@ public: void SetFootage(Footage* f); - virtual void Hash(QCryptographicHash *hash, const rational &time) override; + virtual void Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time) override; diff --git a/app/node/node.cpp b/app/node/node.cpp index 8107c6302..ae987e1ae 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -77,7 +77,9 @@ void Node::InvalidateCache(NodeInput* from, const rational &start_range, const r // If the Node is an output, relay the signal to any Nodes that are connected to it if (param->type() == NodeParam::kOutput) { - foreach (NodeEdgePtr edge, param->edges()) { + QVector edges = param->edges(); + + foreach (NodeEdgePtr edge, edges) { NodeInput* connected_input = edge->input(); Node* connected_node = connected_input->parent(); @@ -298,7 +300,7 @@ void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time QList deps = RunDependencies(from, time); foreach (const NodeDependency& dep, deps) { // Hash the connected node - dep.node()->parent()->Hash(hash, dep.node(), time); + dep.node()->parent()->Hash(hash, dep.node(), dep.time()); } } diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index 936306a6c..277a1c95e 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -90,6 +90,11 @@ void TrackOutput::Refresh() foreach (Block* b, block_cache_) { if (!detect_attached_blocks.contains(b)) { + // If the current block was removed, stop referencing it + if (current_block_ == b) { + current_block_ = this; + } + emit BlockRemoved(b); } } diff --git a/app/node/processor/renderer/renderer.cpp b/app/node/processor/renderer/renderer.cpp index d9b2872d3..0d04e35a6 100644 --- a/app/node/processor/renderer/renderer.cpp +++ b/app/node/processor/renderer/renderer.cpp @@ -96,18 +96,22 @@ QVariant RendererProcessor::Value(NodeOutput* output, const rational& time) return 0; } - QString fn = CachePathName(time); - if (QFileInfo::exists(fn)) { - auto in = OIIO::ImageInput::open(fn.toStdString()); + // Find frame in map + if (time_hash_map_.contains(time)) { + QString fn = CachePathName(time_hash_map_[time]); - if (in) { - in->read_image(PixelService::GetPixelFormatInfo(format_).oiio_desc, cache_frame_load_buffer_.data()); + if (QFileInfo::exists(fn)) { + auto in = OIIO::ImageInput::open(fn.toStdString()); - in->close(); + if (in) { + in->read_image(PixelService::GetPixelFormatInfo(format_).oiio_desc, cache_frame_load_buffer_.data()); - master_texture_->Upload(cache_frame_load_buffer_.data()); + in->close(); - return QVariant::fromValue(master_texture_); + master_texture_->Upload(cache_frame_load_buffer_.data()); + + return QVariant::fromValue(master_texture_); + } } } } @@ -122,6 +126,8 @@ void RendererProcessor::Release() void RendererProcessor::InvalidateCache(NodeInput* from, const rational &start_range, const rational &end_range) { + Q_UNUSED(from) + qDebug() << "[RendererProcessor] Cache invalidated between" << start_range.toDouble() << "and" @@ -136,17 +142,12 @@ void RendererProcessor::InvalidateCache(NodeInput* from, const rational &start_r for (rational r=true_start_range;r<=end_range;r+=timebase_) { if (!cache_queue_.contains(r)) { cache_queue_.append(r); - - QString fn = CachePathName(r); - if (QFileInfo::exists(fn)) { - QFile(fn).remove(); - } } + + time_hash_map_.remove(r); } CacheNext(); - - Node::InvalidateCache(from, start_range, end_range); } void RendererProcessor::SetTimebase(const rational &timebase) @@ -209,7 +210,7 @@ void RendererProcessor::Start() threads_.resize(background_thread_count); for (int i=0;i(ctx, effective_width_, effective_height_, format_, mode_); + threads_[i] = std::make_shared(this, ctx, effective_width_, effective_height_, format_, mode_); threads_[i]->StartThread(QThread::LowPriority); // Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the @@ -300,16 +301,21 @@ void RendererProcessor::CacheNext() caching_ = true; } -QString RendererProcessor::CachePathName(const rational &time) +QString RendererProcessor::CachePathName(const QByteArray &hash) { QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_); this_cache_dir.mkpath("."); - QString filename = QString("%1.%2.exr").arg(QString::number(time.numerator()), QString::number(time.denominator())); + QString filename = QString("%1.exr").arg(QString(hash.toHex())); return this_cache_dir.filePath(filename); } +bool RendererProcessor::HasHash(const QByteArray &hash) +{ + return QFileInfo::exists(CachePathName(hash)); +} + void RendererProcessor::CalculateEffectiveDimensions() { effective_width_ = width_ / divider_; @@ -322,14 +328,16 @@ void RendererProcessor::ThreadCallback() // Threads are all done now, time to proceed caching_ = false; - RenderTexturePtr texture = texture_input_->get_value(cache_frame_).value(); + RenderTexturePtr texture = master_thread_->texture(); if (texture != nullptr) { - QString fn = CachePathName(cache_frame_); + QString fn = CachePathName(master_thread_->hash()); download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture, fn, cache_frame_); last_download_thread_++; } + time_hash_map_.insert(cache_frame_, master_thread_->hash()); + CacheNext(); } } @@ -347,7 +355,7 @@ void RendererProcessor::ThreadRequestSibling(NodeDependency dep) void RendererProcessor::DownloadThreadFinished(const rational& time) { // Check if we just downloaded (akak finished caching) the frame we're currently on - if (time == last_requested_time_ && texture_output_->IsConnected()) { + if (texture_output_->IsConnected() && time == last_requested_time_) { // Send invalidate cache signal to all nodes connected to the texture output QVector edges = texture_output()->edges(); diff --git a/app/node/processor/renderer/renderer.h b/app/node/processor/renderer/renderer.h index cbfd3c454..4a60717d6 100644 --- a/app/node/processor/renderer/renderer.h +++ b/app/node/processor/renderer/renderer.h @@ -83,6 +83,11 @@ public: void SetDivider(const int& divider); + /** + * @brief Return whether a frame with this hash already exists + */ + bool HasHash(const QByteArray& hash); + /** * @brief Return current instance of a RenderThread (or nullptr if there is none) * @@ -126,7 +131,7 @@ private: /** * @brief Return the path of the cached image at this time */ - QString CachePathName(const rational& time); + QString CachePathName(const QByteArray &hash); /** * @brief Internal list of RenderProcessThreads @@ -175,6 +180,8 @@ private: RenderTexturePtr master_texture_; + QMap time_hash_map_; + private slots: void ThreadCallback(); diff --git a/app/node/processor/renderer/rendererprocessthread.cpp b/app/node/processor/renderer/rendererprocessthread.cpp index a44a8507f..ed323adfc 100644 --- a/app/node/processor/renderer/rendererprocessthread.cpp +++ b/app/node/processor/renderer/rendererprocessthread.cpp @@ -20,12 +20,16 @@ #include "rendererprocessthread.h" -RendererProcessThread::RendererProcessThread(QOpenGLContext *share_ctx, +#include "renderer.h" + +RendererProcessThread::RendererProcessThread(RendererProcessor* parent, + QOpenGLContext *share_ctx, const int &width, const int &height, const olive::PixelFormat &format, const olive::RenderMode &mode) : - RendererThreadBase(share_ctx, width, height, format, mode) + RendererThreadBase(share_ctx, width, height, format, mode), + parent_(parent) { } @@ -56,6 +60,16 @@ bool RendererProcessThread::Queue(const NodeDependency& dep, bool wait) return true; } +const QByteArray &RendererProcessThread::hash() +{ + return hash_; +} + +RenderTexturePtr RendererProcessThread::texture() +{ + return texture_; +} + void RendererProcessThread::ProcessLoop() { while (!Cancelled()) { @@ -71,20 +85,27 @@ void RendererProcessThread::ProcessLoop() NodeOutput* output_to_process = path_.node(); Node* node_to_process = output_to_process->parent(); - QList deps = node_to_process->RunDependencies(output_to_process, path_.time()); + // Check hash + QCryptographicHash hasher(QCryptographicHash::Sha1); + node_to_process->Hash(&hasher, output_to_process, path_.time()); + hash_ = hasher.result(); - // Ask for other threads to run these deps while we're here - if (!deps.isEmpty()) { - for (int i=1;iHasHash(hash_)) { + QList deps = node_to_process->RunDependencies(output_to_process, path_.time()); + + // Ask for other threads to run these deps while we're here + if (!deps.isEmpty()) { + for (int i=1;iget_value(path_.time()).value(); + + render_instance()->context()->functions()->glFinish(); } - // Get the requested value - output_to_process->get_value(path_.time()); - - render_instance()->context()->functions()->glFinish(); - emit FinishedPath(); } } diff --git a/app/node/processor/renderer/rendererprocessthread.h b/app/node/processor/renderer/rendererprocessthread.h index bb875defd..0953ea489 100644 --- a/app/node/processor/renderer/rendererprocessthread.h +++ b/app/node/processor/renderer/rendererprocessthread.h @@ -23,11 +23,14 @@ #include "rendererthreadbase.h" +class RendererProcessor; + class RendererProcessThread : public RendererThreadBase { Q_OBJECT public: - RendererProcessThread(QOpenGLContext* share_ctx, + RendererProcessThread(RendererProcessor* parent, + QOpenGLContext* share_ctx, const int& width, const int& height, const olive::PixelFormat& format, @@ -35,6 +38,10 @@ public: bool Queue(const NodeDependency &dep, bool wait); + const QByteArray& hash(); + + RenderTexturePtr texture(); + protected: virtual void ProcessLoop() override; @@ -44,10 +51,16 @@ signals: void FinishedPath(); private: + RendererProcessor* parent_; + NodeDependency path_; rational time_; + QByteArray hash_; + + RenderTexturePtr texture_; + }; using RendererProcessThreadPtr = std::shared_ptr;