diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index 56ad361ce..e9a0bb671 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -499,7 +499,12 @@ bool FFmpegDecoder::Probe(Footage *f) // Create an audio stream object AudioStreamPtr audio_stream = std::make_shared(); - audio_stream->set_layout(avstream_->codecpar->channel_layout); + uint64_t channel_layout = avstream_->codecpar->channel_layout; + if (!channel_layout) { + channel_layout = static_cast(av_get_default_channel_layout(avstream_->codecpar->channels)); + } + + audio_stream->set_channel_layout(channel_layout); audio_stream->set_channels(avstream_->codecpar->channels); audio_stream->set_sample_rate(avstream_->codecpar->sample_rate); diff --git a/app/node/audio/pan/pan.cpp b/app/node/audio/pan/pan.cpp index 44386de1a..dcc61f963 100644 --- a/app/node/audio/pan/pan.cpp +++ b/app/node/audio/pan/pan.cpp @@ -50,15 +50,17 @@ void PanNode::ProcessSamples(const NodeValueDatabase *values, const AudioRenderi float pan_val = (*values)[panning_input_].Get(NodeParam::kFloat).toFloat(); + output[index] = input[index]; + if (index%2 == 0) { // Sample is left channel if (pan_val > 0) { - output[index] = input[index] * (1.0F - pan_val); + output[index] *= (1.0F - pan_val); } } else { // Sample is right channel if (pan_val < 0) { - output[index] = input[index] * (1.0F - qAbs(pan_val)); + output[index] *= (1.0F - qAbs(pan_val)); } } } diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 786d9c718..29189a479 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -20,6 +20,8 @@ #include "media.h" +#include "common/timecodefunctions.h" + MediaInput::MediaInput() : connected_footage_(nullptr) { @@ -45,6 +47,20 @@ void MediaInput::Retranslate() footage_input_->set_name(tr("Footage")); } +NodeValueTable MediaInput::Value(const NodeValueDatabase &value) const +{ + NodeValueTable table = value.Merge(); + + if (connected_footage_) { + rational media_duration = Timecode::timestamp_to_time(connected_footage_->duration(), + connected_footage_->timebase()); + + table.Push(NodeInput::kRational, QVariant::fromValue(media_duration), "length"); + } + + return table; +} + void MediaInput::FootageChanged() { StreamPtr new_footage = footage_input_->get_standard_value().value(); diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index 125c29c3d..0253cc231 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -38,6 +38,8 @@ public: virtual void Retranslate() override; + virtual NodeValueTable Value(const NodeValueDatabase& value) const override; + protected: NodeInput* footage_input_; diff --git a/app/node/output/timeline/timeline.cpp b/app/node/output/timeline/timeline.cpp index 59f79c40e..4b852e54a 100644 --- a/app/node/output/timeline/timeline.cpp +++ b/app/node/output/timeline/timeline.cpp @@ -98,7 +98,7 @@ const rational &TimelineOutput::timebase() const NodeValueTable TimelineOutput::Value(const NodeValueDatabase &value) const { NodeValueTable table = value.Merge(); - table.Push(NodeParam::kRational, QVariant::fromValue(length())); + table.Push(NodeParam::kRational, QVariant::fromValue(length()), "length"); return table; } diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index b0a583669..2662fec87 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -119,7 +119,7 @@ rational ViewerOutput::Length() Node* connected_node = length_input_->get_connected_node(); if (connected_node) { - return connected_node->Value(NodeValueDatabase()).Get(NodeParam::kNumber).value(); + return connected_node->Value(NodeValueDatabase()).Get(NodeParam::kNumber, "length").value(); } return 0; diff --git a/app/node/param.cpp b/app/node/param.cpp index 3b1696b8e..d2cbd8b68 100644 --- a/app/node/param.cpp +++ b/app/node/param.cpp @@ -65,7 +65,7 @@ void NodeParam::set_name(const QString &name) name_ = name; } -Node *NodeParam::parentNode() +Node *NodeParam::parentNode() const { QObject* p = parent(); @@ -87,7 +87,7 @@ int NodeParam::index() return parentNode()->IndexOfParameter(this); } -bool NodeParam::IsConnected() +bool NodeParam::IsConnected() const { return !edges_.isEmpty(); } diff --git a/app/node/param.h b/app/node/param.h index d2e6983fe..e6057f2b8 100644 --- a/app/node/param.h +++ b/app/node/param.h @@ -254,7 +254,7 @@ public: * Nodes and NodeParams use the QObject parent-child system. This function is a convenience function for * static_cast(QObject::parent()) */ - Node* parentNode(); + Node* parentNode() const; /** * @brief Return the row index of this parameter in the parent node (primarily used for UI drawing functions) @@ -264,7 +264,7 @@ public: /** * @brief Returns whether anything is connected to this parameter or not */ - bool IsConnected(); + bool IsConnected() const; bool IsConnectable() const; void SetConnectable(bool connectable); diff --git a/app/panel/CMakeLists.txt b/app/panel/CMakeLists.txt index aa33c11c6..9358ead66 100644 --- a/app/panel/CMakeLists.txt +++ b/app/panel/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(audiomonitor) add_subdirectory(curve) +add_subdirectory(footageviewer) add_subdirectory(node) add_subdirectory(param) add_subdirectory(project) diff --git a/app/panel/footageviewer/CMakeLists.txt b/app/panel/footageviewer/CMakeLists.txt new file mode 100644 index 000000000..127fe1a00 --- /dev/null +++ b/app/panel/footageviewer/CMakeLists.txt @@ -0,0 +1,22 @@ +# 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 . + +set(OLIVE_SOURCES + ${OLIVE_SOURCES} + panel/footageviewer/footageviewer.h + panel/footageviewer/footageviewer.cpp + PARENT_SCOPE +) diff --git a/app/panel/footageviewer/footageviewer.cpp b/app/panel/footageviewer/footageviewer.cpp new file mode 100644 index 000000000..14db827fa --- /dev/null +++ b/app/panel/footageviewer/footageviewer.cpp @@ -0,0 +1,58 @@ +/*** + + 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 "footageviewer.h" + +#include "widget/viewer/footageviewer.h" + +FootageViewerPanel::FootageViewerPanel(QWidget *parent) : + ViewerPanelBase(parent) +{ + // FIXME: This won't work if there's ever more than one of this panel + setObjectName("FootageViewerPanel"); + + // QObject system handles deleting this + viewer_ = new FootageViewerWidget(this); + + // Set ViewerWidget as the central widget + setWidget(viewer_); + + // Set strings + Retranslate(); +} + +void FootageViewerPanel::SetFootage(Footage *f) +{ + static_cast(viewer_)->SetFootage(f); +} + +void FootageViewerPanel::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::LanguageChange) { + Retranslate(); + } + PanelWidget::changeEvent(e); +} + +void FootageViewerPanel::Retranslate() +{ + SetTitle(tr("Footage Viewer")); + SetSubtitle(tr("(none)")); +} diff --git a/app/panel/footageviewer/footageviewer.h b/app/panel/footageviewer/footageviewer.h new file mode 100644 index 000000000..582c56a41 --- /dev/null +++ b/app/panel/footageviewer/footageviewer.h @@ -0,0 +1,46 @@ +/*** + + 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 FOOTAGE_VIEWER_PANEL_H +#define FOOTAGE_VIEWER_PANEL_H + +#include + +#include "panel/viewer/viewerbase.h" + +/** + * @brief Dockable wrapper around a ViewerWidget + */ +class FootageViewerPanel : public ViewerPanelBase { + Q_OBJECT +public: + FootageViewerPanel(QWidget* parent); + + void SetFootage(Footage* f); + +protected: + virtual void changeEvent(QEvent* e) override; + +private: + void Retranslate(); + +}; + +#endif // FOOTAGE_VIEWER_PANEL_H diff --git a/app/panel/project/project.cpp b/app/panel/project/project.cpp index fbd6b2441..7d17b8e27 100644 --- a/app/panel/project/project.cpp +++ b/app/panel/project/project.cpp @@ -24,6 +24,8 @@ #include #include "core.h" +#include "panel/footageviewer/footageviewer.h" +#include "panel/panelmanager.h" #include "widget/menu/menushared.h" #include "widget/projecttoolbar/projecttoolbar.h" @@ -121,9 +123,12 @@ void ProjectPanel::ItemDoubleClickSlot(Item *item) if (item == nullptr) { // If the user double clicks on empty space, show the import dialog Core::instance()->DialogImportShow(); + } else if (item->type() == Item::kFootage) { + // Open this footage in a FootageViewer + PanelManager::instance()->MostRecentlyFocused()->SetFootage(static_cast(item)); + } else if (item->type() == Item::kSequence) { + // FIXME: Open this sequence in the Timeline } - - // FIXME: Double click Item should do something } void ProjectPanel::ShowNewMenu() diff --git a/app/panel/viewer/CMakeLists.txt b/app/panel/viewer/CMakeLists.txt index b2ccf398b..74bdc110a 100644 --- a/app/panel/viewer/CMakeLists.txt +++ b/app/panel/viewer/CMakeLists.txt @@ -18,5 +18,7 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} panel/viewer/viewer.h panel/viewer/viewer.cpp + panel/viewer/viewerbase.h + panel/viewer/viewerbase.cpp PARENT_SCOPE ) diff --git a/app/panel/viewer/viewer.cpp b/app/panel/viewer/viewer.cpp index 471ef78fa..7025eb6b6 100644 --- a/app/panel/viewer/viewer.cpp +++ b/app/panel/viewer/viewer.cpp @@ -21,7 +21,7 @@ #include "viewer.h" ViewerPanel::ViewerPanel(QWidget *parent) : - PanelWidget(parent) + ViewerPanelBase(parent) { // FIXME: This won't work if there's ever more than one of this panel setObjectName("ViewerPanel"); @@ -37,61 +37,6 @@ ViewerPanel::ViewerPanel(QWidget *parent) : Retranslate(); } -void ViewerPanel::ZoomIn() -{ - viewer_->SetScale(viewer_->scale() * 2); -} - -void ViewerPanel::ZoomOut() -{ - viewer_->SetScale(viewer_->scale() * 0.5); -} - -void ViewerPanel::GoToStart() -{ - viewer_->GoToStart(); -} - -void ViewerPanel::PrevFrame() -{ - viewer_->PrevFrame(); -} - -void ViewerPanel::PlayPause() -{ - viewer_->TogglePlayPause(); -} - -void ViewerPanel::NextFrame() -{ - viewer_->NextFrame(); -} - -void ViewerPanel::GoToEnd() -{ - viewer_->GoToEnd(); -} - -void ViewerPanel::ShuttleLeft() -{ - viewer_->ShuttleLeft(); -} - -void ViewerPanel::ShuttleStop() -{ - viewer_->ShuttleStop(); -} - -void ViewerPanel::ShuttleRight() -{ - viewer_->ShuttleRight(); -} - -void ViewerPanel::SetTimebase(const rational &timebase) -{ - viewer_->SetTimebase(timebase); -} - void ViewerPanel::ConnectViewerNode(ViewerOutput *node) { viewer_->ConnectViewerNode(node); diff --git a/app/panel/viewer/viewer.h b/app/panel/viewer/viewer.h index 322f3608c..ea128fe52 100644 --- a/app/panel/viewer/viewer.h +++ b/app/panel/viewer/viewer.h @@ -23,39 +23,16 @@ #include -#include "widget/panel/panel.h" -#include "widget/viewer/viewer.h" +#include "viewerbase.h" /** * @brief Dockable wrapper around a ViewerWidget */ -class ViewerPanel : public PanelWidget { +class ViewerPanel : public ViewerPanelBase { Q_OBJECT public: ViewerPanel(QWidget* parent); - virtual void ZoomIn() override; - - virtual void ZoomOut() override; - - virtual void GoToStart() override; - - virtual void PrevFrame() override; - - virtual void PlayPause() override; - - virtual void NextFrame() override; - - virtual void GoToEnd() override; - - virtual void ShuttleLeft() override; - - virtual void ShuttleStop() override; - - virtual void ShuttleRight() override; - - void SetTimebase(const rational& timebase); - void ConnectViewerNode(ViewerOutput* node); void DisconnectViewerNode(); @@ -76,7 +53,6 @@ signals: private: void Retranslate(); - ViewerWidget* viewer_; }; #endif // VIEWER_PANEL_H diff --git a/app/panel/viewer/viewerbase.cpp b/app/panel/viewer/viewerbase.cpp new file mode 100644 index 000000000..e5d5af669 --- /dev/null +++ b/app/panel/viewer/viewerbase.cpp @@ -0,0 +1,56 @@ +#include "viewerbase.h" + +ViewerPanelBase::ViewerPanelBase(QWidget *parent) : + PanelWidget(parent) +{ +} + +void ViewerPanelBase::ZoomIn() +{ + viewer_->SetScale(viewer_->scale() * 2); +} + +void ViewerPanelBase::ZoomOut() +{ + viewer_->SetScale(viewer_->scale() * 0.5); +} + +void ViewerPanelBase::GoToStart() +{ + viewer_->GoToStart(); +} + +void ViewerPanelBase::PrevFrame() +{ + viewer_->PrevFrame(); +} + +void ViewerPanelBase::PlayPause() +{ + viewer_->TogglePlayPause(); +} + +void ViewerPanelBase::NextFrame() +{ + viewer_->NextFrame(); +} + +void ViewerPanelBase::GoToEnd() +{ + viewer_->GoToEnd(); +} + +void ViewerPanelBase::ShuttleLeft() +{ + viewer_->ShuttleLeft(); +} + +void ViewerPanelBase::ShuttleStop() +{ + viewer_->ShuttleStop(); +} + +void ViewerPanelBase::ShuttleRight() +{ + viewer_->ShuttleRight(); +} diff --git a/app/panel/viewer/viewerbase.h b/app/panel/viewer/viewerbase.h new file mode 100644 index 000000000..475eeaf16 --- /dev/null +++ b/app/panel/viewer/viewerbase.h @@ -0,0 +1,40 @@ +#ifndef VIEWERPANELBASE_H +#define VIEWERPANELBASE_H + +#include "widget/panel/panel.h" +#include "widget/viewer/viewer.h" + +class ViewerPanelBase : public PanelWidget +{ + Q_OBJECT +public: + ViewerPanelBase(QWidget* parent = nullptr); + + virtual void ZoomIn() override; + + virtual void ZoomOut() override; + + virtual void GoToStart() override; + + virtual void PrevFrame() override; + + virtual void PlayPause() override; + + virtual void NextFrame() override; + + virtual void GoToEnd() override; + + virtual void ShuttleLeft() override; + + virtual void ShuttleStop() override; + + virtual void ShuttleRight() override; + +protected: + ViewerWidget* viewer_; + +private: + +}; + +#endif // VIEWERPANELBASE_H diff --git a/app/project/item/footage/audiostream.cpp b/app/project/item/footage/audiostream.cpp index e909a369a..a6a154bdf 100644 --- a/app/project/item/footage/audiostream.cpp +++ b/app/project/item/footage/audiostream.cpp @@ -42,12 +42,12 @@ void AudioStream::set_channels(const int &channels) channels_ = channels; } -const uint64_t &AudioStream::layout() +const uint64_t &AudioStream::channel_layout() { return layout_; } -void AudioStream::set_layout(const uint64_t &layout) +void AudioStream::set_channel_layout(const uint64_t &layout) { layout_ = layout; } diff --git a/app/project/item/footage/audiostream.h b/app/project/item/footage/audiostream.h index 4635c21b0..5350596d5 100644 --- a/app/project/item/footage/audiostream.h +++ b/app/project/item/footage/audiostream.h @@ -37,8 +37,8 @@ public: const int& channels(); void set_channels(const int& channels); - const uint64_t& layout(); - void set_layout(const uint64_t& layout); + const uint64_t& channel_layout(); + void set_channel_layout(const uint64_t& channel_layout); const int& sample_rate(); void set_sample_rate(const int& sample_rate); diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index cb53abcb0..d639eb2de 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -25,6 +25,7 @@ #include #include "common/define.h" +#include "core.h" #include "dialog/footageproperties/footageproperties.h" ProjectExplorer::ProjectExplorer(QWidget *parent) : @@ -38,8 +39,8 @@ ProjectExplorer::ProjectExplorer(QWidget *parent) : // Set up navigation bar nav_bar_ = new ProjectExplorerNavigation(this); - connect(nav_bar_, SIGNAL(SizeChanged(int)), this, SLOT(SizeChangedSlot(int))); - connect(nav_bar_, SIGNAL(DirectoryUpClicked()), this, SLOT(DirUpSlot())); + connect(nav_bar_, &ProjectExplorerNavigation::SizeChanged, this, &ProjectExplorer::SizeChangedSlot); + connect(nav_bar_, &ProjectExplorerNavigation::DirectoryUpClicked, this, &ProjectExplorer::DirUpSlot); layout->addWidget(nav_bar_); // Set up stacked widget @@ -111,8 +112,9 @@ void ProjectExplorer::AddView(QAbstractItemView *view) { view->setModel(&model_); view->setEditTriggers(QAbstractItemView::NoEditTriggers); - connect(view, SIGNAL(DoubleClickedView(const QModelIndex&)), this, SLOT(DoubleClickViewSlot(const QModelIndex&))); - connect(view, SIGNAL(clicked(const QModelIndex&)), this, SLOT(ItemClickedSlot(const QModelIndex&))); + connect(view, &QAbstractItemView::clicked, this, &ProjectExplorer::ItemClickedSlot); + connect(view, &QAbstractItemView::doubleClicked, this, &ProjectExplorer::ItemDoubleClickedSlot); + connect(view, SIGNAL(DoubleClickedEmptyArea()), this, SLOT(ViewEmptyAreaDoubleClickedSlot())); stacked_widget_->addWidget(view); } @@ -162,30 +164,34 @@ void ProjectExplorer::ItemClickedSlot(const QModelIndex &index) } } -void ProjectExplorer::DoubleClickViewSlot(const QModelIndex &index) +void ProjectExplorer::ViewEmptyAreaDoubleClickedSlot() { - if (index.isValid()) { + // Ensure no attempts to rename are made + clicked_index_ = QModelIndex(); + rename_timer_.stop(); - // Retrieve source item from index - Item* i = static_cast(index.internalPointer()); + emit DoubleClickedItem(nullptr); +} - // If the item is a folder, browse to it - if (i->CanHaveChildren() - && (view_type() == ProjectToolbar::ListView || view_type() == ProjectToolbar::IconView)) { +void ProjectExplorer::ItemDoubleClickedSlot(const QModelIndex &index) +{ + // Ensure no attempts to rename are made + clicked_index_ = QModelIndex(); + rename_timer_.stop(); - BrowseToFolder(index); + // Retrieve source item from index + Item* i = static_cast(index.internalPointer()); - } + // If the item is a folder, browse to it + if (i->CanHaveChildren() + && (view_type() == ProjectToolbar::ListView || view_type() == ProjectToolbar::IconView)) { - // Emit a signal - emit DoubleClickedItem(i); - - } else { - - // Emit nullptr since no item was actually clicked on - emit DoubleClickedItem(nullptr); + BrowseToFolder(index); } + + // Emit a signal + emit DoubleClickedItem(i); } void ProjectExplorer::SizeChangedSlot(int s) @@ -218,9 +224,6 @@ void ProjectExplorer::RenameTimerSlot() rename_timer_.stop(); } -// FIXME: This is down here because of the code in ShowContextMenu() which may be unnecessary allowing this to be removed -#include "core.h" - void ProjectExplorer::ShowContextMenu() { QMenu menu; @@ -231,17 +234,17 @@ void ProjectExplorer::ShowContextMenu() if (selected_items.isEmpty()) { // FIXME: These are both duplicates of items from MainMenu, is there any way to re-use the code? QAction* import_action = menu.addAction(tr("&Import...")); - connect(import_action, SIGNAL(triggered(bool)), Core::instance(), SLOT(DialogImportShow())); + connect(import_action, &QAction::triggered, Core::instance(), &Core::DialogImportShow); menu.addSeparator(); QAction* project_properties = menu.addAction(tr("&Project Properties...")); - connect(project_properties, SIGNAL(triggered(bool)), Core::instance(), SLOT(DialogProjectPropertiesShow())); + connect(project_properties, &QAction::triggered, Core::instance(), &Core::DialogProjectPropertiesShow); } else { QAction* properties_action = menu.addAction(tr("P&roperties")); if (selected_items.first()->type() == Item::kFootage) { - connect(properties_action, SIGNAL(triggered(bool)), this, SLOT(ShowFootagePropertiesDialog())); + connect(properties_action, &QAction::triggered, this, &ProjectExplorer::ShowFootagePropertiesDialog); } } diff --git a/app/widget/projectexplorer/projectexplorer.h b/app/widget/projectexplorer/projectexplorer.h index 45416b49a..de08d374e 100644 --- a/app/widget/projectexplorer/projectexplorer.h +++ b/app/widget/projectexplorer/projectexplorer.h @@ -135,7 +135,9 @@ private: private slots: void ItemClickedSlot(const QModelIndex& index); - void DoubleClickViewSlot(const QModelIndex& index); + void ViewEmptyAreaDoubleClickedSlot(); + + void ItemDoubleClickedSlot(const QModelIndex& index); void SizeChangedSlot(int s); diff --git a/app/widget/projectexplorer/projectexplorerlistviewbase.cpp b/app/widget/projectexplorer/projectexplorerlistviewbase.cpp index a29099030..8aca63ef0 100644 --- a/app/widget/projectexplorer/projectexplorerlistviewbase.cpp +++ b/app/widget/projectexplorer/projectexplorerlistviewbase.cpp @@ -43,9 +43,8 @@ void ProjectExplorerListViewBase::mouseDoubleClickEvent(QMouseEvent *event) // Perform default double click functions QListView::mouseDoubleClickEvent(event); - // Get the index at whatever position was double clicked - QModelIndex index = indexAt(event->pos()); - - // Emit the signal with this index - emit DoubleClickedView(index); + // QAbstractItemView already has a doubleClicked() signal, but we emit another here for double clicking empty space + if (!indexAt(event->pos()).isValid()) { + emit DoubleClickedEmptyArea(); + } } diff --git a/app/widget/projectexplorer/projectexplorerlistviewbase.h b/app/widget/projectexplorer/projectexplorerlistviewbase.h index 72e5f5fd2..41f563f67 100644 --- a/app/widget/projectexplorer/projectexplorerlistviewbase.h +++ b/app/widget/projectexplorer/projectexplorerlistviewbase.h @@ -33,7 +33,7 @@ class ProjectExplorerListViewBase : public QListView public: ProjectExplorerListViewBase(QWidget* parent); -protected: +protected: /** * @brief Double click event override * @@ -48,9 +48,9 @@ signals: /** * @brief Unconditional double click signal * - * Emits a signal when the view is double clicked, regardless of whether the double clicked index was valid. + * Emits a signal when the view is double clicked but not on any particular item */ - void DoubleClickedView(const QModelIndex& index); + void DoubleClickedEmptyArea(); }; #endif // PROJECTEXPLORERLISTVIEWBASE_H diff --git a/app/widget/projectexplorer/projectexplorertreeview.cpp b/app/widget/projectexplorer/projectexplorertreeview.cpp index 5cc3c6e80..92771d828 100644 --- a/app/widget/projectexplorer/projectexplorertreeview.cpp +++ b/app/widget/projectexplorer/projectexplorertreeview.cpp @@ -46,9 +46,8 @@ void ProjectExplorerTreeView::mouseDoubleClickEvent(QMouseEvent *event) // Perform default double click functions QTreeView::mouseDoubleClickEvent(event); - // Get the index at whatever position was double clicked - QModelIndex index = indexAt(event->pos()); - - // Emit the signal with this index - emit DoubleClickedView(index); + // QAbstractItemView already has a doubleClicked() signal, but we emit another here for double clicking empty space + if (!indexAt(event->pos()).isValid()) { + emit DoubleClickedEmptyArea(); + } } diff --git a/app/widget/projectexplorer/projectexplorertreeview.h b/app/widget/projectexplorer/projectexplorertreeview.h index 8ab60ac14..5e221e0ab 100644 --- a/app/widget/projectexplorer/projectexplorertreeview.h +++ b/app/widget/projectexplorer/projectexplorertreeview.h @@ -50,9 +50,9 @@ signals: /** * @brief Unconditional double click signal * - * Emits a signal when the view is double clicked, regardless of whether the double clicked index was valid. + * Emits a signal when the view is double clicked but not on any particular item */ - void DoubleClickedView(const QModelIndex& index); + void DoubleClickedEmptyArea(); }; #endif // PROJECTEXPLORERTREEVIEW_H diff --git a/app/widget/viewer/CMakeLists.txt b/app/widget/viewer/CMakeLists.txt index 10267b43d..c5cf17d3b 100644 --- a/app/widget/viewer/CMakeLists.txt +++ b/app/widget/viewer/CMakeLists.txt @@ -16,6 +16,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + widget/viewer/footageviewer.h + widget/viewer/footageviewer.cpp widget/viewer/viewer.h widget/viewer/viewer.cpp widget/viewer/viewerglwidget.h diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp new file mode 100644 index 000000000..7e13c9a92 --- /dev/null +++ b/app/widget/viewer/footageviewer.cpp @@ -0,0 +1,60 @@ +#include "footageviewer.h" + +#include "project/project.h" + +FootageViewerWidget::FootageViewerWidget(QWidget *parent) : + ViewerWidget(parent), + footage_(nullptr) +{ + video_node_ = new VideoInput(); + audio_node_ = new AudioInput(); + viewer_node_ = new ViewerOutput(); + + NodeParam::ConnectEdge(video_node_->output(), viewer_node_->texture_input()); + NodeParam::ConnectEdge(audio_node_->output(), viewer_node_->samples_input()); + NodeParam::ConnectEdge(video_node_->output(), viewer_node_->length_input()); +} + +void FootageViewerWidget::SetFootage(Footage *footage) +{ + if (footage_) { + DisconnectViewerNode(); + } + + footage_ = footage; + + if (footage_) { + VideoStreamPtr video_stream = nullptr; + AudioStreamPtr audio_stream = nullptr; + + foreach (StreamPtr s, footage->streams()) { + if (!audio_stream && s->type() == Stream::kAudio) { + audio_stream = std::static_pointer_cast(s); + } + + if (!video_stream + && (s->type() == Stream::kVideo || s->type() == Stream::kImage)) { + video_stream = std::static_pointer_cast(s); + } + + if (audio_stream && video_stream) { + break; + } + } + + if (video_stream) { + video_node_->SetFootage(video_stream); + viewer_node_->set_video_params(VideoParams(video_stream->width(), video_stream->height(), video_stream->frame_rate().flipped())); + } + + if (audio_stream) { + audio_node_->SetFootage(audio_stream); + qDebug() << "Audio stream channel layout:" << audio_stream->channel_layout(); + viewer_node_->set_audio_params(AudioParams(audio_stream->sample_rate(), audio_stream->channel_layout())); + } + + ConnectViewerNode(viewer_node_, footage->project()->color_manager()); + video_renderer_->InvalidateCache(0, viewer_node_->Length()); + audio_renderer_->InvalidateCache(0, viewer_node_->Length()); + } +} diff --git a/app/widget/viewer/footageviewer.h b/app/widget/viewer/footageviewer.h new file mode 100644 index 000000000..257ceabae --- /dev/null +++ b/app/widget/viewer/footageviewer.h @@ -0,0 +1,28 @@ +#ifndef FOOTAGEVIEWERWIDGET_H +#define FOOTAGEVIEWERWIDGET_H + +#include "node/input/media/audio/audio.h" +#include "node/input/media/video/video.h" +#include "node/output/viewer/viewer.h" +#include "viewer.h" + +class FootageViewerWidget : public ViewerWidget +{ + Q_OBJECT +public: + FootageViewerWidget(QWidget* parent = nullptr); + + void SetFootage(Footage* footage); + +private: + Footage* footage_; + + VideoInput* video_node_; + + AudioInput* audio_node_; + + ViewerOutput* viewer_node_; + +}; + +#endif // FOOTAGEVIEWERWIDGET_H diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 8d9899c78..3e2827f46 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -133,7 +133,7 @@ bool ViewerWidget::IsPlaying() const return playback_timer_.isActive(); } -void ViewerWidget::ConnectViewerNode(ViewerOutput *node) +void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_manager) { if (viewer_node_ != nullptr) { SetTimebase(0); @@ -153,6 +153,9 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node) // Set texture to new texture (or null if no viewer node is available) UpdateTextureFromNode(GetTime()); + video_renderer_->SetViewerNode(viewer_node_); + audio_renderer_->SetViewerNode(viewer_node_); + if (viewer_node_ != nullptr) { SetTimebase(viewer_node_->video_params().time_base()); @@ -163,14 +166,17 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node) SizeChangedSlot(viewer_node_->video_params().width(), viewer_node_->video_params().height()); LengthChangedSlot(viewer_node_->Length()); - gl_widget_->ConnectColorManager(static_cast(viewer_node_->parent())->project()->color_manager()); + if (color_manager) { + gl_widget_->ConnectColorManager(color_manager); + } else if (viewer_node_->parent()) { + gl_widget_->ConnectColorManager(static_cast(viewer_node_->parent())->project()->color_manager()); + } else { + qWarning() << "Failed to find a suitable color manager for the connected viewer node"; + } + + video_renderer_->SetParameters(VideoRenderingParams(viewer_node_->video_params(), PixelFormat::PIX_FMT_RGBA16F, RenderMode::kOffline, 2)); + audio_renderer_->SetParameters(AudioRenderingParams(viewer_node_->audio_params(), SAMPLE_FMT_FLT)); } - - video_renderer_->SetViewerNode(viewer_node_); - video_renderer_->SetParameters(VideoRenderingParams(viewer_node_->video_params(), PixelFormat::PIX_FMT_RGBA16F, RenderMode::kOffline, 2)); - - audio_renderer_->SetViewerNode(viewer_node_); - audio_renderer_->SetParameters(AudioRenderingParams(viewer_node_->audio_params(), SAMPLE_FMT_FLT)); } void ViewerWidget::DisconnectViewerNode() diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index caac4cd40..8c82e4be8 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -63,7 +63,7 @@ public: bool IsPlaying() const; - void ConnectViewerNode(ViewerOutput* node); + void ConnectViewerNode(ViewerOutput* node, ColorManager *color_manager = nullptr); void DisconnectViewerNode(); @@ -136,6 +136,9 @@ signals: protected: virtual void resizeEvent(QResizeEvent *event) override; + OpenGLBackend* video_renderer_; + AudioBackend* audio_renderer_; + private: void UpdateTimeInternal(int64_t i); @@ -145,9 +148,6 @@ private: void PushScrubbedAudio(); - OpenGLBackend* video_renderer_; - AudioBackend* audio_renderer_; - ViewerSizer* sizer_; ViewerGLWidget* gl_widget_; diff --git a/app/widget/viewer/viewerglwidget.cpp b/app/widget/viewer/viewerglwidget.cpp index c64fef5a4..2f226ef1e 100644 --- a/app/widget/viewer/viewerglwidget.cpp +++ b/app/widget/viewer/viewerglwidget.cpp @@ -147,6 +147,12 @@ void ViewerGLWidget::paintGL() void ViewerGLWidget::RefreshColorPipeline() { + if (!color_manager_) { + color_service_ = nullptr; + pipeline_ = nullptr; + return; + } + QStringList displays = color_manager_->ListAvailableDisplays(); if (!displays.contains(ocio_display_)) { ocio_display_ = color_manager_->GetDefaultDisplay(); diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 1a2d03886..8cf3ef855 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -54,8 +54,11 @@ MainWindow::MainWindow(QWidget *parent) : // Create standard panels node_panel_ = PanelManager::instance()->CreatePanel(this); addDockWidget(Qt::TopDockWidgetArea, node_panel_); + footage_viewer_panel_ = PanelManager::instance()->CreatePanel(this); + addDockWidget(Qt::TopDockWidgetArea, footage_viewer_panel_); param_panel_ = PanelManager::instance()->CreatePanel(this); - addDockWidget(Qt::TopDockWidgetArea, param_panel_); + tabifyDockWidget(footage_viewer_panel_, param_panel_); + footage_viewer_panel_->raise(); viewer_panel_ = PanelManager::instance()->CreatePanel(this); addDockWidget(Qt::TopDockWidgetArea, viewer_panel_); project_panel_ = PanelManager::instance()->CreatePanel(this); @@ -150,6 +153,7 @@ void MainWindow::SetDefaultLayout() task_man_panel_->setFloating(true); task_man_panel_->setVisible(false); curve_panel_->setFloating(true); + curve_panel_->setVisible(false); resizeDocks({node_panel_, param_panel_, viewer_panel_}, {width()/3, width()/3, width()/3}, diff --git a/app/window/mainwindow/mainwindow.h b/app/window/mainwindow/mainwindow.h index c8864bf8a..a334d81e8 100644 --- a/app/window/mainwindow/mainwindow.h +++ b/app/window/mainwindow/mainwindow.h @@ -32,6 +32,7 @@ #include "panel/taskmanager/taskmanager.h" #include "panel/timeline/timeline.h" #include "panel/tool/tool.h" +#include "panel/footageviewer/footageviewer.h" #include "panel/viewer/viewer.h" #include "project/project.h" @@ -60,6 +61,7 @@ private: NodePanel* node_panel_; ParamPanel* param_panel_; ViewerPanel* viewer_panel_; + FootageViewerPanel* footage_viewer_panel_; ProjectPanel* project_panel_; ToolPanel* tool_panel_; TimelinePanel* timeline_panel_;