From ca47792dc75308272b86390567048f2be44d79ea Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Tue, 1 Nov 2022 12:44:01 -0700 Subject: [PATCH] nodes: clamp footage retrieval times for clips Fixes #2070 --- app/codec/ffmpeg/ffmpegencoder.cpp | 2 +- app/node/block/clip/clip.cpp | 19 +++++++++++++++---- app/node/block/clip/clip.h | 2 +- app/node/block/transition/transition.cpp | 4 ++-- app/node/block/transition/transition.h | 2 +- app/node/node.cpp | 4 ++-- app/node/node.h | 2 +- app/node/output/track/track.cpp | 12 ++++++------ app/node/output/track/track.h | 2 +- app/node/output/viewer/viewer.cpp | 6 +++--- app/node/time/timeoffset/timeoffsetnode.cpp | 4 ++-- app/node/time/timeoffset/timeoffsetnode.h | 2 +- app/node/time/timeremap/timeremap.cpp | 4 ++-- app/node/time/timeremap/timeremap.h | 2 +- app/node/traverser.cpp | 6 +++--- 15 files changed, 42 insertions(+), 31 deletions(-) diff --git a/app/codec/ffmpeg/ffmpegencoder.cpp b/app/codec/ffmpeg/ffmpegencoder.cpp index 403bfab71..da6c8b510 100644 --- a/app/codec/ffmpeg/ffmpegencoder.cpp +++ b/app/codec/ffmpeg/ffmpegencoder.cpp @@ -290,7 +290,7 @@ bool FFmpegEncoder::WriteAudio(const SampleBuffer &audio) size_t end = audio.sample_count(); const size_t max_frame = 48000; - while (start < end) { + while (result && start < end) { // Create input buffer uint8_t** input_data = nullptr; size_t input_sample_count = std::min(end - start, max_frame); diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index 4d4bf1ebd..684d0112d 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -458,15 +458,26 @@ void ClipBlock::InputValueChangedEvent(const QString &input, int element) } } -TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const +TimeRange ClipBlock::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const { Q_UNUSED(element) if (input == kBufferIn) { - return TimeRange(SequenceToMediaTime(input_time.in()), SequenceToMediaTime(input_time.out())); + rational in = input_time.in(); + rational out = input_time.out(); + + if (clamp) { + in = std::max(in, rational(0)); + out = std::min(out, length()); + } + + in = SequenceToMediaTime(in); + out = SequenceToMediaTime(out); + + return TimeRange(in, out); } - return super::InputTimeAdjustment(input, element, input_time); + return super::InputTimeAdjustment(input, element, input_time, clamp); } TimeRange ClipBlock::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const @@ -540,7 +551,7 @@ void ClipBlock::ConnectedToPreviewEvent() TimeRange ClipBlock::media_range() const { - return InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length())); + return InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()), false); } MultiCamNode *ClipBlock::FindMulticam() diff --git a/app/node/block/clip/clip.h b/app/node/block/clip/clip.h index ddbbd881a..19987c7fb 100644 --- a/app/node/block/clip/clip.h +++ b/app/node/block/clip/clip.h @@ -68,7 +68,7 @@ public: virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options) override; - virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const override; virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; diff --git a/app/node/block/transition/transition.cpp b/app/node/block/transition/transition.cpp index f0590e5c5..3eda77d5f 100644 --- a/app/node/block/transition/transition.cpp +++ b/app/node/block/transition/transition.cpp @@ -287,7 +287,7 @@ void TransitionBlock::InputDisconnectedEvent(const QString &input, int element, } } -TimeRange TransitionBlock::InputTimeAdjustment(const QString &input, int element, const TimeRange &input_time) const +TimeRange TransitionBlock::InputTimeAdjustment(const QString &input, int element, const TimeRange &input_time, bool clamp) const { if (input == kInBlockInput || input == kOutBlockInput) { Block* block = dynamic_cast(GetConnectedOutput(input)); @@ -296,7 +296,7 @@ TimeRange TransitionBlock::InputTimeAdjustment(const QString &input, int element } } - return super::InputTimeAdjustment(input, element, input_time); + return super::InputTimeAdjustment(input, element, input_time, clamp); } TimeRange TransitionBlock::OutputTimeAdjustment(const QString &input, int element, const TimeRange &input_time) const diff --git a/app/node/block/transition/transition.h b/app/node/block/transition/transition.h index 554a7c469..d16a195bd 100644 --- a/app/node/block/transition/transition.h +++ b/app/node/block/transition/transition.h @@ -83,7 +83,7 @@ protected: virtual void InputDisconnectedEvent(const QString& input, int element, Node *output) override; - virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const override; virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; diff --git a/app/node/node.cpp b/app/node/node.cpp index d843a460a..4d4f362cf 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -959,7 +959,7 @@ void Node::InvalidateCache(const TimeRange &range, const QString &from, int elem SendInvalidateCache(range, options); } -TimeRange Node::InputTimeAdjustment(const QString &, int, const TimeRange &input_time) const +TimeRange Node::InputTimeAdjustment(const QString &, int, const TimeRange &input_time, bool clamp) const { // Default behavior is no time adjustment at all return input_time; @@ -1727,7 +1727,7 @@ TimeRange Node::TransformTimeTo(TimeRange time, Node *target, TransformTimeDirec if (dir == kTransformTowardsInput) { for (auto it=path.crbegin(); it!=path.crend(); it++) { const NodeInput &i = (*it); - time = i.node()->InputTimeAdjustment(i.input(), i.element(), time); + time = i.node()->InputTimeAdjustment(i.input(), i.element(), time, false); } } else { // Traverse in output direction diff --git a/app/node/node.h b/app/node/node.h index 253788cbe..f5e5a8485 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -907,7 +907,7 @@ public: * If this node modifies the `time` (i.e. a clip converting sequence time to media time), this function should be * overridden to do so. Also make sure to override OutputTimeAdjustment() to provide the inverse function. */ - virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const; /** * @brief The inverse of InputTimeAdjustment() diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index f2bed9064..be6843eaf 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -146,7 +146,7 @@ void Track::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeVal } } -TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const +TimeRange Track::InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const { if (input == kBlockInput && element >= 0) { int cache_index = GetCacheIndexFromArrayIndex(element); @@ -156,7 +156,7 @@ TimeRange Track::InputTimeAdjustment(const QString& input, int element, const Ti } } - return Node::InputTimeAdjustment(input, element, input_time); + return Node::InputTimeAdjustment(input, element, input_time, clamp); } TimeRange Track::OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const @@ -647,16 +647,16 @@ void Track::ProcessAudioTrack(const NodeValueRow &value, const NodeGlobals &glob TimeRange range_for_block(qMax(b->in(), range.in()), qMin(b->out(), range.out())); + qint64 source_offset = 0; qint64 destination_offset = globals.aparams().time_to_samples(range_for_block.in() - range.in()); qint64 max_dest_sz = globals.aparams().time_to_samples(range_for_block.length()); // Destination buffer SampleBuffer samples_from_this_block = it->second.toSamples(); - ClipBlock *clip_cast = dynamic_cast(b); if (samples_from_this_block.is_allocated()) { // If this is a clip, we might have extra speed/reverse information - if (clip_cast) { + if (ClipBlock *clip_cast = dynamic_cast(b)) { double speed_value = clip_cast->speed(); bool reversed = clip_cast->reverse(); @@ -711,11 +711,11 @@ void Track::ProcessAudioTrack(const NodeValueRow &value, const NodeGlobals &glob } } - qint64 copy_length = qMin(max_dest_sz, qint64(samples_from_this_block.sample_count() - destination_offset)); + qint64 copy_length = qMin(max_dest_sz, qint64(samples_from_this_block.sample_count() - source_offset)); // Copy samples into destination buffer for (int i=0; ithumbnail_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly); if (autocache_input_video_) { - TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, GetVideoLength())); + TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, GetVideoLength()), false); connected->video_frame_cache()->Request(range.Intersected(max_range)); } } else if (from == kSamplesInput) { - TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, GetAudioLength())); + TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, GetAudioLength()), false); if (waveform_requests_enabled_) { connected->waveform_cache()->Request(range.Intersected(max_range)); } @@ -394,7 +394,7 @@ void ViewerOutput::SetWaveformEnabled(bool e) { if ((waveform_requests_enabled_ = e)) { if (Node *connected = this->GetConnectedSampleOutput()) { - TimeRange max_range = InputTimeAdjustment(kSamplesInput, -1, TimeRange(0, GetAudioLength())); + TimeRange max_range = InputTimeAdjustment(kSamplesInput, -1, TimeRange(0, GetAudioLength()), false); TimeRangeList invalid = connected->waveform_cache()->GetInvalidatedRanges(max_range); for (const TimeRange &r : invalid) { connected->waveform_cache()->Request(r); diff --git a/app/node/time/timeoffset/timeoffsetnode.cpp b/app/node/time/timeoffset/timeoffsetnode.cpp index 98fc2c6d0..1a6201b5e 100644 --- a/app/node/time/timeoffset/timeoffsetnode.cpp +++ b/app/node/time/timeoffset/timeoffsetnode.cpp @@ -46,12 +46,12 @@ void TimeOffsetNode::Retranslate() SetInputName(kInputInput, QStringLiteral("Input")); } -TimeRange TimeOffsetNode::InputTimeAdjustment(const QString &input, int element, const TimeRange &input_time) const +TimeRange TimeOffsetNode::InputTimeAdjustment(const QString &input, int element, const TimeRange &input_time, bool clamp) const { if (input == kInputInput) { return TimeRange(GetRemappedTime(input_time.in()), GetRemappedTime(input_time.out())); } else { - return super::InputTimeAdjustment(input, element, input_time); + return super::InputTimeAdjustment(input, element, input_time, clamp); } } diff --git a/app/node/time/timeoffset/timeoffsetnode.h b/app/node/time/timeoffset/timeoffsetnode.h index 56f1c0711..f1890924f 100644 --- a/app/node/time/timeoffset/timeoffsetnode.h +++ b/app/node/time/timeoffset/timeoffsetnode.h @@ -52,7 +52,7 @@ public: return tr("Offset time passing through the graph."); } - virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const override; virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; virtual void Retranslate() override; diff --git a/app/node/time/timeremap/timeremap.cpp b/app/node/time/timeremap/timeremap.cpp index 2c1d0d2c5..d3035bb3d 100644 --- a/app/node/time/timeremap/timeremap.cpp +++ b/app/node/time/timeremap/timeremap.cpp @@ -58,12 +58,12 @@ QString TimeRemapNode::Description() const return tr("Arbitrarily remap time through the nodes."); } -TimeRange TimeRemapNode::InputTimeAdjustment(const QString &input, int element, const TimeRange &input_time) const +TimeRange TimeRemapNode::InputTimeAdjustment(const QString &input, int element, const TimeRange &input_time, bool clamp) const { if (input == kInputInput) { return TimeRange(GetRemappedTime(input_time.in()), GetRemappedTime(input_time.out())); } else { - return super::InputTimeAdjustment(input, element, input_time); + return super::InputTimeAdjustment(input, element, input_time, clamp); } } diff --git a/app/node/time/timeremap/timeremap.h b/app/node/time/timeremap/timeremap.h index 8efd60ed0..3ba9cd8e1 100644 --- a/app/node/time/timeremap/timeremap.h +++ b/app/node/time/timeremap/timeremap.h @@ -38,7 +38,7 @@ public: virtual QVector Category() const override; virtual QString Description() const override; - virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; + virtual TimeRange InputTimeAdjustment(const QString& input, int element, const TimeRange& input_time, bool clamp) const override; virtual TimeRange OutputTimeAdjustment(const QString& input, int element, const TimeRange& input_time) const override; virtual void Retranslate() override; diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index e99be2d4f..86e91ea2d 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -189,7 +189,7 @@ NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& inpu // If input is connected, retrieve value directly if (node->IsInputConnectedForRender(input)) { - TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range); + TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range, true); // Value will equal something from the connected node, follow it Node *output = node->GetConnectedRenderOutput(input); @@ -224,7 +224,7 @@ NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& inpu } else { // Not connected or an array, just pull the immediate - TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range); + TimeRange adjusted_range = node->InputTimeAdjustment(input, -1, range, true); return_val = node->GetValueAtTime(input, adjusted_range.in()); @@ -240,7 +240,7 @@ NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& inpu void NodeTraverser::ProcessInputElement(NodeValueTableArray &array_tbl, const Node *node, const QString &input, int element, const TimeRange &range) { NodeValueTable& sub_tbl = array_tbl[element]; - TimeRange adjusted_range = node->InputTimeAdjustment(input, element, range); + TimeRange adjusted_range = node->InputTimeAdjustment(input, element, range, true); if (node->IsInputConnectedForRender(input, element)) { Node *output = node->GetConnectedRenderOutput(input, element);