From cb1127936acc2fe65c41dbe5595036dd0bb388ac Mon Sep 17 00:00:00 2001 From: Thomas Wilshaw Date: Thu, 7 May 2020 22:14:37 +0100 Subject: [PATCH 01/14] Implement Alt Click to default on sliders The following sliders now have Alt Click functionality: - Nodes (What it's initialy set too or 0 if not) - Export Dialog Dimensions~ (Defaults to Sequence width/height) - Export Compression Settings~ - Advanced (0) - Speed/Duration - Speed (100%) - Duration (clip length) - Stream Properties Image sequence - Start Index (1) - End Index (sequence length) Sliders with no default value set ignore an Alt Click. Added SliderBase::SetDefaultValue() which sets the default value of a slider. Added ValueReset() to SliderBase signals which is called if a slider is Alt Clicked on. Only updates if default_value_ is not Null. --- app/dialog/export/codec/h264section.cpp | 1 + app/dialog/export/export.cpp | 2 + .../export/exportadvancedvideodialog.cpp | 1 + .../videostreamproperties.cpp | 2 + app/dialog/speedduration/speedduration.cpp | 2 + .../nodeparamviewwidgetbridge.cpp | 11 +++++ app/widget/slider/sliderbase.cpp | 16 ++++++- app/widget/slider/sliderbase.h | 5 +++ app/widget/slider/sliderlabel.cpp | 45 ++++++++++++------- app/widget/slider/sliderlabel.h | 2 + 10 files changed, 69 insertions(+), 18 deletions(-) diff --git a/app/dialog/export/codec/h264section.cpp b/app/dialog/export/codec/h264section.cpp index 42833f665..e0e50fb55 100644 --- a/app/dialog/export/codec/h264section.cpp +++ b/app/dialog/export/codec/h264section.cpp @@ -121,6 +121,7 @@ H264CRFSection::H264CRFSection(QWidget *parent) : crf_input->SetMinimum(kMinimumCRF); crf_input->SetMaximum(kMaximumCRF); crf_input->SetValue(kDefaultCRF); + crf_input->SetDefaultValue(kDefaultCRF); layout->addWidget(crf_input); connect(crf_slider_, &QSlider::valueChanged, crf_input, &IntegerSlider::SetValue); diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index 23480684b..3071d92c5 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -204,7 +204,9 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : // Set defaults format_combobox_->setCurrentIndex(kFormatMPEG4); video_tab_->width_slider()->SetValue(viewer_node_->video_params().width()); + video_tab_->width_slider()->SetDefaultValue(viewer_node_->video_params().width()); video_tab_->height_slider()->SetValue(viewer_node_->video_params().height()); + video_tab_->height_slider()->SetDefaultValue(viewer_node_->video_params().height()); video_tab_->set_frame_rate(viewer_node_->video_params().time_base().flipped()); audio_tab_->set_sample_rate(viewer_node_->audio_params().sample_rate()); audio_tab_->set_channel_layout(viewer_node_->audio_params().channel_layout()); diff --git a/app/dialog/export/exportadvancedvideodialog.cpp b/app/dialog/export/exportadvancedvideodialog.cpp index bbaba2ee6..1b11d85b9 100644 --- a/app/dialog/export/exportadvancedvideodialog.cpp +++ b/app/dialog/export/exportadvancedvideodialog.cpp @@ -19,6 +19,7 @@ ExportAdvancedVideoDialog::ExportAdvancedVideoDialog(QWidget *parent) : thread_slider_ = new IntegerSlider(); thread_slider_->SetMinimum(0); + thread_slider_->SetDefaultValue(0); layout->addWidget(thread_slider_, row, 1); row++; diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp index 6f565aa33..5f63fdfd3 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp @@ -80,6 +80,7 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) : imgseq_start_time_ = new IntegerSlider(); imgseq_start_time_->SetMinimum(0); imgseq_start_time_->SetValue(video_stream->start_time()); + imgseq_start_time_->SetDefaultValue(video_stream->start_time()); imgseq_layout->addWidget(imgseq_start_time_, imgseq_row, 1); imgseq_row++; @@ -89,6 +90,7 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) : imgseq_end_time_ = new IntegerSlider(); imgseq_end_time_->SetMinimum(0); imgseq_end_time_->SetValue(video_stream->start_time() + video_stream->duration() - 1); + imgseq_end_time_->SetDefaultValue(video_stream->start_time() + video_stream->duration() - 1); imgseq_layout->addWidget(imgseq_end_time_, imgseq_row, 1); video_layout->addWidget(imgseq_group, row, 0, 1, 2); diff --git a/app/dialog/speedduration/speedduration.cpp b/app/dialog/speedduration/speedduration.cpp index 4c6fc4ac9..02aeefa13 100644 --- a/app/dialog/speedduration/speedduration.cpp +++ b/app/dialog/speedduration/speedduration.cpp @@ -93,6 +93,7 @@ SpeedDurationDialog::SpeedDurationDialog(const rational& timebase, const QListSetMinimum(0); speed_slider_->SetDisplayType(FloatSlider::kPercentage); + speed_slider_->SetDefaultValue(1); speed_layout->addWidget(speed_slider_, row, 1); if (same_speed) { @@ -111,6 +112,7 @@ SpeedDurationDialog::SpeedDurationDialog(const rational& timebase, const QListSetTimebase(timebase_); duration_slider_->SetMinimum(1); + duration_slider_->SetDefaultValue(Timecode::time_to_timestamp(clips_.first()->length(), timebase_)); speed_layout->addWidget(duration_slider_, row, 1); if (same_duration) { diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 3f9933218..e791c66fb 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -66,6 +66,7 @@ const QList &NodeParamViewWidgetBridge::widgets() const void NodeParamViewWidgetBridge::CreateWidgets() { + if (input_->IsArray()) { NodeParamViewArrayWidget* w = new NodeParamViewArrayWidget(static_cast(input_)); @@ -91,6 +92,11 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeParam::kInt: { IntegerSlider* slider = new IntegerSlider(); + if (!input_->get_standard_value().isNull()) { + slider->SetDefaultValue(input_->get_standard_value()); + } else { + slider->SetDefaultValue(0); + } widgets_.append(slider); connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); break; @@ -363,6 +369,11 @@ void NodeParamViewWidgetBridge::CreateSliders(int count) { for (int i=0;iget_standard_value().isNull()) { + fs->SetDefaultValue(input_->get_split_standard_value().at(i)); + } else { + fs->SetDefaultValue(0.0f); + } widgets_.append(fs); connect(fs, &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); } diff --git a/app/widget/slider/sliderbase.cpp b/app/widget/slider/sliderbase.cpp index 4e41119bf..2b30810ca 100644 --- a/app/widget/slider/sliderbase.cpp +++ b/app/widget/slider/sliderbase.cpp @@ -23,6 +23,7 @@ #include #include #include +#include OLIVE_NAMESPACE_ENTER @@ -49,6 +50,7 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) : connect(label_, SIGNAL(dragged(int)), this, SLOT(LabelDragged(int))); connect(label_, SIGNAL(drag_stop()), this, SLOT(LabelClicked())); connect(label_, SIGNAL(focused()), this, SLOT(LabelClicked())); + connect(label_, SIGNAL(ResetResult()), this, SLOT(ValueReset())); connect(editor_, SIGNAL(Confirmed()), this, SLOT(LineEditConfirmed())); connect(editor_, SIGNAL(Cancelled()), this, SLOT(LineEditCancelled())); @@ -137,6 +139,11 @@ void SliderBase::SetValue(const QVariant &v) UpdateLabel(value_); } +void SliderBase::SetDefaultValue(const QVariant &v) +{ + default_value_ = ClampValue(v); +} + void SliderBase::SetMinimumInternal(const QVariant &v) { min_value_ = v; @@ -241,7 +248,6 @@ void SliderBase::LabelClicked() emit ValueChanged(value_); } else { // This was a simple click - // Load label's text into editor editor_->setText(ValueToString(value_)); @@ -327,4 +333,12 @@ void SliderBase::LineEditCancelled() label_->blockSignals(false); } +void SliderBase::ValueReset() +{ + if (!default_value_.isNull()) { + SetValue(default_value_); + emit ValueChanged(value_); + } +} + OLIVE_NAMESPACE_EXIT diff --git a/app/widget/slider/sliderbase.h b/app/widget/slider/sliderbase.h index 2a4b8899f..4272f111c 100644 --- a/app/widget/slider/sliderbase.h +++ b/app/widget/slider/sliderbase.h @@ -45,6 +45,8 @@ public: void SetAlignment(Qt::Alignment alignment); + void SetDefaultValue(const QVariant& v); + bool IsTristate() const; void SetTristate(); @@ -89,6 +91,7 @@ private: FocusableLineEdit* editor_; QVariant value_; + QVariant default_value_; bool has_min_; QVariant min_value_; @@ -120,6 +123,8 @@ private slots: void LineEditConfirmed(); void LineEditCancelled(); + + void ValueReset(); }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/slider/sliderlabel.cpp b/app/widget/slider/sliderlabel.cpp index aee4a8409..1fde819d6 100644 --- a/app/widget/slider/sliderlabel.cpp +++ b/app/widget/slider/sliderlabel.cpp @@ -55,49 +55,60 @@ SliderLabel::SliderLabel(QWidget *parent) : void SliderLabel::mousePressEvent(QMouseEvent *) { - emit drag_start(); + if (QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) { + emit ResetResult(); + }else { + emit drag_start(); #if defined(Q_OS_MAC) - CGAssociateMouseAndMouseCursorPosition(false); - CGDisplayHideCursor(kCGDirectMainDisplay); - CGGetLastMouseDelta(nullptr, nullptr); + CGAssociateMouseAndMouseCursorPosition(false); + CGDisplayHideCursor(kCGDirectMainDisplay); + CGGetLastMouseDelta(nullptr, nullptr); #else - drag_start_ = QCursor::pos(); + drag_start_ = QCursor::pos(); - static_cast(QApplication::instance())->setOverrideCursor(Qt::BlankCursor); + static_cast(QApplication::instance())->setOverrideCursor(Qt::BlankCursor); #endif + } } void SliderLabel::mouseMoveEvent(QMouseEvent *) { - int32_t x_mvmt, y_mvmt; + if (QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) { + // do nothing + }else { + int32_t x_mvmt, y_mvmt; - // Keep cursor in the same position + // Keep cursor in the same position #if defined(Q_OS_MAC) - CGGetLastMouseDelta(&x_mvmt, &y_mvmt); + CGGetLastMouseDelta(&x_mvmt, &y_mvmt); #else - QPoint current_pos = QCursor::pos(); + QPoint current_pos = QCursor::pos(); - x_mvmt = current_pos.x() - drag_start_.x(); - y_mvmt = drag_start_.y() - current_pos.y(); + x_mvmt = current_pos.x() - drag_start_.x(); + y_mvmt = drag_start_.y() - current_pos.y(); - QCursor::setPos(drag_start_); + QCursor::setPos(drag_start_); #endif - emit dragged(x_mvmt + y_mvmt); + emit dragged(x_mvmt + y_mvmt); + } } void SliderLabel::mouseReleaseEvent(QMouseEvent *) { + if (QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) { + //do nothing + } else { + // Emit a clicked signal + emit drag_stop(); + } #if defined(Q_OS_MAC) CGAssociateMouseAndMouseCursorPosition(true); CGDisplayShowCursor(kCGDirectMainDisplay); #else static_cast(QApplication::instance())->restoreOverrideCursor(); #endif - - // Emit a clicked signal - emit drag_stop(); } void SliderLabel::focusInEvent(QFocusEvent *event) diff --git a/app/widget/slider/sliderlabel.h b/app/widget/slider/sliderlabel.h index c83e86bfc..cd867692a 100644 --- a/app/widget/slider/sliderlabel.h +++ b/app/widget/slider/sliderlabel.h @@ -51,6 +51,8 @@ signals: void focused(); + void ResetResult(); + private: QPoint drag_start_; From 8c4c6ba2a5d61d808183e6290fed64a7f89d68b4 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 04:40:12 +1000 Subject: [PATCH 02/14] viewer: queue frames during playback Rather than have to decode EXRs and display them in the span of one frame, decode into memory ahead of time so that they're ready for upload. Has the potential to speed up playback by about 5-6x. --- app/widget/viewer/viewer.cpp | 119 ++++++++++++++++++++++++++-- app/widget/viewer/viewer.h | 19 ++++- app/widget/viewer/viewerdisplay.cpp | 60 +------------- app/widget/viewer/viewerdisplay.h | 5 -- 4 files changed, 133 insertions(+), 70 deletions(-) diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 5d8cf5e62..34fd47a6f 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -345,15 +345,31 @@ void ViewerWidget::SetGizmos(Node *node) void ViewerWidget::UpdateTextureFromNode(const rational& time) { - if (!GetConnectedNode() || time >= GetConnectedNode()->Length()) { - main_gl_widget()->SetImage(QString()); - video_renderer_->UpdateLastRequestedTime(time); - } else { - QString frame_fn = video_renderer_->GetCachedFrame(time); + { + QMutexLocker locker(&playback_frame_queue_lock_); + while (!playback_frame_queue_.isEmpty()) { + PlaybackFrame pf = playback_frame_queue_.takeFirst(); + + if (pf.timestamp == time) { + // Frame was in queue, no need to decode anything + main_gl_widget()->SetImageFromLoadBuffer(pf.frame.get()); + QtConcurrent::run(this, &ViewerWidget::FillPlaybackQueue); + return; + } + } + } + + // Frame was not in queue, will require decoding + if (FrameExistsAtTime(time)) { + QString frame_fn = GetCachedFilenameFromTime(time); if (!frame_fn.isEmpty()) { - main_gl_widget()->SetImage(frame_fn); + FramePtr f = DecodeCachedImage(frame_fn); + main_gl_widget()->SetImageFromLoadBuffer(f.get()); } + } else { + main_gl_widget()->SetImageFromLoadBuffer(nullptr); + video_renderer_->UpdateLastRequestedTime(time); } } @@ -372,11 +388,14 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) QString audio_fn = audio_renderer_->CachePathName(); if (!audio_fn.isEmpty()) { AudioManager::instance()->SetOutputParams(audio_renderer_->params()); - AudioManager::instance()->StartOutput(audio_fn, audio_renderer_->params().time_to_bytes(GetTime()), playback_speed_); + AudioManager::instance()->StartOutput(audio_fn, + audio_renderer_->params().time_to_bytes(GetTime()), + playback_speed_); } start_msec_ = QDateTime::currentMSecsSinceEpoch(); start_timestamp_ = ruler()->GetTime(); + playback_frame_queue_next_frame_ = start_timestamp_; controls_->ShowPauseButton(); @@ -385,6 +404,8 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) if (playback_is_audio_only_) { connect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate); } else { + FillPlaybackQueue(); + connect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } } @@ -442,6 +463,86 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp sender->SetColorTransform(transform); } +void ViewerWidget::FillPlaybackQueue() +{ + playback_frame_queue_lock_.lock(); + while (playback_frame_queue_.size() < 8) { + // Load frame from cache + FramePtr frame = nullptr; + + rational rtime = Timecode::timestamp_to_time(playback_frame_queue_next_frame_, + timebase()); + + QString frame_fn = GetCachedFilenameFromTime(rtime); + + if (!frame_fn.isEmpty()) { + frame = DecodeCachedImage(frame_fn); + } + + playback_frame_queue_.append({rtime, frame}); + + if (!playback_speed_) { + break; + } + + playback_frame_queue_next_frame_ += playback_speed_; + } + playback_frame_queue_lock_.unlock(); +} + +QString ViewerWidget::GetCachedFilenameFromTime(const rational &time) +{ + if (FrameExistsAtTime(time)) { + return video_renderer_->GetCachedFrame(time); + } else { + return QString(); + } +} + +bool ViewerWidget::FrameExistsAtTime(const rational &time) +{ + return GetConnectedNode() && time < GetConnectedNode()->Length(); +} + +FramePtr ViewerWidget::DecodeCachedImage(const QString &fn) +{ + FramePtr frame = nullptr; + + if (!fn.isEmpty() && QFileInfo::exists(fn)) { + auto input = OIIO::ImageInput::open(fn.toStdString()); + + if (input) { + + PixelFormat::Format image_format = PixelFormat::OIIOFormatToOliveFormat(input->spec().format, + input->spec().nchannels == kRGBAChannels); + + frame = Frame::Create(); + + frame->set_video_params(VideoRenderingParams(input->spec().width, + input->spec().height, + image_format)); + + frame->allocate(); + + input->read_image(input->spec().format, + frame->data(), + OIIO::AutoStride, + frame->linesize_bytes()); + + input->close(); + +#if OIIO_VERSION < 10903 + OIIO::ImageInput::destroy(input); +#endif + + } else { + qWarning() << "OIIO Error:" << OIIO::geterror().c_str(); + } + } + + return frame; +} + void ViewerWidget::UpdateStack() { if (GetConnectedNode() @@ -710,6 +811,10 @@ void ViewerWidget::Pause() } else { disconnect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } + + playback_frame_queue_lock_.lock(); + playback_frame_queue_.clear(); + playback_frame_queue_lock_.unlock(); } } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 164f77122..26abc3985 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -172,6 +172,14 @@ private: void SetColorTransform(const ColorTransform& transform, ViewerDisplayWidget* sender); + void FillPlaybackQueue(); + + QString GetCachedFilenameFromTime(const rational& time); + + bool FrameExistsAtTime(const rational& time); + + FramePtr DecodeCachedImage(const QString& fn); + QStackedWidget* stack_; ViewerSizer* sizer_; @@ -179,7 +187,7 @@ private: qint64 start_msec_; int64_t start_timestamp_; - int playback_speed_; + QAtomicInt playback_speed_; qint64 frame_cache_job_time_; @@ -205,6 +213,15 @@ private: ViewerDisplayWidget* context_menu_widget_; + struct PlaybackFrame { + rational timestamp; + FramePtr frame; + }; + + QMutex playback_frame_queue_lock_; + QLinkedList playback_frame_queue_; + int64_t playback_frame_queue_next_frame_; + private slots: void PlaybackTimerUpdate(); diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 3d86eb5d6..68584b836 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -30,6 +30,7 @@ #include #include "common/define.h" +#include "common/functiontimer.h" #include "gizmotraverser.h" #include "render/backend/opengl/openglrenderfunctions.h" #include "render/backend/opengl/openglshader.h" @@ -61,63 +62,6 @@ void ViewerDisplayWidget::SetMatrix(const QMatrix4x4 &mat) update(); } -void ViewerDisplayWidget::SetImage(const QString &fn) -{ - has_image_ = false; - - if (!fn.isEmpty() && QFileInfo::exists(fn)) { - auto input = OIIO::ImageInput::open(fn.toStdString()); - - if (input) { - - PixelFormat::Format image_format = PixelFormat::OIIOFormatToOliveFormat(input->spec().format, - input->spec().nchannels == kRGBAChannels); - - // Ensure the following texture operations are done in our context (in case we're in a separate window for instance) - makeCurrent(); - - if (!texture_.IsCreated() - || texture_.width() != input->spec().width - || texture_.height() != input->spec().height - || texture_.format() != image_format) { - load_buffer_.destroy(); - texture_.Destroy(); - - load_buffer_.set_video_params(VideoRenderingParams(input->spec().width, input->spec().height, image_format)); - load_buffer_.allocate(); - - texture_.Create(context(), VideoRenderingParams(input->spec().width, input->spec().height, image_format)); - } - - input->read_image(input->spec().format, load_buffer_.data(), OIIO::AutoStride, load_buffer_.linesize_bytes()); - input->close(); - - texture_.Upload(&load_buffer_); - - doneCurrent(); - - emit LoadedBuffer(&load_buffer_); - - has_image_ = true; - -#if OIIO_VERSION < 10903 - OIIO::ImageInput::destroy(input); -#endif - - } else { - qWarning() << "OIIO Error:" << OIIO::geterror().c_str(); - } - } - - update(); - - if (has_image_) { - emit LoadedBuffer(&load_buffer_); - } else { - emit LoadedBuffer(nullptr); - } -} - void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e) { signal_cursor_color_ = e; @@ -144,6 +88,8 @@ void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer) } update(); + + emit LoadedBuffer(in_buffer); } void ViewerDisplayWidget::ConnectSibling(ViewerDisplayWidget *sibling) diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 996758718..307c350a6 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -66,11 +66,6 @@ public: virtual ~ViewerDisplayWidget() override; - /** - * @brief Set an image to load and display on screen - */ - void SetImage(const QString& fn); - const QMatrix4x4& GetMatrix(); void ConnectSibling(ViewerDisplayWidget* sibling); From 00248de49c472e3933a0920796a3af2492b5501f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 16:02:18 +1000 Subject: [PATCH 03/14] viewer: improved frame queue and linked window Improves minor issues with frame queue implemented in earlier commit. Also links ViewerWindow with the queue and a separate timer to make it much smoother. --- app/panel/viewer/viewerbase.cpp | 14 ++ app/panel/viewer/viewerbase.h | 4 + app/widget/viewer/CMakeLists.txt | 3 + app/widget/viewer/footageviewer.cpp | 2 +- app/widget/viewer/viewer.cpp | 176 +++++++++++++--------- app/widget/viewer/viewer.h | 26 ++-- app/widget/viewer/viewerdisplay.cpp | 35 ++--- app/widget/viewer/viewerdisplay.h | 24 +-- app/widget/viewer/viewerplaybacktimer.cpp | 44 ++++++ app/widget/viewer/viewerplaybacktimer.h | 48 ++++++ app/widget/viewer/viewerqueue.h | 50 ++++++ app/widget/viewer/viewerwindow.cpp | 51 ++++++- app/widget/viewer/viewerwindow.h | 23 ++- 13 files changed, 370 insertions(+), 130 deletions(-) create mode 100644 app/widget/viewer/viewerplaybacktimer.cpp create mode 100644 app/widget/viewer/viewerplaybacktimer.h create mode 100644 app/widget/viewer/viewerqueue.h diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index 9f9e4c84e..73fd1cade 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -111,4 +111,18 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type) vw->ForceUpdate(); } +void ViewerPanelBase::showEvent(QShowEvent *e) +{ + static_cast(GetTimeBasedWidget())->Pause(); + + TimeBasedPanel::showEvent(e); +} + +void ViewerPanelBase::closeEvent(QCloseEvent *e) +{ + static_cast(GetTimeBasedWidget())->Pause(); + + TimeBasedPanel::closeEvent(e); +} + OLIVE_NAMESPACE_EXIT diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h index ed4c5651a..d620a94f1 100644 --- a/app/panel/viewer/viewerbase.h +++ b/app/panel/viewer/viewerbase.h @@ -62,6 +62,10 @@ public slots: protected: void CreateScopePanel(ScopePanel::Type type); + virtual void showEvent(QShowEvent* e) override; + + virtual void closeEvent(QCloseEvent* e) override; + }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/CMakeLists.txt b/app/widget/viewer/CMakeLists.txt index 70df051ae..f745ca0d6 100644 --- a/app/widget/viewer/CMakeLists.txt +++ b/app/widget/viewer/CMakeLists.txt @@ -26,6 +26,9 @@ set(OLIVE_SOURCES widget/viewer/viewer.cpp widget/viewer/viewerdisplay.h widget/viewer/viewerdisplay.cpp + widget/viewer/viewerplaybacktimer.h + widget/viewer/viewerplaybacktimer.cpp + widget/viewer/viewerqueue.h widget/viewer/viewersafemargininfo.h widget/viewer/viewersizer.h widget/viewer/viewersizer.cpp diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 3ee2569b0..b11acc76b 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -35,7 +35,7 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) : audio_node_ = new AudioInput(); viewer_node_ = new ViewerOutput(); - connect(main_gl_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag); + connect(display_widget(), &ViewerDisplayWidget::DragStarted, this, &FootageViewerWidget::StartFootageDrag); controls_->SetAudioVideoDragButtonsVisible(true); connect(controls_, &PlaybackControls::VideoPressed, this, &FootageViewerWidget::StartVideoDrag); diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 34fd47a6f..4b9461cde 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -63,15 +63,13 @@ ViewerWidget::ViewerWidget(QWidget *parent) : sizer_ = new ViewerSizer(); stack_->addWidget(sizer_); - ViewerDisplayWidget* main_widget = new ViewerDisplayWidget(); - connect(main_widget, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); - connect(main_widget, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor); - connect(main_widget, &ViewerDisplayWidget::LoadedBuffer, this, &ViewerWidget::LoadedBuffer); - connect(main_widget, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged); - connect(main_widget, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged); - connect(sizer_, &ViewerSizer::RequestMatrix, main_widget, &ViewerDisplayWidget::SetMatrix); - sizer_->SetWidget(main_widget); - gl_widgets_.append(main_widget); + display_widget_ = new ViewerDisplayWidget(); + connect(display_widget_, &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); + connect(display_widget_, &ViewerDisplayWidget::CursorColor, this, &ViewerWidget::CursorColor); + connect(display_widget_, &ViewerDisplayWidget::ColorProcessorChanged, this, &ViewerWidget::ColorProcessorChanged); + connect(display_widget_, &ViewerDisplayWidget::ColorManagerChanged, this, &ViewerWidget::ColorManagerChanged); + connect(sizer_, &ViewerSizer::RequestMatrix, display_widget_, &ViewerDisplayWidget::SetMatrix); + sizer_->SetWidget(display_widget_); // Create waveform view when audio is connected and video isn't waveform_view_ = new AudioWaveformView(); @@ -117,6 +115,15 @@ ViewerWidget::ViewerWidget(QWidget *parent) : SetAutoMaxScrollBar(true); } +ViewerWidget::~ViewerWidget() +{ + QList windows = windows_; + + foreach (ViewerWindow* window, windows) { + delete window; + } +} + void ViewerWidget::TimeChangedEvent(const int64_t &i) { if (!time_changed_from_timer_) { @@ -133,7 +140,7 @@ void ViewerWidget::TimeChangedEvent(const int64_t &i) PushScrubbedAudio(); - main_gl_widget()->SetTime(time_set); + display_widget_->SetTime(time_set); } last_time_ = i; @@ -170,8 +177,9 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n) using_manager = nullptr; } - foreach (ViewerDisplayWidget* glw, gl_widgets_) { - glw->ConnectColorManager(using_manager); + display_widget_->ConnectColorManager(using_manager); + foreach (ViewerWindow* window, windows_) { + window->display_widget()->ConnectColorManager(using_manager); } divider_ = CalculateDivider(); @@ -202,8 +210,9 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) // Effectively disables the viewer and clears the state SizeChangedSlot(0, 0); - foreach (ViewerDisplayWidget* glw, gl_widgets_) { - glw->DisconnectColorManager(); + display_widget_->DisconnectColorManager(); + foreach (ViewerWindow* window, windows_) { + window->display_widget()->DisconnectColorManager(); } waveform_view_->ConnectTimelinePoints(nullptr); @@ -236,14 +245,9 @@ void ViewerWidget::resizeEvent(QResizeEvent *event) UpdateMinimumScale(); } -const QList &ViewerWidget::gl_widgets() const +ViewerDisplayWidget *ViewerWidget::display_widget() const { - return gl_widgets_; -} - -ViewerDisplayWidget *ViewerWidget::main_gl_widget() const -{ - return gl_widgets_.first(); + return display_widget_; } void ViewerWidget::TogglePlayPause() @@ -282,8 +286,9 @@ void ViewerWidget::SetOverrideSize(int width, int height) void ViewerWidget::SetMatrix(const QMatrix4x4 &mat) { - foreach (ViewerDisplayWidget* glw, gl_widgets_) { - glw->SetMatrix(mat); + display_widget_->SetMatrix(mat); + foreach (ViewerWindow* vw, windows_) { + vw->display_widget()->SetMatrix(mat); } } @@ -306,19 +311,19 @@ void ViewerWidget::SetFullScreen(QScreen *screen) ViewerWindow* vw = new ViewerWindow(this); - vw->showFullScreen(); vw->setGeometry(screen->geometry()); - vw->gl_widget()->ConnectColorManager(main_gl_widget()->color_manager()); - main_gl_widget()->ConnectSibling(vw->gl_widget()); + vw->showFullScreen(); + vw->display_widget()->ConnectColorManager(color_manager()); connect(vw, &ViewerWindow::destroyed, this, &ViewerWidget::WindowAboutToClose); - connect(vw->gl_widget(), &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); + connect(vw->display_widget(), &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); if (GetConnectedNode()) { vw->SetResolution(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height()); } + vw->display_widget()->SetImage(display_widget_->last_loaded_buffer()); + windows_.append(vw); - gl_widgets_.append(vw->gl_widget()); } void ViewerWidget::ForceUpdate() @@ -334,27 +339,29 @@ VideoRenderBackend *ViewerWidget::video_renderer() const ColorManager *ViewerWidget::color_manager() const { - return main_gl_widget()->color_manager(); + return display_widget_->color_manager(); } void ViewerWidget::SetGizmos(Node *node) { - main_gl_widget()->SetTimeTarget(GetConnectedNode()); - main_gl_widget()->SetGizmos(node); + display_widget_->SetTimeTarget(GetConnectedNode()); + display_widget_->SetGizmos(node); } void ViewerWidget::UpdateTextureFromNode(const rational& time) { { - QMutexLocker locker(&playback_frame_queue_lock_); - while (!playback_frame_queue_.isEmpty()) { - PlaybackFrame pf = playback_frame_queue_.takeFirst(); + QMutexLocker locker(playback_queue_.lock()); + while (!playback_queue_.isEmpty()) { + const ViewerPlaybackFrame& pf = playback_queue_.first(); if (pf.timestamp == time) { // Frame was in queue, no need to decode anything - main_gl_widget()->SetImageFromLoadBuffer(pf.frame.get()); + SetDisplayImage(pf.frame, true); QtConcurrent::run(this, &ViewerWidget::FillPlaybackQueue); return; + } else { + playback_queue_.removeFirst(); } } } @@ -365,10 +372,10 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time) if (!frame_fn.isEmpty()) { FramePtr f = DecodeCachedImage(frame_fn); - main_gl_widget()->SetImageFromLoadBuffer(f.get()); + SetDisplayImage(f, false); } } else { - main_gl_widget()->SetImageFromLoadBuffer(nullptr); + SetDisplayImage(nullptr, false); video_renderer_->UpdateLastRequestedTime(time); } } @@ -393,9 +400,12 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) playback_speed_); } - start_msec_ = QDateTime::currentMSecsSinceEpoch(); - start_timestamp_ = ruler()->GetTime(); - playback_frame_queue_next_frame_ = start_timestamp_; + playback_queue_next_frame_ = ruler()->GetTime(); + playback_timer_.Start(playback_queue_next_frame_, playback_speed_, timebase_dbl()); + + foreach (ViewerWindow* window, windows_) { + window->Play(playback_queue_next_frame_, playback_speed_, timebase()); + } controls_->ShowPauseButton(); @@ -404,10 +414,10 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) if (playback_is_audio_only_) { connect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate); } else { - FillPlaybackQueue(); - - connect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); + connect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } + + FillPlaybackQueue(); } void ViewerWidget::PushScrubbedAudio() @@ -436,7 +446,7 @@ int ViewerWidget::CalculateDivider() { if (GetConnectedNode() && Config::Current()["AutoSelectDivider"].toBool()) { int long_side_of_video = qMax(GetConnectedNode()->video_params().width(), GetConnectedNode()->video_params().height()); - int long_side_of_widget = qMax(main_gl_widget()->width(), main_gl_widget()->height()); + int long_side_of_widget = qMax(display_widget_->width(), display_widget_->height()); return qMax(1, long_side_of_video / long_side_of_widget); } @@ -465,12 +475,16 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp void ViewerWidget::FillPlaybackQueue() { - playback_frame_queue_lock_.lock(); - while (playback_frame_queue_.size() < 8) { + playback_queue_.lock()->lock(); + foreach (ViewerWindow* window, windows_) { + window->queue()->lock()->lock(); + } + + while (playback_queue_.size() < 8) { // Load frame from cache FramePtr frame = nullptr; - rational rtime = Timecode::timestamp_to_time(playback_frame_queue_next_frame_, + rational rtime = Timecode::timestamp_to_time(playback_queue_next_frame_, timebase()); QString frame_fn = GetCachedFilenameFromTime(rtime); @@ -479,15 +493,25 @@ void ViewerWidget::FillPlaybackQueue() frame = DecodeCachedImage(frame_fn); } - playback_frame_queue_.append({rtime, frame}); + ViewerPlaybackFrame f = {rtime, frame}; + + playback_queue_.append(f); + + foreach (ViewerWindow* window, windows_) { + window->queue()->append(f); + } if (!playback_speed_) { break; } - playback_frame_queue_next_frame_ += playback_speed_; + playback_queue_next_frame_ += playback_speed_; } - playback_frame_queue_lock_.unlock(); + + foreach (ViewerWindow* window, windows_) { + window->queue()->lock()->unlock(); + } + playback_queue_.lock()->unlock(); } QString ViewerWidget::GetCachedFilenameFromTime(const rational &time) @@ -543,6 +567,19 @@ FramePtr ViewerWidget::DecodeCachedImage(const QString &fn) return frame; } +void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only) +{ + display_widget_->SetImage(frame); + + if (!main_only) { + foreach (ViewerWindow* vw, windows_) { + vw->display_widget()->SetImage(frame); + } + } + + emit LoadedBuffer(frame.get()); +} + void ViewerWidget::UpdateStack() { if (GetConnectedNode() @@ -616,10 +653,7 @@ void ViewerWidget::ContextMenuSetCustomSafeMargins() void ViewerWidget::WindowAboutToClose() { - ViewerWindow* vw = static_cast(sender()); - - windows_.removeAll(vw); - gl_widgets_.removeAll(vw->gl_widget()); + windows_.removeOne(static_cast(sender())); } void ViewerWidget::ContextMenuScopeTriggered(QAction *action) @@ -629,9 +663,7 @@ void ViewerWidget::ContextMenuScopeTriggered(QAction *action) void ViewerWidget::RendererGeneratedFrame(FramePtr f) { - foreach (ViewerDisplayWidget* glw, gl_widgets_) { - glw->SetImageFromLoadBuffer(f.get()); - } + SetDisplayImage(f, false); } void ViewerWidget::UpdateRendererParameters() @@ -652,7 +684,7 @@ void ViewerWidget::UpdateRendererParameters() video_renderer_->InvalidateCache(TimeRange(0, GetConnectedNode()->Length()), nullptr); } - main_gl_widget()->SetVideoParams(vparam); + display_widget_->SetVideoParams(vparam); AudioRenderingParams aparam(GetConnectedNode()->audio_params(), SampleFormat::kInternalFormat); @@ -809,12 +841,18 @@ void ViewerWidget::Pause() if (playback_is_audio_only_) { disconnect(AudioManager::instance(), &AudioManager::OutputNotified, this, &ViewerWidget::PlaybackTimerUpdate); } else { - disconnect(main_gl_widget(), &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); + disconnect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } - playback_frame_queue_lock_.lock(); - playback_frame_queue_.clear(); - playback_frame_queue_lock_.unlock(); + foreach (ViewerWindow* window, windows_) { + window->Pause(); + } + + { + playback_queue_.lock()->lock(); + playback_queue_.clear(); + playback_queue_.lock()->unlock(); + } } } @@ -859,13 +897,15 @@ void ViewerWidget::ShuttleRight() void ViewerWidget::SetColorTransform(const ColorTransform &transform) { - SetColorTransform(transform, main_gl_widget()); + SetColorTransform(transform, display_widget_); } void ViewerWidget::SetSignalCursorColorEnabled(bool e) { - foreach (ViewerDisplayWidget* glw, gl_widgets_) { - glw->SetSignalCursorColorEnabled(e); + display_widget_->SetSignalCursorColorEnabled(e); + + foreach (ViewerWindow* vw, windows_) { + vw->display_widget()->SetSignalCursorColorEnabled(e); } } @@ -881,11 +921,7 @@ void ViewerWidget::TimebaseChangedEvent(const rational &timebase) void ViewerWidget::PlaybackTimerUpdate() { - int64_t real_time = QDateTime::currentMSecsSinceEpoch() - start_msec_; - - int64_t frames_since_start = qRound(static_cast(real_time) / (timebase_dbl() * 1000)); - - int64_t current_time = start_timestamp_ + frames_since_start * playback_speed_; + int64_t current_time = playback_timer_.GetTimestampNow(); int64_t min_time, max_time; diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 26abc3985..4fcd386bf 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -36,6 +36,8 @@ #include "render/backend/opengl/opengltexture.h" #include "render/backend/audio/audiobackend.h" #include "viewerdisplay.h" +#include "viewerplaybacktimer.h" +#include "viewerqueue.h" #include "viewersizer.h" #include "viewerwindow.h" #include "widget/playbackcontrols/playbackcontrols.h" @@ -52,6 +54,8 @@ class ViewerWidget : public TimeBasedWidget public: ViewerWidget(QWidget* parent = nullptr); + virtual ~ViewerWidget() override; + void SetPlaybackControlsEnabled(bool enabled); void SetTimeRulerEnabled(bool enabled); @@ -116,7 +120,7 @@ signals: void CursorColor(const Color& reference, const Color& display); /** - * @brief Wrapper for ViewerGLWidget::LoadedBuffer() + * @brief Signal emitted when a new frame is loaded */ void LoadedBuffer(Frame* load_buffer); @@ -154,8 +158,7 @@ protected: PlaybackControls* controls_; - const QList &gl_widgets() const; - ViewerDisplayWidget* main_gl_widget() const; + ViewerDisplayWidget* display_widget() const; private: void UpdateTimeInternal(int64_t i); @@ -180,13 +183,12 @@ private: FramePtr DecodeCachedImage(const QString& fn); + void SetDisplayImage(FramePtr frame, bool main_only); + QStackedWidget* stack_; ViewerSizer* sizer_; - qint64 start_msec_; - int64_t start_timestamp_; - QAtomicInt playback_speed_; qint64 frame_cache_job_time_; @@ -209,18 +211,14 @@ private: QList windows_; - QList gl_widgets_; + ViewerDisplayWidget* display_widget_; ViewerDisplayWidget* context_menu_widget_; - struct PlaybackFrame { - rational timestamp; - FramePtr frame; - }; + ViewerPlaybackTimer playback_timer_; - QMutex playback_frame_queue_lock_; - QLinkedList playback_frame_queue_; - int64_t playback_frame_queue_next_frame_; + ViewerQueue playback_queue_; + int64_t playback_queue_next_frame_; private slots: void PlaybackTimerUpdate(); diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 68584b836..6fcc8e0e2 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -44,10 +44,10 @@ bool ViewerDisplayWidget::nouveau_check_done_ = false; ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : ManagedDisplayWidget(parent), - has_image_(false), signal_cursor_color_(false), gizmos_(nullptr), - gizmo_click_(false) + gizmo_click_(false), + last_loaded_buffer_(nullptr) { } @@ -68,18 +68,18 @@ void ViewerDisplayWidget::SetSignalCursorColorEnabled(bool e) setMouseTracking(e); } -void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer) +void ViewerDisplayWidget::SetImage(FramePtr in_buffer) { - has_image_ = in_buffer; + last_loaded_buffer_ = in_buffer; - if (has_image_) { + if (last_loaded_buffer_) { makeCurrent(); if (!texture_.IsCreated() || texture_.width() != in_buffer->width() || texture_.height() != in_buffer->height() || texture_.format() != in_buffer->format()) { - texture_.Create(context(), in_buffer->video_params(), in_buffer->data(), load_buffer_.linesize_pixels()); + texture_.Create(context(), in_buffer->video_params(), in_buffer->data(), in_buffer->linesize_pixels()); } else { texture_.Upload(in_buffer); } @@ -88,14 +88,6 @@ void ViewerDisplayWidget::SetImageFromLoadBuffer(Frame *in_buffer) } update(); - - emit LoadedBuffer(in_buffer); -} - -void ViewerDisplayWidget::ConnectSibling(ViewerDisplayWidget *sibling) -{ - connect(this, &ViewerDisplayWidget::LoadedBuffer, sibling, &ViewerDisplayWidget::SetImageFromLoadBuffer, Qt::QueuedConnection); - sibling->SetImageFromLoadBuffer(&load_buffer_); } const ViewerSafeMarginInfo &ViewerDisplayWidget::GetSafeMargin() const @@ -135,6 +127,11 @@ void ViewerDisplayWidget::SetTime(const rational &time) } } +FramePtr ViewerDisplayWidget::last_loaded_buffer() const +{ + return last_loaded_buffer_; +} + void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) { if (gizmos_ @@ -161,17 +158,17 @@ void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event) if (signal_cursor_color_) { Color reference, display; - if (has_image_) { + if (last_loaded_buffer_) { QVector3D pixel_pos(static_cast(event->x()) / static_cast(width()) * 2.0f - 1.0f, static_cast(event->y()) / static_cast(height()) * 2.0f - 1.0f, 0); pixel_pos = pixel_pos * matrix_.inverted(); - int frame_x = qRound((pixel_pos.x() + 1.0f) * 0.5f * load_buffer_.width()); - int frame_y = qRound((pixel_pos.y() + 1.0f) * 0.5f * load_buffer_.height()); + int frame_x = qRound((pixel_pos.x() + 1.0f) * 0.5f * last_loaded_buffer_->width()); + int frame_y = qRound((pixel_pos.y() + 1.0f) * 0.5f * last_loaded_buffer_->height()); - reference = load_buffer_.get_pixel(frame_x, frame_y); + reference = last_loaded_buffer_->get_pixel(frame_x, frame_y); display = color_service()->ConvertColor(reference); } @@ -223,7 +220,7 @@ void ViewerDisplayWidget::paintGL() f->glClear(GL_COLOR_BUFFER_BIT); // We only draw if we have a pipeline - if (has_image_ && color_service()) { + if (last_loaded_buffer_ && color_service()) { // Bind retrieved texture f->glBindTexture(GL_TEXTURE_2D, texture_.texture()); diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index 307c350a6..0f0d2b81c 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -68,8 +68,6 @@ public: const QMatrix4x4& GetMatrix(); - void ConnectSibling(ViewerDisplayWidget* sibling); - const ViewerSafeMarginInfo& GetSafeMargin() const; void SetSafeMargins(const ViewerSafeMarginInfo& safe_margin); @@ -77,6 +75,8 @@ public: void SetVideoParams(const VideoRenderingParams& params); void SetTime(const rational& time); + FramePtr last_loaded_buffer() const; + public slots: /** * @brief Set the transformation matrix to draw with @@ -100,7 +100,7 @@ public slots: * If there are multiple ViewerGLWidgets showing the same thing, this is faster than decoding the image from file * each time. */ - void SetImageFromLoadBuffer(Frame* in_buffer); + void SetImage(FramePtr in_buffer); signals: /** @@ -113,15 +113,6 @@ signals: */ void CursorColor(const Color& reference, const Color& display); - /** - * @brief Signal emitted when a buffer is loaded from file into memory - * - * This buffer will be the direct output of the renderer in reference space in CPU memory. - * - * Connect this to the SetImageFromLoadBuffer() slot of another ViewerGLWidget to show the same thing - */ - void LoadedBuffer(Frame* load_buffer); - protected: /** * @brief Override the mouse press event for the DragStarted() signal and gizmos @@ -169,17 +160,10 @@ private: */ QMatrix4x4 matrix_; - /** - * @brief Buffer to load images into RAM before sending them to the display - */ - Frame load_buffer_; - #ifdef Q_OS_LINUX static bool nouveau_check_done_; #endif - bool has_image_; - bool signal_cursor_color_; ViewerSafeMarginInfo safe_margin_; @@ -192,6 +176,8 @@ private: rational time_; + FramePtr last_loaded_buffer_; + private slots: /** * @brief Slot to connect just before the OpenGL context is destroyed to clean up resources diff --git a/app/widget/viewer/viewerplaybacktimer.cpp b/app/widget/viewer/viewerplaybacktimer.cpp new file mode 100644 index 000000000..e07e925c9 --- /dev/null +++ b/app/widget/viewer/viewerplaybacktimer.cpp @@ -0,0 +1,44 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "viewerplaybacktimer.h" + +#include + +OLIVE_NAMESPACE_ENTER + +void ViewerPlaybackTimer::Start(const int64_t &start_timestamp, const int &playback_speed, const double &timebase) +{ + start_msec_ = QDateTime::currentMSecsSinceEpoch(); + start_timestamp_ = start_timestamp; + playback_speed_ = playback_speed; + timebase_ = timebase; +} + +int64_t ViewerPlaybackTimer::GetTimestampNow() const +{ + int64_t real_time = QDateTime::currentMSecsSinceEpoch() - start_msec_; + + int64_t frames_since_start = qRound(static_cast(real_time) / (timebase_ * 1000)); + + return start_timestamp_ + frames_since_start * playback_speed_; +} + +OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/viewerplaybacktimer.h b/app/widget/viewer/viewerplaybacktimer.h new file mode 100644 index 000000000..a4669a571 --- /dev/null +++ b/app/widget/viewer/viewerplaybacktimer.h @@ -0,0 +1,48 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef VIEWERPLAYBACKTIMER_H +#define VIEWERPLAYBACKTIMER_H + +#include + +#include "common/define.h" + +OLIVE_NAMESPACE_ENTER + +class ViewerPlaybackTimer { +public: + void Start(const int64_t& start_timestamp, const int& playback_speed, const double& timebase); + + int64_t GetTimestampNow() const; + +private: + qint64 start_msec_; + int64_t start_timestamp_; + + int playback_speed_; + + double timebase_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // VIEWERPLAYBACKTIMER_H diff --git a/app/widget/viewer/viewerqueue.h b/app/widget/viewer/viewerqueue.h new file mode 100644 index 000000000..a08816051 --- /dev/null +++ b/app/widget/viewer/viewerqueue.h @@ -0,0 +1,50 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef VIEWERQUEUE_H +#define VIEWERQUEUE_H + +#include + +#include "codec/frame.h" + +OLIVE_NAMESPACE_ENTER + +struct ViewerPlaybackFrame { + rational timestamp; + FramePtr frame; +}; + +class ViewerQueue : public QLinkedList { +public: + ViewerQueue() = default; + + QMutex* lock() { + return &queue_lock_; + } + +private: + QMutex queue_lock_; + +}; + +OLIVE_NAMESPACE_EXIT + +#endif // VIEWERQUEUE_H diff --git a/app/widget/viewer/viewerwindow.cpp b/app/widget/viewer/viewerwindow.cpp index 3f92f997d..04ae55afb 100644 --- a/app/widget/viewer/viewerwindow.cpp +++ b/app/widget/viewer/viewerwindow.cpp @@ -23,6 +23,8 @@ #include #include +#include "common/timecodefunctions.h" + OLIVE_NAMESPACE_ENTER ViewerWindow::ViewerWindow(QWidget *parent) : @@ -32,13 +34,13 @@ ViewerWindow::ViewerWindow(QWidget *parent) : layout->setMargin(0); layout->setSpacing(0); - gl_widget_ = new ViewerDisplayWidget(); - layout->addWidget(gl_widget_); + display_widget_ = new ViewerDisplayWidget(); + layout->addWidget(display_widget_); } -ViewerDisplayWidget *ViewerWindow::gl_widget() const +ViewerDisplayWidget *ViewerWindow::display_widget() const { - return gl_widget_; + return display_widget_; } void ViewerWindow::SetResolution(int width, int height) @@ -57,7 +59,26 @@ void ViewerWindow::SetResolution(int width, int height) mat.scale(1.0f, window_ar / image_ar, 1.0f); } - gl_widget_->SetMatrix(mat); + display_widget_->SetMatrix(mat); +} + +void ViewerWindow::Play(const int64_t& start_timestamp, const int& playback_speed, const rational &timebase) +{ + timer_.Start(start_timestamp, playback_speed, timebase.toDouble()); + + playback_timebase_ = timebase; + + connect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWindow::UpdateFromQueue); + + display_widget_->update(); +} + +void ViewerWindow::Pause() +{ + disconnect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWindow::UpdateFromQueue); + + QMutexLocker locker(queue_.lock()); + queue_.clear(); } void ViewerWindow::keyPressEvent(QKeyEvent *e) @@ -76,4 +97,24 @@ void ViewerWindow::closeEvent(QCloseEvent *e) deleteLater(); } +void ViewerWindow::UpdateFromQueue() +{ + int64_t t = timer_.GetTimestampNow(); + + rational time = Timecode::timestamp_to_time(t, playback_timebase_); + + QMutexLocker locker(queue_.lock()); + while (!queue_.isEmpty()) { + const ViewerPlaybackFrame& pf = queue_.first(); + + if (pf.timestamp == time) { + // Frame was in queue, no need to decode anything + display_widget_->SetImage(pf.frame); + return; + } else { + queue_.removeFirst(); + } + } +} + OLIVE_NAMESPACE_EXIT diff --git a/app/widget/viewer/viewerwindow.h b/app/widget/viewer/viewerwindow.h index 8baa30c9c..2f7742c9f 100644 --- a/app/widget/viewer/viewerwindow.h +++ b/app/widget/viewer/viewerwindow.h @@ -24,6 +24,8 @@ #include #include "viewerdisplay.h" +#include "viewerplaybacktimer.h" +#include "viewerqueue.h" OLIVE_NAMESPACE_ENTER @@ -32,20 +34,37 @@ class ViewerWindow : public QWidget public: ViewerWindow(QWidget* parent = nullptr); - ViewerDisplayWidget* gl_widget() const; + ViewerDisplayWidget* display_widget() const; /** * @brief Used to adjust resulting picture to be the right aspect ratio */ void SetResolution(int width, int height); + ViewerQueue* queue() { + return &queue_; + } + + void Play(const int64_t &start_timestamp, const int &playback_speed, const rational &timebase); + + void Pause(); + protected: virtual void keyPressEvent(QKeyEvent* e) override; virtual void closeEvent(QCloseEvent* e) override; +private slots: + void UpdateFromQueue(); + private: - ViewerDisplayWidget* gl_widget_; + ViewerDisplayWidget* display_widget_; + + ViewerQueue queue_; + + ViewerPlaybackTimer timer_; + + rational playback_timebase_; }; From 869f41fc50415783a812364d2601e00bb71e5fde Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 16:06:05 +1000 Subject: [PATCH 04/14] ffmpegdecoder: fixed audio bug that failed to conform if the codec didn't supply a channel layout --- app/codec/ffmpeg/ffmpegdecoder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index abea45088..5b2c45a90 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -835,7 +835,7 @@ bool FFmpegDecoder::ConformAudio(const QAtomicInt *cancelled, const AudioRenderi p.channel_layout(), FFmpegCommon::GetFFmpegSampleFormat(p.format()), p.sample_rate(), - static_cast(index_instance.stream()->codecpar->channel_layout), + channel_layout, static_cast(index_instance.stream()->codecpar->format), index_instance.stream()->codecpar->sample_rate, 0, From 0340373faa588e067a3a00f0ea0e73e2091f8ef1 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 16:20:31 +1000 Subject: [PATCH 05/14] viewerpanel: only listen to closeEvent to pause Using showEvent as well would pause if Maximize Panel was used which is undesirable. --- app/panel/viewer/viewerbase.cpp | 7 ------- app/panel/viewer/viewerbase.h | 2 -- 2 files changed, 9 deletions(-) diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp index 73fd1cade..fd1a066c5 100644 --- a/app/panel/viewer/viewerbase.cpp +++ b/app/panel/viewer/viewerbase.cpp @@ -111,13 +111,6 @@ void ViewerPanelBase::CreateScopePanel(ScopePanel::Type type) vw->ForceUpdate(); } -void ViewerPanelBase::showEvent(QShowEvent *e) -{ - static_cast(GetTimeBasedWidget())->Pause(); - - TimeBasedPanel::showEvent(e); -} - void ViewerPanelBase::closeEvent(QCloseEvent *e) { static_cast(GetTimeBasedWidget())->Pause(); diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h index d620a94f1..99340a983 100644 --- a/app/panel/viewer/viewerbase.h +++ b/app/panel/viewer/viewerbase.h @@ -62,8 +62,6 @@ public slots: protected: void CreateScopePanel(ScopePanel::Type type); - virtual void showEvent(QShowEvent* e) override; - virtual void closeEvent(QCloseEvent* e) override; }; From 20ef7b1c211f59f30a94c7f2a4ca19afbd2bbfac Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 16:21:03 +1000 Subject: [PATCH 06/14] viewer: fixed issue that would cause playback queue to stall --- app/widget/viewer/viewer.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 4b9461cde..26e8f188c 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -352,6 +352,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time) { { QMutexLocker locker(playback_queue_.lock()); + while (!playback_queue_.isEmpty()) { const ViewerPlaybackFrame& pf = playback_queue_.first(); @@ -400,11 +401,15 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) playback_speed_); } - playback_queue_next_frame_ = ruler()->GetTime(); - playback_timer_.Start(playback_queue_next_frame_, playback_speed_, timebase_dbl()); + int64_t start_time = ruler()->GetTime(); + + playback_queue_next_frame_ = start_time; + FillPlaybackQueue(); + + playback_timer_.Start(start_time, playback_speed_, timebase_dbl()); foreach (ViewerWindow* window, windows_) { - window->Play(playback_queue_next_frame_, playback_speed_, timebase()); + window->Play(start_time, playback_speed_, timebase()); } controls_->ShowPauseButton(); @@ -416,8 +421,6 @@ void ViewerWidget::PlayInternal(int speed, bool in_to_out_only) } else { connect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWidget::PlaybackTimerUpdate); } - - FillPlaybackQueue(); } void ViewerWidget::PushScrubbedAudio() From e0b08040ce33ab9760b0911e16fa7ed73f5ff64d Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 16:24:13 +1000 Subject: [PATCH 07/14] cmake: explicitly state minimum OIIO version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ccb6e9478..7d102e253 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ find_package(OpenGL REQUIRED) find_package(OpenColorIO REQUIRED) -find_package(OpenImageIO REQUIRED) +find_package(OpenImageIO 1.6 REQUIRED) find_package(OpenEXR REQUIRED) From ec6eb4c053afc9cc607205ddeb955c8212006794 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 16:40:13 +1000 Subject: [PATCH 08/14] cmake: bumped minimum version We use `target_link_libraries` now which is 3.13+ only. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d102e253..ee3cacf7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -cmake_minimum_required(VERSION 3.9 FATAL_ERROR) +cmake_minimum_required(VERSION 3.13 FATAL_ERROR) project(olive-editor VERSION 0.2.0 LANGUAGES CXX) From 8decdd22249fa08e27ac8407b990af9d1a642237 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 16:47:35 +1000 Subject: [PATCH 09/14] travis: use updated cmake version --- .travis/before_install.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis/before_install.sh b/.travis/before_install.sh index 569d5f5bb..3526f518f 100644 --- a/.travis/before_install.sh +++ b/.travis/before_install.sh @@ -11,6 +11,9 @@ if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then # OpenColorIO sudo add-apt-repository ppa:olive-editor/opencolorio -y + # CMake + sudo add-apt-repository ppa:olive-editor/cmake -y + # Update apt sudo apt-get update -qq From 42b749e69753136572ec162df3539c11e0bac174 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 17:20:35 +1000 Subject: [PATCH 10/14] travis: attempt to prioritize our cmake build --- .travis/before_install.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis/before_install.sh b/.travis/before_install.sh index 3526f518f..8c41527d2 100644 --- a/.travis/before_install.sh +++ b/.travis/before_install.sh @@ -2,6 +2,9 @@ if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then + # CMake + sudo add-apt-repository ppa:olive-editor/cmake -y + # Qt 5.11 sudo add-apt-repository ppa:beineri/opt-qt-5.11.0-xenial -y @@ -11,9 +14,6 @@ if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then # OpenColorIO sudo add-apt-repository ppa:olive-editor/opencolorio -y - # CMake - sudo add-apt-repository ppa:olive-editor/cmake -y - # Update apt sudo apt-get update -qq From 476e45cfdc57d27994d5022c09e4a5595865341c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 17:38:47 +1000 Subject: [PATCH 11/14] appveyor: don't upload artifacts AppVeyor has reached its artifact limit causing builds to fail. We already upload separately to GitHub Releases anyway, so it's kind of unnecessary. --- appveyor.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ed053f1e1..436930972 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,9 +14,9 @@ install: - cd %APPVEYOR_BUILD_FOLDER% build_script: - cmd: .appveyor\build.bat -artifacts: -- path: Olive*.zip - name: Olive Portable -- path: Olive*.exe - name: Olive Installer +#artifacts: +#- path: Olive*.zip +# name: Olive Portable +#- path: Olive*.exe +# name: Olive Installer cache: c:\tools\vcpkg\installed\ From a5747715528458dafd7184a82267eabfa00c502c Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 17:55:39 +1000 Subject: [PATCH 12/14] travis: attempt to acquire latest cmake from site --- .travis/before_install.sh | 3 --- .travis/install.sh | 8 +++++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis/before_install.sh b/.travis/before_install.sh index 8c41527d2..569d5f5bb 100644 --- a/.travis/before_install.sh +++ b/.travis/before_install.sh @@ -2,9 +2,6 @@ if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - # CMake - sudo add-apt-repository ppa:olive-editor/cmake -y - # Qt 5.11 sudo add-apt-repository ppa:beineri/opt-qt-5.11.0-xenial -y diff --git a/.travis/install.sh b/.travis/install.sh index fa29d2ef8..8d57de697 100644 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -6,7 +6,13 @@ if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then elif [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - sudo apt-get -y -o Dpkg::Options::="--force-overwrite" install qt511base qt511multimedia qt511svg qt511tools libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev cmake libopencolorio-dev libopenimageio-dev libgl1-mesa-dev + sudo apt-get -y -o Dpkg::Options::="--force-overwrite" install qt511base qt511multimedia qt511svg qt511tools libavformat-dev libavcodec-dev libavfilter-dev libavutil-dev libswscale-dev libswresample-dev libopencolorio-dev libopenimageio-dev libgl1-mesa-dev source /opt/qt*/bin/qt*-env.sh + # Acquire latest cmake (apt somehow gets the wrong version?) + wget -c https://github.com/Kitware/CMake/releases/download/v3.17.2/cmake-3.17.2-Linux-x86_64.sh -O cmake.sh + chmod +x cmake.sh + ./cmake.sh --skip-license --prefix=cmake --exclude-dir + export PATH=$PWD/cmake/bin:$PATH + fi From d653070a9d380920133b6619a21fa9a2da588c9f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 18:53:00 +1000 Subject: [PATCH 13/14] slider: reviewed and conformed code --- .../videostreamproperties.cpp | 2 - app/dialog/speedduration/speedduration.cpp | 4 +- app/node/input.cpp | 13 ++++++ app/node/input.h | 2 + .../nodeparamviewwidgetbridge.cpp | 12 +---- app/widget/slider/sliderbase.cpp | 19 ++++---- app/widget/slider/sliderbase.h | 2 +- app/widget/slider/sliderlabel.cpp | 45 +++++++++++-------- app/widget/slider/sliderlabel.h | 4 +- 9 files changed, 59 insertions(+), 44 deletions(-) diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp index 5f63fdfd3..6f565aa33 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp @@ -80,7 +80,6 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) : imgseq_start_time_ = new IntegerSlider(); imgseq_start_time_->SetMinimum(0); imgseq_start_time_->SetValue(video_stream->start_time()); - imgseq_start_time_->SetDefaultValue(video_stream->start_time()); imgseq_layout->addWidget(imgseq_start_time_, imgseq_row, 1); imgseq_row++; @@ -90,7 +89,6 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) : imgseq_end_time_ = new IntegerSlider(); imgseq_end_time_->SetMinimum(0); imgseq_end_time_->SetValue(video_stream->start_time() + video_stream->duration() - 1); - imgseq_end_time_->SetDefaultValue(video_stream->start_time() + video_stream->duration() - 1); imgseq_layout->addWidget(imgseq_end_time_, imgseq_row, 1); video_layout->addWidget(imgseq_group, row, 0, 1, 2); diff --git a/app/dialog/speedduration/speedduration.cpp b/app/dialog/speedduration/speedduration.cpp index 02aeefa13..84e1b1875 100644 --- a/app/dialog/speedduration/speedduration.cpp +++ b/app/dialog/speedduration/speedduration.cpp @@ -112,9 +112,11 @@ SpeedDurationDialog::SpeedDurationDialog(const rational& timebase, const QListSetTimebase(timebase_); duration_slider_->SetMinimum(1); - duration_slider_->SetDefaultValue(Timecode::time_to_timestamp(clips_.first()->length(), timebase_)); speed_layout->addWidget(duration_slider_, row, 1); + // Calculate duration that would occur if the speed was 100% + duration_slider_->SetDefaultValue(GetUnadjustedLengthTimestamp(clips_.first())); + if (same_duration) { duration_slider_->SetValue(Timecode::time_to_timestamp(clips_.first()->length(), timebase_)); } else { diff --git a/app/node/input.cpp b/app/node/input.cpp index 17c41c02b..db1bbb5c9 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -386,9 +386,22 @@ void NodeInput::GetDependencies(QList &list, bool traverse, bool exclusi QVariant NodeInput::GetDefaultValue() const { + if (default_value_.isEmpty()) { + return QVariant(); + } + return combine_track_values_into_normal_value(default_value_); } +QVariant NodeInput::GetDefaultValueForTrack(int track) const +{ + if (default_value_.isEmpty()) { + return QVariant(); + } + + return default_value_.at(track); +} + QList NodeInput::GetDependencies(bool traverse, bool exclusive_only) const { QList list; diff --git a/app/node/input.h b/app/node/input.h index a8b776261..c9e1b1d8d 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -279,6 +279,8 @@ public: QVariant GetDefaultValue() const; + QVariant GetDefaultValueForTrack(int track) const; + QList GetDependencies(bool traverse = true, bool exclusive_only = false) const; QList GetExclusiveDependencies() const; diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index e791c66fb..ab1f20b49 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -92,11 +92,7 @@ void NodeParamViewWidgetBridge::CreateWidgets() case NodeParam::kInt: { IntegerSlider* slider = new IntegerSlider(); - if (!input_->get_standard_value().isNull()) { - slider->SetDefaultValue(input_->get_standard_value()); - } else { - slider->SetDefaultValue(0); - } + slider->SetDefaultValue(input_->GetDefaultValue()); widgets_.append(slider); connect(slider, &IntegerSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); break; @@ -369,11 +365,7 @@ void NodeParamViewWidgetBridge::CreateSliders(int count) { for (int i=0;iget_standard_value().isNull()) { - fs->SetDefaultValue(input_->get_split_standard_value().at(i)); - } else { - fs->SetDefaultValue(0.0f); - } + fs->SetDefaultValue(input_->GetDefaultValueForTrack(i)); widgets_.append(fs); connect(fs, &FloatSlider::ValueChanged, this, &NodeParamViewWidgetBridge::WidgetCallback); } diff --git a/app/widget/slider/sliderbase.cpp b/app/widget/slider/sliderbase.cpp index 2b30810ca..5de18603d 100644 --- a/app/widget/slider/sliderbase.cpp +++ b/app/widget/slider/sliderbase.cpp @@ -23,7 +23,6 @@ #include #include #include -#include OLIVE_NAMESPACE_ENTER @@ -46,13 +45,13 @@ SliderBase::SliderBase(Mode mode, QWidget *parent) : editor_ = new FocusableLineEdit(this); addWidget(editor_); - connect(label_, SIGNAL(drag_start()), this, SLOT(LabelPressed())); - connect(label_, SIGNAL(dragged(int)), this, SLOT(LabelDragged(int))); - connect(label_, SIGNAL(drag_stop()), this, SLOT(LabelClicked())); - connect(label_, SIGNAL(focused()), this, SLOT(LabelClicked())); - connect(label_, SIGNAL(ResetResult()), this, SLOT(ValueReset())); - connect(editor_, SIGNAL(Confirmed()), this, SLOT(LineEditConfirmed())); - connect(editor_, SIGNAL(Cancelled()), this, SLOT(LineEditCancelled())); + connect(label_, &SliderLabel::drag_start, this, &SliderBase::LabelPressed); + connect(label_, &SliderLabel::dragged, this, &SliderBase::LabelDragged); + connect(label_, &SliderLabel::drag_stop, this, &SliderBase::LabelClicked); + connect(label_, &SliderLabel::focused, this, &SliderBase::LabelClicked); + connect(label_, &SliderLabel::RequestReset, this, &SliderBase::ResetValue); + connect(editor_, &FocusableLineEdit::Confirmed, this, &SliderBase::LineEditConfirmed); + connect(editor_, &FocusableLineEdit::Cancelled, this, &SliderBase::LineEditCancelled); // Set valid cursor based on mode switch (mode_) { @@ -141,7 +140,7 @@ void SliderBase::SetValue(const QVariant &v) void SliderBase::SetDefaultValue(const QVariant &v) { - default_value_ = ClampValue(v); + default_value_ = v; } void SliderBase::SetMinimumInternal(const QVariant &v) @@ -333,7 +332,7 @@ void SliderBase::LineEditCancelled() label_->blockSignals(false); } -void SliderBase::ValueReset() +void SliderBase::ResetValue() { if (!default_value_.isNull()) { SetValue(default_value_); diff --git a/app/widget/slider/sliderbase.h b/app/widget/slider/sliderbase.h index 4272f111c..316e82f37 100644 --- a/app/widget/slider/sliderbase.h +++ b/app/widget/slider/sliderbase.h @@ -124,7 +124,7 @@ private slots: void LineEditCancelled(); - void ValueReset(); + void ResetValue(); }; OLIVE_NAMESPACE_EXIT diff --git a/app/widget/slider/sliderlabel.cpp b/app/widget/slider/sliderlabel.cpp index 1fde819d6..79f077757 100644 --- a/app/widget/slider/sliderlabel.cpp +++ b/app/widget/slider/sliderlabel.cpp @@ -31,7 +31,8 @@ OLIVE_NAMESPACE_ENTER SliderLabel::SliderLabel(QWidget *parent) : - QLabel(parent) + QLabel(parent), + dragging_(false) { QPalette p = palette(); @@ -53,12 +54,13 @@ SliderLabel::SliderLabel(QWidget *parent) : setFocusPolicy(Qt::TabFocus); } -void SliderLabel::mousePressEvent(QMouseEvent *) +void SliderLabel::mousePressEvent(QMouseEvent *e) { - if (QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) { - emit ResetResult(); - }else { - emit drag_start(); + if (e->modifiers() & Qt::AltModifier) { + + emit RequestReset(); + + } else { #if defined(Q_OS_MAC) CGAssociateMouseAndMouseCursorPosition(false); @@ -69,14 +71,18 @@ void SliderLabel::mousePressEvent(QMouseEvent *) static_cast(QApplication::instance())->setOverrideCursor(Qt::BlankCursor); #endif + + emit drag_start(); + + dragging_ = true; + } } void SliderLabel::mouseMoveEvent(QMouseEvent *) { - if (QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) { - // do nothing - }else { + if (dragging_) { + int32_t x_mvmt, y_mvmt; // Keep cursor in the same position @@ -92,23 +98,26 @@ void SliderLabel::mouseMoveEvent(QMouseEvent *) #endif emit dragged(x_mvmt + y_mvmt); + } } void SliderLabel::mouseReleaseEvent(QMouseEvent *) { - if (QGuiApplication::keyboardModifiers().testFlag(Qt::AltModifier)) { - //do nothing - } else { - // Emit a clicked signal - emit drag_stop(); - } + if (dragging_) { + #if defined(Q_OS_MAC) - CGAssociateMouseAndMouseCursorPosition(true); - CGDisplayShowCursor(kCGDirectMainDisplay); + CGAssociateMouseAndMouseCursorPosition(true); + CGDisplayShowCursor(kCGDirectMainDisplay); #else - static_cast(QApplication::instance())->restoreOverrideCursor(); + static_cast(QApplication::instance())->restoreOverrideCursor(); #endif + + emit drag_stop(); + + dragging_ = false; + + } } void SliderLabel::focusInEvent(QFocusEvent *event) diff --git a/app/widget/slider/sliderlabel.h b/app/widget/slider/sliderlabel.h index cd867692a..641caeac4 100644 --- a/app/widget/slider/sliderlabel.h +++ b/app/widget/slider/sliderlabel.h @@ -51,12 +51,12 @@ signals: void focused(); - void ResetResult(); + void RequestReset(); private: QPoint drag_start_; - bool cancel_mm_event_; + bool dragging_; }; From 25b35725f6333d6dc7e2450fb80a0f6b3e1c0fe2 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sat, 9 May 2020 18:55:42 +1000 Subject: [PATCH 14/14] nodes: added more default values to nodes --- app/node/audio/pan/pan.cpp | 2 +- app/node/audio/volume/volume.cpp | 2 +- app/node/generator/matrix/matrix.cpp | 6 +++--- app/node/math/math/math.cpp | 4 ++-- app/node/math/trigonometry/trigonometry.cpp | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index c9ef24520..156c69818 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -27,7 +27,7 @@ PanNode::PanNode() samples_input_ = new NodeInput("samples_in", NodeParam::kSamples); AddInput(samples_input_); - panning_input_ = new NodeInput("panning_in", NodeParam::kFloat); + panning_input_ = new NodeInput("panning_in", NodeParam::kFloat, 0.0); panning_input_->set_property("min", -1.0); panning_input_->set_property("max", 1.0); panning_input_->set_property("view", "percent"); diff --git a/app/node/audio/volume/volume.cpp b/app/node/audio/volume/volume.cpp index 9d815b19a..41b7dbcda 100644 --- a/app/node/audio/volume/volume.cpp +++ b/app/node/audio/volume/volume.cpp @@ -27,7 +27,7 @@ VolumeNode::VolumeNode() samples_input_ = new NodeInput("samples_in", NodeParam::kSamples); AddInput(samples_input_); - volume_input_ = new NodeInput("volume_in", NodeParam::kFloat, 1); + volume_input_ = new NodeInput("volume_in", NodeParam::kFloat, 1.0); volume_input_->set_property("min", 0.0); volume_input_->set_property("view", "db"); AddInput(volume_input_); diff --git a/app/node/generator/matrix/matrix.cpp b/app/node/generator/matrix/matrix.cpp index bafa5d530..ee9ae0a31 100644 --- a/app/node/generator/matrix/matrix.cpp +++ b/app/node/generator/matrix/matrix.cpp @@ -30,10 +30,10 @@ OLIVE_NAMESPACE_ENTER MatrixGenerator::MatrixGenerator() { - position_input_ = new NodeInput("pos_in", NodeParam::kVec2); + position_input_ = new NodeInput("pos_in", NodeParam::kVec2, QVector2D()); AddInput(position_input_); - rotation_input_ = new NodeInput("rot_in", NodeParam::kFloat); + rotation_input_ = new NodeInput("rot_in", NodeParam::kFloat, 0.0f); AddInput(rotation_input_); scale_input_ = new NodeInput("scale_in", NodeParam::kVec2, QVector2D(1.0f, 1.0f)); @@ -48,7 +48,7 @@ MatrixGenerator::MatrixGenerator() connect(uniform_scale_input_, &NodeInput::ValueChanged, this, &MatrixGenerator::UniformScaleChanged); AddInput(uniform_scale_input_); - anchor_input_ = new NodeInput("anchor_in", NodeParam::kVec2); + anchor_input_ = new NodeInput("anchor_in", NodeParam::kVec2, QVector2D()); AddInput(anchor_input_); } diff --git a/app/node/math/math/math.cpp b/app/node/math/math/math.cpp index 9f2f4ca3e..149bcb08a 100644 --- a/app/node/math/math/math.cpp +++ b/app/node/math/math/math.cpp @@ -35,12 +35,12 @@ MathNode::MathNode() method_in_->set_is_keyframable(false); AddInput(method_in_); - param_a_in_ = new NodeInput(QStringLiteral("param_a_in"), NodeParam::kFloat); + param_a_in_ = new NodeInput(QStringLiteral("param_a_in"), NodeParam::kFloat, 0.0); param_a_in_->set_property(QStringLiteral("decimalplaces"), 8); param_a_in_->set_property(QStringLiteral("autotrim"), true); AddInput(param_a_in_); - param_b_in_ = new NodeInput(QStringLiteral("param_b_in"), NodeParam::kFloat); + param_b_in_ = new NodeInput(QStringLiteral("param_b_in"), NodeParam::kFloat, 0.0); param_b_in_->set_property(QStringLiteral("decimalplaces"), 8); param_b_in_->set_property(QStringLiteral("autotrim"), true); AddInput(param_b_in_); diff --git a/app/node/math/trigonometry/trigonometry.cpp b/app/node/math/trigonometry/trigonometry.cpp index 7ab8ddb3a..9352fded8 100644 --- a/app/node/math/trigonometry/trigonometry.cpp +++ b/app/node/math/trigonometry/trigonometry.cpp @@ -29,7 +29,7 @@ TrigonometryNode::TrigonometryNode() method_in_->set_is_keyframable(false); AddInput(method_in_); - x_in_ = new NodeInput(QStringLiteral("x_in"), NodeParam::kFloat); + x_in_ = new NodeInput(QStringLiteral("x_in"), NodeParam::kFloat, 0.0); AddInput(x_in_); }