From b361ba8c51dc79640e77358951b7d4e8f55807f1 Mon Sep 17 00:00:00 2001 From: itsmattkc <34096995+itsmattkc@users.noreply.github.com> Date: Sun, 21 Aug 2022 10:03:07 -0700 Subject: [PATCH] viewer: ignore scrub events when time changes programmatically --- app/widget/viewer/footageviewer.cpp | 2 + app/widget/viewer/viewer.cpp | 70 +++++++++++++++++------------ app/widget/viewer/viewer.h | 9 ++++ 3 files changed, 52 insertions(+), 29 deletions(-) diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 16c9b2d4c..b643f6740 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -60,6 +60,7 @@ void FootageViewerWidget::ConnectNodeEvent(ViewerOutput *n) { super::ConnectNodeEvent(n); + IgnoreNextScrubEvent(); SetTime(cached_timestamps_.value(n, 0)); } @@ -70,6 +71,7 @@ void FootageViewerWidget::DisconnectNodeEvent(ViewerOutput *n) super::DisconnectNodeEvent(n); + IgnoreNextScrubEvent(); SetTime(0); } diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index a11acbce5..a0a63f60f 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -73,7 +73,8 @@ ViewerWidget::ViewerWidget(QWidget *parent) : recording_(false), first_requeue_watcher_(nullptr), enable_audio_scrubbing_(true), - waveform_mode_(kWFAutomatic) + waveform_mode_(kWFAutomatic), + ignore_scrub_(0) { // Set up main layout QVBoxLayout* layout = new QVBoxLayout(this); @@ -257,6 +258,7 @@ void ViewerWidget::DisconnectNodeEvent(ViewerOutput *n) disconnect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateWaveformViewFromMode); CloseAudioProcessor(); + audio_scrub_watchers_.clear(); SetDisplayImage(QVariant()); @@ -487,7 +489,7 @@ void ViewerWidget::DisarmRecording() void ViewerWidget::UpdateAudioProcessor() { if (GetConnectedNode()) { - audio_processor_.Close(); + CloseAudioProcessor(); AudioParams ap = GetConnectedNode()->GetAudioParams(); AudioParams packed(OLIVE_CONFIG("AudioOutputSampleRate").toInt(), @@ -681,30 +683,33 @@ void ViewerWidget::ReceivedAudioBufferForPlayback() void ViewerWidget::ReceivedAudioBufferForScrubbing() { - // NOTE: Might be good to organize a queue for this in the event that audio takes a long time to - // keep the scrubbed chunks ordered, similar to the playback_queue_ or audio_playback_queue_ - RenderTicketWatcher *watcher = static_cast(sender()); - if (watcher->HasResult()) { - SampleBuffer samples = watcher->Get().value(); - if (samples.is_allocated()) { - if (samples.audio_params().channel_count() > 0) { - AudioProcessor::Buffer buf; - int r = audio_processor_.Convert(samples.to_raw_ptrs().data(), samples.sample_count(), &buf); + while (!audio_scrub_watchers_.empty() && audio_scrub_watchers_.front() != watcher) { + audio_scrub_watchers_.pop_front(); + } - if (r >= 0) { - if (!buf.empty()) { - QString error; - const QByteArray &packed = buf.at(0); - AudioManager::instance()->ClearBufferedOutput(); - if (!AudioManager::instance()->PushToOutput(audio_processor_.to(), packed, &error)) { - Core::instance()->ShowStatusBarMessage(tr("Audio scrubbing failed: %1").arg(error)); + if (!audio_scrub_watchers_.empty()) { + if (watcher->HasResult()) { + SampleBuffer samples = watcher->Get().value(); + if (samples.is_allocated()) { + if (samples.audio_params().channel_count() > 0) { + AudioProcessor::Buffer buf; + int r = audio_processor_.Convert(samples.to_raw_ptrs().data(), samples.sample_count(), &buf); + + if (r >= 0) { + if (!buf.empty()) { + QString error; + const QByteArray &packed = buf.at(0); + AudioManager::instance()->ClearBufferedOutput(); + if (!AudioManager::instance()->PushToOutput(audio_processor_.to(), packed, &error)) { + Core::instance()->ShowStatusBarMessage(tr("Audio scrubbing failed: %1").arg(error)); + } + AudioMonitor::PushSampleBufferOnAll(samples); } - AudioMonitor::PushSampleBufferOnAll(samples); + } else { + qCritical() << "Failed to process audio for scrubbing:" << r; } - } else { - qCritical() << "Failed to process audio for scrubbing:" << r; } } } @@ -937,16 +942,23 @@ void ViewerWidget::PauseInternal() void ViewerWidget::PushScrubbedAudio() { if (!IsPlaying() && GetConnectedNode() && OLIVE_CONFIG("AudioScrubbing").toBool() && enable_audio_scrubbing_) { - // Get audio src device from renderer - const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters(); + if (ignore_scrub_ > 0) { + ignore_scrub_--; + } - if (params.is_valid()) { - // NOTE: Hardcoded scrubbing interval (20ms) - rational interval = rational(20, 1000); + if (ignore_scrub_ == 0) { + // Get audio src device from renderer + const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters(); - RenderTicketWatcher *watcher = new RenderTicketWatcher(); - connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForScrubbing); - watcher->SetTicket(auto_cacher_.GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval))); + if (params.is_valid()) { + // NOTE: Hardcoded scrubbing interval (20ms) + rational interval = rational(20, 1000); + + RenderTicketWatcher *watcher = new RenderTicketWatcher(); + connect(watcher, &RenderTicketWatcher::Finished, this, &ViewerWidget::ReceivedAudioBufferForScrubbing); + audio_scrub_watchers_.push_back(watcher); + watcher->SetTicket(auto_cacher_.GetRangeOfAudio(TimeRange(GetTime(), GetTime() + interval))); + } } } } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index bcc1a19ff..a6c2fec22 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -177,6 +177,11 @@ protected: return display_widget_; } + void IgnoreNextScrubEvent() + { + ignore_scrub_++; + } + private: int64_t GetTimestamp() const { @@ -281,6 +286,8 @@ private: static QVector instances_; + std::list audio_scrub_watchers_; + bool record_armed_; bool recording_; TimelineWidget *recording_callback_; @@ -297,6 +304,8 @@ private: QVector dry_run_watchers_; + int ignore_scrub_; + private slots: void PlaybackTimerUpdate();