diff --git a/app/common/timecodefunctions.cpp b/app/common/timecodefunctions.cpp index 8d1947f8f..c2725290b 100644 --- a/app/common/timecodefunctions.cpp +++ b/app/common/timecodefunctions.cpp @@ -231,6 +231,14 @@ err_fatal: return 0; } +rational Timecode::snap_time_to_timebase(const rational &time, const rational &timebase) +{ + // Just convert to a timestamp in timebase units and back + int64_t timestamp = time_to_timestamp(time, timebase); + + return timestamp_to_time(timestamp, timebase); +} + rational Timecode::timestamp_to_time(const int64_t ×tamp, const rational &timebase) { return rational(timestamp) * timebase; diff --git a/app/common/timecodefunctions.h b/app/common/timecodefunctions.h index a57be0f94..e9bb4d35d 100644 --- a/app/common/timecodefunctions.h +++ b/app/common/timecodefunctions.h @@ -55,6 +55,8 @@ public: static int64_t timecode_to_timestamp(const QString& timecode, const rational& timebase, const Display& display, bool *ok = nullptr); + static rational snap_time_to_timebase(const rational& time, const rational& timebase); + static int64_t time_to_timestamp(const rational& time, const rational& timebase); static int64_t time_to_timestamp(const double& time, const rational& timebase); diff --git a/app/common/timerange.cpp b/app/common/timerange.cpp index fc8d96c7d..3a2564057 100644 --- a/app/common/timerange.cpp +++ b/app/common/timerange.cpp @@ -58,9 +58,13 @@ TimeRange TimeRange::CombineWith(const TimeRange &a) const return Combine(a, *this); } -bool TimeRange::Contains(const TimeRange &a) const +bool TimeRange::Contains(const TimeRange &compare, bool inout_inclusive) const { - return (a.in() >= in() && a.out() <= out()); + if (inout_inclusive) { + return (compare.in() >= in() && compare.out() <= out()); + } else { + return (compare.in() > in() && compare.out() < out()); + } } bool TimeRange::Overlap(const TimeRange &a, const TimeRange &b) @@ -114,7 +118,7 @@ void TimeRangeList::RemoveTimeRange(const TimeRange &range) // This element is entirely encompassed in this range, remove it removeAt(i); i--; - } else if (compare.Contains(range)) { + } else if (compare.Contains(range, false)) { // The remove range is within this element, only choice is to split the element into two TimeRange first(compare.in(), range.in()); TimeRange last(range.out(), compare.out()); @@ -123,10 +127,25 @@ void TimeRangeList::RemoveTimeRange(const TimeRange &range) append(last); } else if (compare.in() < range.in() && compare.out() > range.in()) { // This element's out point overlaps the range's in, we'll trim it - (*this)[i].set_out(range.in()); + TimeRange trimmed = compare; + trimmed.set_out(range.in()); + replace(i, trimmed); } else if (compare.in() < range.out() && compare.out() > range.out()) { // This element's in point overlaps the range's out, we'll trim it - (*this)[i].set_in(range.out()); + TimeRange trimmed = compare; + trimmed.set_in(range.out()); + replace(i, trimmed); } } } + +bool TimeRangeList::ContainsTimeRange(const TimeRange &range) const +{ + for (int i=0;i& threads(); @@ -98,6 +100,9 @@ protected: QHash render_job_info_; +protected slots: + void QueueRecompile(); + private: bool AllProcessorsAreAvailable() const; @@ -136,9 +141,6 @@ private: QVector processor_busy_state_; -private slots: - void QueueRecompile(); - }; #endif // RENDERBACKEND_H diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 140d85d8a..950ae6a92 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -37,79 +37,6 @@ VideoRenderBackend::VideoRenderBackend(QObject *parent) : { } -void VideoRenderBackend::InvalidateCache(const rational &start_range, const rational &end_range) -{ - if (!params_.is_valid()) { - return; - } - - RenderBackend::InvalidateCache(start_range, end_range); - - // Adjust range to min/max values - rational start_range_adj = qMax(rational(0), start_range); - rational end_range_adj = qMin(GetSequenceLength(), end_range); - - qDebug() << "Cache invalidated between" - << start_range_adj.toDouble() - << "and" - << end_range_adj.toDouble(); - - // Snap start_range to timebase - int64_t timestamp = Timecode::time_to_timestamp(start_range_adj, params_.time_base()); - rational true_start = Timecode::timestamp_to_time(timestamp, params_.time_base()); - - if (true_start == end_range_adj) { - // Ensure that a single frame is always rendered - end_range_adj += params_.time_base(); - } - - for (rational r=true_start;r diff) { - cache_queue_.insert(i, new_range); - added = true; - break; - } - } - - if (!added) { - cache_queue_.append(new_range); - } - } - - // Remove frames after this time code if it's changed - frame_cache_.Truncate(GetSequenceLength()); - - // Queue value update - QueueValueUpdate(); - - CacheNext(); -} - bool VideoRenderBackend::InitInternal() { cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height())); @@ -123,14 +50,16 @@ void VideoRenderBackend::CloseInternal() void VideoRenderBackend::ConnectViewer(ViewerOutput *node) { - connect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); - connect(node, SIGNAL(VideoGraphChanged()), this, SLOT(QueueRecompile())); + connect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache); + connect(node, &ViewerOutput::VideoGraphChanged, this, &VideoRenderBackend::QueueRecompile); + connect(node, &ViewerOutput::LengthChanged, this, &VideoRenderBackend::TruncateFrameCacheLength); } void VideoRenderBackend::DisconnectViewer(ViewerOutput *node) { - disconnect(node, SIGNAL(VideoChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); - disconnect(node, SIGNAL(VideoGraphChanged()), this, SLOT(QueueRecompile())); + disconnect(node, &ViewerOutput::VideoChangedBetween, this, &VideoRenderBackend::InvalidateCache); + disconnect(node, &ViewerOutput::VideoGraphChanged, this, &VideoRenderBackend::QueueRecompile); + disconnect(node, &ViewerOutput::LengthChanged, this, &VideoRenderBackend::TruncateFrameCacheLength); } const VideoRenderingParams &VideoRenderBackend::params() const @@ -254,6 +183,25 @@ bool VideoRenderBackend::CanRender() return params_.is_valid(); } +TimeRange VideoRenderBackend::PopNextFrameFromQueue() +{ + TimeRange range = cache_queue_.first(); + + // Snap the range to a single discrete frame + rational snapped_in = Timecode::snap_time_to_timebase(range.in(), params_.time_base()); + + // Check if the range starts earlier, in which case we should render that frame instead + if (range.in() < snapped_in) { + snapped_in -= params_.time_base(); + } + + // Remove this particular frame from the queue + cache_queue_.RemoveTimeRange(TimeRange(snapped_in, snapped_in + params_.time_base())); + + // Return the snapped frame + return TimeRange(snapped_in, snapped_in); +} + void VideoRenderBackend::ThreadCompletedFrame(NodeDependency path, qint64 job_time, QByteArray hash, QVariant value) { if (last_time_requested_ == path.in() || frame_cache_.TimeToHash(last_time_requested_) == hash) { @@ -265,12 +213,12 @@ void VideoRenderBackend::ThreadCompletedDownload(NodeDependency dep, qint64 job_ { SetWorkerBusyState(static_cast(sender()), false); - if (SetFrameHash(dep, hash, job_time)) { - QList hashes_with_time = frame_cache()->FramesWithHash(hash); + SetFrameHash(dep, hash, job_time); - foreach (const rational& t, hashes_with_time) { - emit CachedTimeReady(t, job_time); - } + QList hashes_with_time = frame_cache()->FramesWithHash(hash); + + foreach (const rational& t, hashes_with_time) { + emit CachedTimeReady(t, job_time); } // Queue up a new frame for this worker @@ -302,9 +250,15 @@ void VideoRenderBackend::ThreadHashAlreadyExists(NodeDependency dep, qint64 job_ CacheNext(); } +void VideoRenderBackend::TruncateFrameCacheLength(const rational &length) +{ + // Remove frames after this time code if it's changed + frame_cache_.Truncate(length); +} + bool VideoRenderBackend::TimeIsQueued(const TimeRange &time) const { - return cache_queue_.contains(time); + return cache_queue_.ContainsTimeRange(time); } bool VideoRenderBackend::JobIsCurrent(const NodeDependency &dep, const qint64& job_time) const diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index e60cd388b..bb71040b4 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -57,9 +57,6 @@ public: bool IsRendered(const rational& time) const; -public slots: - virtual void InvalidateCache(const rational &start_range, const rational &end_range) override; - protected: /** * @brief Allocate and start the multithreaded backend @@ -86,6 +83,8 @@ protected: virtual bool CanRender() override; + virtual TimeRange PopNextFrameFromQueue() override; + VideoRenderFrameCache* frame_cache(); const VideoRenderingParams& params() const; @@ -128,6 +127,8 @@ private slots: void ThreadSkippedFrame(NodeDependency dep, qint64 job_time, QByteArray hash); void ThreadHashAlreadyExists(NodeDependency dep, qint64 job_time, QByteArray hash); + void TruncateFrameCacheLength(const rational& length); + }; #endif // VIDEORENDERERBACKEND_H