From 898ea25e89a7cd4ffd736ebfaabcbb7510077c3b Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Tue, 6 Apr 2021 13:08:56 +1000 Subject: [PATCH] finished merge of viewer and footage --- app/codec/decoder.h | 1 - app/codec/ffmpeg/ffmpegdecoder.cpp | 2 +- app/dialog/export/export.cpp | 31 +- app/dialog/sequence/sequence.cpp | 16 +- .../sequence/sequencedialogparametertab.cpp | 12 +- app/node/node.cpp | 5 +- app/node/node.h | 2 +- app/node/output/track/track.cpp | 19 + app/node/output/track/track.h | 61 +++ app/node/output/viewer/viewer.cpp | 340 +++++++++++++--- app/node/output/viewer/viewer.h | 56 ++- app/node/traverser.cpp | 4 +- app/node/value.cpp | 1 - app/panel/footageviewer/footageviewer.cpp | 33 +- app/panel/footageviewer/footageviewer.h | 4 +- app/panel/project/footagemanagementpanel.h | 2 +- app/panel/project/project.cpp | 21 +- app/panel/project/project.h | 4 +- app/panel/timebased/timebased.cpp | 8 +- app/panel/timebased/timebased.h | 12 +- app/panel/timeline/timeline.cpp | 4 +- app/panel/timeline/timeline.h | 4 +- app/project/item/footage/CMakeLists.txt | 1 - app/project/item/footage/footage.cpp | 370 ++++-------------- app/project/item/footage/footage.h | 96 +---- app/project/item/footage/footagedescription.h | 10 +- app/project/project.cpp | 2 - app/project/projectviewmodel.cpp | 12 +- app/project/projectviewmodel.h | 5 + app/render/job/footagejob.h | 8 +- app/render/previewautocacher.cpp | 16 +- app/render/previewautocacher.h | 8 +- app/render/rendermanager.cpp | 6 +- app/render/renderprocessor.cpp | 14 +- app/task/export/export.cpp | 9 +- app/task/precache/precachetask.cpp | 6 +- app/task/project/import/import.cpp | 2 +- app/task/project/loadotio/loadotio.cpp | 8 +- .../nodeparamviewarraywidget.cpp | 5 +- .../nodeparamview/nodeparamviewarraywidget.h | 2 +- .../nodeparamview/nodeparamviewitem.cpp | 4 +- app/widget/nodeparamview/nodeparamviewitem.h | 2 +- .../projectexplorer/projectexplorer.cpp | 2 + app/widget/projectexplorer/projectexplorer.h | 2 + app/widget/timebased/timebasedwidget.cpp | 8 +- app/widget/timelinewidget/timelinewidget.cpp | 12 +- app/widget/timelinewidget/timelinewidget.h | 4 +- app/widget/timelinewidget/tool/import.cpp | 63 +-- app/widget/timelinewidget/tool/import.h | 8 +- .../timelinewidget/view/timelineview.cpp | 17 - app/widget/timelinewidget/view/timelineview.h | 1 - .../view/timelineviewghostitem.h | 2 +- app/widget/viewer/footageviewer.cpp | 12 +- app/widget/viewer/viewer.cpp | 83 ++-- app/widget/viewer/viewer.h | 6 +- app/widget/viewer/viewerdisplay.cpp | 30 +- app/widget/viewer/viewerdisplay.h | 12 + app/window/mainwindow/mainwindow.cpp | 12 +- 58 files changed, 783 insertions(+), 719 deletions(-) diff --git a/app/codec/decoder.h b/app/codec/decoder.h index 477206a2c..a3ec48499 100644 --- a/app/codec/decoder.h +++ b/app/codec/decoder.h @@ -37,7 +37,6 @@ extern "C" { #include "common/rational.h" #include "project/item/footage/footage.h" #include "project/item/footage/footagedescription.h" -#include "project/item/footage/stream.h" namespace olive { diff --git a/app/codec/ffmpeg/ffmpegdecoder.cpp b/app/codec/ffmpeg/ffmpegdecoder.cpp index e71edf118..a9cbd25c9 100644 --- a/app/codec/ffmpeg/ffmpegdecoder.cpp +++ b/app/codec/ffmpeg/ffmpegdecoder.cpp @@ -100,7 +100,7 @@ bool FFmpegDecoder::OpenInternal() int64_t ts; // If it's an image sequence, we'll probably need to transform the filename - if (stream().GetStream().video_type() == Stream::kVideoTypeImageSequence) { + if (stream().GetStream().video_type() == Track::kVideoTypeImageSequence) { ts = stream().GetTimeInTimebaseUnits(timecode); img_filename = TransformImageSequenceFileName(stream().filename(), ts); diff --git a/app/dialog/export/export.cpp b/app/dialog/export/export.cpp index fa1f8772c..642678855 100644 --- a/app/dialog/export/export.cpp +++ b/app/dialog/export/export.cpp @@ -188,18 +188,21 @@ ExportDialog::ExportDialog(ViewerOutput *viewer_node, QWidget *parent) : &ExportDialog::FormatChanged); FormatChanged(ExportFormat::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_->frame_rate_combobox()->SetFrameRate(viewer_node_->video_params().time_base().flipped()); - video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(viewer_node_->video_params().pixel_aspect_ratio()); - video_tab_->pixel_format_field()->SetPixelFormat(static_cast(Config::Current()["OnlinePixelFormat"].toInt())); - video_tab_->interlaced_combobox()->SetInterlaceMode(viewer_node_->video_params().interlacing()); - audio_tab_->sample_rate_combobox()->SetSampleRate(viewer_node_->audio_params().sample_rate()); - audio_tab_->channel_layout_combobox()->SetChannelLayout(viewer_node_->audio_params().channel_layout()); + VideoParams vp = viewer_node_->GetVideoParams(); + AudioParams ap = viewer_node_->GetAudioParams(); - video_aspect_ratio_ = static_cast(viewer_node_->video_params().width()) / static_cast(viewer_node_->video_params().height()); + video_tab_->width_slider()->SetValue(vp.width()); + video_tab_->width_slider()->SetDefaultValue(vp.width()); + video_tab_->height_slider()->SetValue(vp.height()); + video_tab_->height_slider()->SetDefaultValue(vp.height()); + video_tab_->frame_rate_combobox()->SetFrameRate(vp.time_base().flipped()); + video_tab_->pixel_aspect_combobox()->SetPixelAspectRatio(vp.pixel_aspect_ratio()); + video_tab_->pixel_format_field()->SetPixelFormat(static_cast(Config::Current()["OnlinePixelFormat"].toInt())); + video_tab_->interlaced_combobox()->SetInterlaceMode(vp.interlacing()); + audio_tab_->sample_rate_combobox()->SetSampleRate(ap.sample_rate()); + audio_tab_->channel_layout_combobox()->SetChannelLayout(ap.channel_layout()); + + video_aspect_ratio_ = static_cast(vp.width()) / static_cast(vp.height()); connect(video_tab_->width_slider(), &IntegerSlider::ValueChanged, @@ -498,10 +501,12 @@ void ExportDialog::UpdateViewerDimensions() preview_viewer_->SetViewerResolution(static_cast(video_tab_->width_slider()->GetValue()), static_cast(video_tab_->height_slider()->GetValue())); + VideoParams vp = viewer_node_->GetVideoParams(); + QMatrix4x4 transform = ExportParams::GenerateMatrix( static_cast(video_tab_->scaling_method_combobox()->currentData().toInt()), - viewer_node_->video_params().width(), - viewer_node_->video_params().height(), + vp.width(), + vp.height(), static_cast(video_tab_->width_slider()->GetValue()), static_cast(video_tab_->height_slider()->GetValue()) ); diff --git a/app/dialog/sequence/sequence.cpp b/app/dialog/sequence/sequence.cpp index 08705e7b9..8b5a36163 100644 --- a/app/dialog/sequence/sequence.cpp +++ b/app/dialog/sequence/sequence.cpp @@ -128,8 +128,8 @@ void SequenceDialog::accept() } else { // Set sequence values directly with no undo command - sequence_->set_video_params(video_params); - sequence_->set_audio_params(audio_params); + sequence_->SetVideoParams(video_params); + sequence_->SetAudioParams(audio_params); sequence_->SetLabel(name_field_->text()); } @@ -144,8 +144,8 @@ SequenceDialog::SequenceParamCommand::SequenceParamCommand(Sequence* s, new_video_params_(video_params), new_audio_params_(audio_params), new_name_(name), - old_video_params_(s->video_params()), - old_audio_params_(s->audio_params()), + old_video_params_(s->GetVideoParams()), + old_audio_params_(s->GetAudioParams()), old_name_(s->GetLabel()) { } @@ -157,15 +157,15 @@ Project *SequenceDialog::SequenceParamCommand::GetRelevantProject() const void SequenceDialog::SequenceParamCommand::redo() { - sequence_->set_video_params(new_video_params_); - sequence_->set_audio_params(new_audio_params_); + sequence_->SetVideoParams(new_video_params_); + sequence_->SetAudioParams(new_audio_params_); sequence_->SetLabel(new_name_); } void SequenceDialog::SequenceParamCommand::undo() { - sequence_->set_video_params(old_video_params_); - sequence_->set_audio_params(old_audio_params_); + sequence_->SetVideoParams(old_video_params_); + sequence_->SetAudioParams(old_audio_params_); sequence_->SetLabel(old_name_); } diff --git a/app/dialog/sequence/sequencedialogparametertab.cpp b/app/dialog/sequence/sequencedialogparametertab.cpp index 96bbd2dd4..2617cae4c 100644 --- a/app/dialog/sequence/sequencedialogparametertab.cpp +++ b/app/dialog/sequence/sequencedialogparametertab.cpp @@ -59,11 +59,13 @@ SequenceDialogParameterTab::SequenceDialogParameterTab(Sequence* sequence, QWidg layout->addWidget(preview_group); // Set values based on input sequence - video_section_->SetVideoParams(sequence->video_params()); - preview_resolution_field_->SetDivider(sequence->video_params().divider()); - preview_format_field_->SetPixelFormat(sequence->video_params().format()); - audio_sample_rate_field_->SetSampleRate(sequence->audio_params().sample_rate()); - audio_channels_field_->SetChannelLayout(sequence->audio_params().channel_layout()); + VideoParams vp = sequence->GetVideoParams(); + AudioParams ap = sequence->GetAudioParams(); + video_section_->SetVideoParams(vp); + preview_resolution_field_->SetDivider(vp.divider()); + preview_format_field_->SetPixelFormat(vp.format()); + audio_sample_rate_field_->SetSampleRate(ap.sample_rate()); + audio_channels_field_->SetChannelLayout(ap.channel_layout()); connect(preview_resolution_field_, static_cast(&QComboBox::currentIndexChanged), this, &SequenceDialogParameterTab::UpdatePreviewResolutionLabel); diff --git a/app/node/node.cpp b/app/node/node.cpp index dcd5704d1..be9f7035b 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -1346,8 +1346,9 @@ void Node::ArrayResizeInternal(const QString &id, int size) // equal subinputs_.size() } + int old_sz = imm->array_size; imm->array_size = size; - emit InputArraySizeChanged(id, size); + emit InputArraySizeChanged(id, old_sz, size); ParameterValueChanged(id, -1, TimeRange(RATIONAL_MIN, RATIONAL_MAX)); } } @@ -1842,6 +1843,8 @@ void Node::ParameterValueChanged(const QString& input, int element, const TimeRa void Node::LoadImmediate(QXmlStreamReader *reader, const QString& input, int element, XMLNodeData &xml_node_data, const QAtomicInt *cancelled) { + Q_UNUSED(xml_node_data) + NodeValue::Type data_type = GetInputDataType(input); while (XMLReadNextStartElement(reader)) { diff --git a/app/node/node.h b/app/node/node.h index 14990f9c6..4e957c57b 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -876,7 +876,7 @@ signals: void LinksChanged(); - void InputArraySizeChanged(const QString& input, int new_size); + void InputArraySizeChanged(const QString& input, int old_size, int new_size); void KeyframeAdded(NodeKeyframe* key); diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index d6bf3a3f1..091925162 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -673,4 +673,23 @@ uint qHash(const Track::Reference &r, uint seed) seed); } +QDataStream &operator<<(QDataStream &out, const Track::Reference &ref) +{ + out << static_cast(ref.type()) << ref.index(); + + return out; +} + +QDataStream &operator>>(QDataStream &in, Track::Reference &ref) +{ + int type; + int index; + + in >> type >> index; + + ref = Track::Reference(static_cast(type), index); + + return in; +} + } diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 8eb1f5ce4..28af8b627 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -134,6 +134,63 @@ public: return !(*this == ref); } + bool operator<(const Track::Reference& rhs) const + { + if (type_ != rhs.type_) { + return type_ < rhs.type_; + } + + return index_ < rhs.index_; + } + + QString ToString() const + { + QString type_string; + + if (type_ == Track::kVideo) { + type_string = QStringLiteral("v"); + } else if (type_ == Track::kAudio) { + type_string = QStringLiteral("a"); + } else { + return QString(); + } + + return QStringLiteral("%1:%2").arg(type_string, QString::number(index_)); + } + + static Type TypeFromString(const QString& s) + { + if (s.at(1) == ':') { + if (s.at(0) == 'v') { + // Video stream + return Track::kVideo; + } else if (s.at(0) == 'a') { + // Audio stream + return Track::kAudio; + } + } + + return Track::kNone; + } + + static Reference FromString(const QString& s) + { + Reference ref; + Type parse_type = TypeFromString(s); + + if (parse_type != Track::kNone) { + bool ok; + int parse_index = s.mid(2).toInt(&ok); + + if (ok) { + ref.type_ = parse_type; + ref.index_ = parse_index; + } + } + + return ref; + } + private: Track::Type type_; @@ -380,6 +437,10 @@ private slots: uint qHash(const Track::Reference& r, uint seed = 0); +QDataStream &operator<<(QDataStream &out, const Track::Reference &ref); + +QDataStream &operator>>(QDataStream &in, Track::Reference &ref); + } #endif // TRACK_H diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index c27a9ebb4..48b64eb48 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -36,18 +36,25 @@ const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight #define super Node -ViewerOutput::ViewerOutput() : +ViewerOutput::ViewerOutput(bool create_default_streams) : video_frame_cache_(this), audio_playback_cache_(this), operation_stack_(0) { - AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray)); SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(kVideoParamEditMask)); - AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); + AddInput(kAudioParamsInput, NodeValue::kAudioParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray)); + + connect(this, &Node::InputArraySizeChanged, this, &ViewerOutput::InputResized); AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable)); AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable)); + + if (create_default_streams) { + AddStream(Track::kVideo, QVariant()); + AddStream(Track::kAudio, QVariant()); + } } ViewerOutput::~ViewerOutput() @@ -85,16 +92,116 @@ QString ViewerOutput::Description() const QString ViewerOutput::duration() const { - rational timeline_length = GetLength(); + /*rational timeline_length = GetLength(); - int64_t timestamp = Timecode::time_to_timestamp(timeline_length, video_params().time_base()); + rational timebase = GetVideoParams().time_base(); - return Timecode::timestamp_to_timecode(timestamp, video_params().time_base(), Core::instance()->GetTimecodeDisplay()); + int64_t timestamp = Timecode::time_to_timestamp(timeline_length, timebase); + + return Timecode::timestamp_to_timecode(timestamp, timebase, Core::instance()->GetTimecodeDisplay());*/ + // Try video first + VideoParams video = GetFirstEnabledVideoStream(); + + if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) { + int64_t duration = video.duration(); + + rational frame_rate_timebase = video.frame_rate().flipped(); + if (frame_rate_timebase.isNull()) { + frame_rate_timebase = video.time_base(); + } + + if (video.time_base() != frame_rate_timebase) { + // Convert from timebase to frame rate + duration = Timecode::rescale_timestamp_ceil(duration, video.time_base(), frame_rate_timebase); + } + + return Timecode::timestamp_to_timecode(duration, + frame_rate_timebase, + Core::instance()->GetTimecodeDisplay()); + } + + // Try audio second + AudioParams audio = GetFirstEnabledAudioStream(); + + if (audio.is_valid()) { + // If we're showing in a timecode, we prefer showing audio in seconds instead + Timecode::Display display = Core::instance()->GetTimecodeDisplay(); + if (display == Timecode::kTimecodeDropFrame + || display == Timecode::kTimecodeNonDropFrame) { + display = Timecode::kTimecodeSeconds; + } + + return Timecode::timestamp_to_timecode(audio.duration(), + audio.time_base(), + display); + } + + // Otherwise, return nothing + return QString(); } QString ViewerOutput::rate() const { - return tr("%1 FPS").arg(video_params().time_base().flipped().toDouble()); + if (HasEnabledVideoStreams()) { + // This is a video editor, prioritize video streams + VideoParams video_stream = GetFirstEnabledVideoStream(); + + if (video_stream.video_type() != VideoParams::kVideoTypeStill) { + rational using_tb = video_stream.frame_rate(); + + if (using_tb.isNull()) { + using_tb = video_stream.time_base().flipped(); + } + + return tr("%1 FPS").arg(using_tb.toDouble()); + } + } else if (HasEnabledAudioStreams()) { + // No video streams, return audio + AudioParams audio_stream = GetFirstEnabledAudioStream(); + return tr("%1 Hz").arg(audio_stream.sample_rate()); + } + + return QString(); +} + +bool ViewerOutput::HasEnabledVideoStreams() const +{ + return GetFirstEnabledVideoStream().is_valid(); +} + +bool ViewerOutput::HasEnabledAudioStreams() const +{ + return GetFirstEnabledAudioStream().is_valid(); +} + +VideoParams ViewerOutput::GetFirstEnabledVideoStream() const +{ + int sz = GetVideoStreamCount(); + + for (int i=0; i(), - static_cast(Config::Current()["OfflinePixelFormat"].toInt()), - VideoParams::kInternalChannelCount, - Config::Current()["DefaultSequencePixelAspect"].value(), - Config::Current()["DefaultSequenceInterlacing"].value(), - VideoParams::generate_auto_divider(width, height))); - set_audio_params(AudioParams(Config::Current()["DefaultSequenceAudioFrequency"].toInt(), - Config::Current()["DefaultSequenceAudioLayout"].toULongLong(), - AudioParams::kInternalFormat)); + SetVideoParams(VideoParams( + width, + height, + Config::Current()["DefaultSequenceFrameRate"].value(), + static_cast(Config::Current()["OfflinePixelFormat"].toInt()), + VideoParams::kInternalChannelCount, + Config::Current()["DefaultSequencePixelAspect"].value(), + Config::Current()["DefaultSequenceInterlacing"].value(), + VideoParams::generate_auto_divider(width, height) + )); + SetAudioParams(AudioParams( + Config::Current()["DefaultSequenceAudioFrequency"].toInt(), + Config::Current()["DefaultSequenceAudioLayout"].toULongLong(), + AudioParams::kInternalFormat + )); } void ViewerOutput::ShiftVideoCache(const rational &from, const rational &to) @@ -165,6 +276,33 @@ const rational& ViewerOutput::GetLength() const return last_length_; } +QVector ViewerOutput::GetEnabledStreamsAsReferences() const +{ + QVector refs; + + { + int vp_sz = GetVideoStreamCount(); + + for (int i=0; i footage) +void ViewerOutput::set_parameters_from_footage(const QVector footage) { - foreach (Footage* f, footage) { + foreach (ViewerOutput* f, footage) { QVector video_streams = f->GetEnabledVideoStreams(); QVector audio_streams = f->GetEnabledAudioStreams(); @@ -327,20 +477,20 @@ void ViewerOutput::set_parameters_from_footage(const QVector footage) // If this is a still image, we'll use it's resolution but won't set // `found_video_params` in case something with a frame rate comes along which we'll // prioritize - using_timebase = video_params().time_base(); + using_timebase = GetVideoParams().time_base(); } else { using_timebase = s.frame_rate().flipped(); found_video_params = true; } - set_video_params(VideoParams(s.width(), + SetVideoParams(VideoParams(s.width(), s.height(), using_timebase, static_cast(Config::Current()[QStringLiteral("OfflinePixelFormat")].toInt()), - VideoParams::kInternalChannelCount, - s.pixel_aspect_ratio(), - s.interlacing(), - VideoParams::generate_auto_divider(s.width(), s.height()))); + VideoParams::kInternalChannelCount, + s.pixel_aspect_ratio(), + s.interlacing(), + VideoParams::generate_auto_divider(s.width(), s.height()))); if (found_video_params) { break; @@ -349,7 +499,7 @@ void ViewerOutput::set_parameters_from_footage(const QVector footage) if (!audio_streams.isEmpty()) { const AudioParams& s = audio_streams.first(); - set_audio_params(AudioParams(s.sample_rate(), s.channel_layout(), AudioParams::kInternalFormat)); + SetAudioParams(AudioParams(s.sample_rate(), s.channel_layout(), AudioParams::kInternalFormat)); } } } @@ -372,4 +522,76 @@ void ViewerOutput::SaveCustom(QXmlStreamWriter *writer) const writer->writeEndElement(); // points } +int ViewerOutput::AddStream(Track::Type type, const QVariant& value) +{ + QString id; + + if (type == Track::kVideo) { + id = kVideoParamsInput; + } else if (type == Track::kAudio) { + id = kAudioParamsInput; + } else { + return -1; + } + + // Add another video/audio param to the array for this stream + int index = InputArraySize(id); + InputArrayAppend(id); + + SetStandardValue(id, value, index); + + return index; +} + +void ViewerOutput::InputResized(const QString &input, int old_size, int new_size) +{ + if (input == kVideoParamsInput || input == kAudioParamsInput) { + Track::Type type = (input == kVideoParamsInput) ? Track::kVideo : Track::kAudio; + + if (new_size > old_size) { + for (int i=old_size; i ViewerOutput::GetEnabledVideoStreams() const +{ + QVector streams; + + int vp_sz = GetVideoStreamCount(); + + for (int i=0; i ViewerOutput::GetEnabledAudioStreams() const +{ + QVector streams; + + int ap_sz = GetAudioStreamCount(); + + for (int i=0; i footage); + void set_parameters_from_footage(const QVector footage); void ShiftVideoCache(const rational& from, const rational& to); void ShiftAudioCache(const rational& from, const rational& to); @@ -67,26 +67,47 @@ public: virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override; - VideoParams video_params() const + VideoParams GetVideoParams(int index = 0) const { - return GetStandardValue(kVideoParamsInput).value(); + return GetStandardValue(kVideoParamsInput, index).value(); } - AudioParams audio_params() const + AudioParams GetAudioParams(int index = 0) const { - return GetStandardValue(kAudioParamsInput).value(); + return GetStandardValue(kAudioParamsInput, index).value(); } - void set_video_params(const VideoParams &video) + void SetVideoParams(const VideoParams &video, int index = 0) { - SetStandardValue(kVideoParamsInput, QVariant::fromValue(video)); + SetStandardValue(kVideoParamsInput, QVariant::fromValue(video), index); } - void set_audio_params(const AudioParams &audio) + void SetAudioParams(const AudioParams &audio, int index = 0) { - SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio)); + SetStandardValue(kAudioParamsInput, QVariant::fromValue(audio), index); } + int GetVideoStreamCount() const + { + return InputArraySize(kVideoParamsInput); + } + + int GetAudioStreamCount() const + { + return InputArraySize(kAudioParamsInput); + } + + int GetTotalStreamCount() const + { + return GetVideoStreamCount() + GetAudioStreamCount(); + } + + bool HasEnabledVideoStreams() const; + bool HasEnabledAudioStreams() const; + + VideoParams GetFirstEnabledVideoStream() const; + AudioParams GetFirstEnabledAudioStream() const; + const rational &GetLength() const; FrameHashCache* video_frame_cache() @@ -104,12 +125,22 @@ public: return &timeline_points_; } + QVector GetEnabledStreamsAsReferences() const; + + QVector GetEnabledVideoStreams() const; + + QVector GetEnabledAudioStreams() const; + virtual void Retranslate() override; virtual void BeginOperation() override; virtual void EndOperation() override; + virtual NodeOutput GetConnectedTextureOutput(); + + virtual NodeOutput GetConnectedSampleOutput(); + static const QString kVideoParamsInput; static const QString kAudioParamsInput; @@ -154,6 +185,8 @@ protected: virtual void SaveCustom(QXmlStreamWriter *writer) const override; + int AddStream(Track::Type type, const QVariant &value); + private: rational last_length_; @@ -167,6 +200,9 @@ private: TimelinePoints timeline_points_; +private slots: + void InputResized(const QString& input, int old_size, int new_size); + }; } diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index f184dde30..8bf1cf3f1 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -234,7 +234,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N // Assume this is a VideoStream, we did a type check earlier in the function FootageJob job = v.data().value(); - if (job.type() == Stream::kVideo) { + if (job.type() == Track::kVideo) { QVariant value = ProcessVideoFootage(job, range.in()); if (!value.isNull()) { @@ -267,7 +267,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N // Assume this is an AudioStream, we did a type check earlier in the function FootageJob job = v.data().value(); - if (job.type() == Stream::kAudio) { + if (job.type() == Track::kAudio) { QVariant value = ProcessAudioFootage(job, range); if (!value.isNull()) { diff --git a/app/node/value.cpp b/app/node/value.cpp index 48a864473..6ab6cd5cf 100644 --- a/app/node/value.cpp +++ b/app/node/value.cpp @@ -27,7 +27,6 @@ #include #include "common/tohex.h" -#include "project/item/footage/stream.h" #include "render/audioparams.h" #include "render/videoparams.h" #include "render/color.h" diff --git a/app/panel/footageviewer/footageviewer.cpp b/app/panel/footageviewer/footageviewer.cpp index 0030ff89a..b598e5af8 100644 --- a/app/panel/footageviewer/footageviewer.cpp +++ b/app/panel/footageviewer/footageviewer.cpp @@ -35,41 +35,22 @@ FootageViewerPanel::FootageViewerPanel(QWidget *parent) : // Set strings Retranslate(); + + // Show and raise on connect + SetShowAndRaiseOnConnect(); } -QVector FootageViewerPanel::GetSelectedFootage() const +QVector FootageViewerPanel::GetSelectedFootage() const { - QVector list; - Footage* f = static_cast(GetTimeBasedWidget())->GetFootage(); + QVector list; - if (f) { - list.append(f); + if (GetConnectedViewer()) { + list.append(GetConnectedViewer()); } return list; } -void FootageViewerPanel::SetFootage(Footage *f) -{ - if (f && !f->IsValid()) { - // Do nothing if footage is invalid - return; - } - - static_cast(GetTimeBasedWidget())->SetFootage(f); - - if (f) { - // SetSubtitle() will call Retranslate(), so we don't need to call it here - SetSubtitle(f->GetLabel()); - - // Pop this panel up so the user doesn't think nothing's happening if it's behind another tab - this->show(); - this->raise(); - } else { - Retranslate(); - } -} - void FootageViewerPanel::Retranslate() { ViewerPanelBase::Retranslate(); diff --git a/app/panel/footageviewer/footageviewer.h b/app/panel/footageviewer/footageviewer.h index 5338c1718..c910da4e7 100644 --- a/app/panel/footageviewer/footageviewer.h +++ b/app/panel/footageviewer/footageviewer.h @@ -36,9 +36,7 @@ class FootageViewerPanel : public ViewerPanelBase, public FootageManagementPanel public: FootageViewerPanel(QWidget* parent); - virtual QVector GetSelectedFootage() const override; - - void SetFootage(Footage* f); + virtual QVector GetSelectedFootage() const override; protected: virtual void Retranslate() override; diff --git a/app/panel/project/footagemanagementpanel.h b/app/panel/project/footagemanagementpanel.h index dba6e9be5..423fdc674 100644 --- a/app/panel/project/footagemanagementpanel.h +++ b/app/panel/project/footagemanagementpanel.h @@ -29,7 +29,7 @@ namespace olive { class FootageManagementPanel { public: - virtual QVector GetSelectedFootage() const = 0; + virtual QVector GetSelectedFootage() const = 0; }; } diff --git a/app/panel/project/project.cpp b/app/panel/project/project.cpp index 4d42cc6ec..56b9b44ae 100644 --- a/app/panel/project/project.cpp +++ b/app/panel/project/project.cpp @@ -57,6 +57,7 @@ ProjectPanel::ProjectPanel(QWidget *parent) : explorer_ = new ProjectExplorer(this); layout->addWidget(explorer_); connect(explorer_, &ProjectExplorer::DoubleClickedItem, this, &ProjectPanel::ItemDoubleClickSlot); + connect(explorer_, &ProjectExplorer::ItemRemoved, this, &ProjectPanel::ItemRemoved); // Set toolbar's view to the explorer's view toolbar->SetView(explorer_->view_type()); @@ -186,7 +187,7 @@ void ProjectPanel::ItemDoubleClickSlot(Node *item) Core::instance()->DialogImportShow(); } else if (dynamic_cast(item)) { // Open this footage in a FootageViewer - PanelManager::instance()->MostRecentlyFocused()->SetFootage(static_cast(item)); + PanelManager::instance()->MostRecentlyFocused()->ConnectViewerNode(static_cast(item)); } else if (dynamic_cast(item)) { // Open this sequence in the Timeline Core::instance()->main_window()->OpenSequence(static_cast(item)); @@ -232,14 +233,24 @@ void ProjectPanel::SaveConnectedProject() Core::instance()->SaveProject(this->project()); } -QVector ProjectPanel::GetSelectedFootage() const +void ProjectPanel::ItemRemoved(Node *item) +{ + // Open this footage in a FootageViewer + FootageViewerPanel* panel = PanelManager::instance()->MostRecentlyFocused(); + + if (panel->GetConnectedViewer() == item) { + panel->DisconnectViewerNode(); + } +} + +QVector ProjectPanel::GetSelectedFootage() const { QVector items = SelectedItems(); - QVector footage; + QVector footage; foreach (Node* i, items) { - if (dynamic_cast(i)) { - footage.append(static_cast(i)); + if (dynamic_cast(i)) { + footage.append(static_cast(i)); } } diff --git a/app/panel/project/project.h b/app/panel/project/project.h index 69ae6daf4..18a109e0e 100644 --- a/app/panel/project/project.h +++ b/app/panel/project/project.h @@ -48,7 +48,7 @@ public: Folder* GetSelectedFolder() const; - virtual QVector GetSelectedFootage() const override; + virtual QVector GetSelectedFootage() const override; ProjectViewModel* model() const; @@ -80,6 +80,8 @@ private slots: void SaveConnectedProject(); + void ItemRemoved(Node* item); + }; } diff --git a/app/panel/timebased/timebased.cpp b/app/panel/timebased/timebased.cpp index cdcad93d3..45f6c7836 100644 --- a/app/panel/timebased/timebased.cpp +++ b/app/panel/timebased/timebased.cpp @@ -24,7 +24,8 @@ namespace olive { TimeBasedPanel::TimeBasedPanel(const QString &object_name, QWidget *parent) : PanelWidget(object_name, parent), - widget_(nullptr) + widget_(nullptr), + show_and_raise_on_connect_(false) { } @@ -122,6 +123,11 @@ void TimeBasedPanel::ConnectViewerNode(ViewerOutput *node) if (node) { connect(node, &ViewerOutput::LabelChanged, this, &TimeBasedPanel::SetSubtitle); + + if (show_and_raise_on_connect_) { + this->show(); + this->raise(); + } } // Update strings diff --git a/app/panel/timebased/timebased.h b/app/panel/timebased/timebased.h index bff649439..092efce1e 100644 --- a/app/panel/timebased/timebased.h +++ b/app/panel/timebased/timebased.h @@ -34,7 +34,10 @@ public: void ConnectViewerNode(ViewerOutput *node); - void DisconnectViewerNode(); + void DisconnectViewerNode() + { + ConnectViewerNode(nullptr); + } rational GetTime(); @@ -122,9 +125,16 @@ protected: virtual void Retranslate() override; + void SetShowAndRaiseOnConnect() + { + show_and_raise_on_connect_ = true; + } + private: TimeBasedWidget* widget_; + bool show_and_raise_on_connect_; + }; } diff --git a/app/panel/timeline/timeline.cpp b/app/panel/timeline/timeline.cpp index f2681140b..c0962109f 100644 --- a/app/panel/timeline/timeline.cpp +++ b/app/panel/timeline/timeline.cpp @@ -170,12 +170,12 @@ void TimelinePanel::SetColorLabel(int index) static_cast(GetTimeBasedWidget())->SetColorLabel(index); } -void TimelinePanel::InsertFootageAtPlayhead(const QVector &footage) +void TimelinePanel::InsertFootageAtPlayhead(const QVector &footage) { static_cast(GetTimeBasedWidget())->InsertFootageAtPlayhead(footage); } -void TimelinePanel::OverwriteFootageAtPlayhead(const QVector &footage) +void TimelinePanel::OverwriteFootageAtPlayhead(const QVector &footage) { static_cast(GetTimeBasedWidget())->OverwriteFootageAtPlayhead(footage); } diff --git a/app/panel/timeline/timeline.h b/app/panel/timeline/timeline.h index e3076ed35..6df691018 100644 --- a/app/panel/timeline/timeline.h +++ b/app/panel/timeline/timeline.h @@ -85,9 +85,9 @@ public: virtual void SetColorLabel(int index) override; - void InsertFootageAtPlayhead(const QVector &footage); + void InsertFootageAtPlayhead(const QVector &footage); - void OverwriteFootageAtPlayhead(const QVector &footage); + void OverwriteFootageAtPlayhead(const QVector &footage); protected: virtual void Retranslate() override; diff --git a/app/project/item/footage/CMakeLists.txt b/app/project/item/footage/CMakeLists.txt index 8c3e24a27..3e27bfead 100644 --- a/app/project/item/footage/CMakeLists.txt +++ b/app/project/item/footage/CMakeLists.txt @@ -21,6 +21,5 @@ set(OLIVE_SOURCES project/item/footage/footage.h project/item/footage/footagedescription.cpp project/item/footage/footagedescription.h - project/item/footage/stream.h PARENT_SCOPE ) diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index ab81bf6a1..8c842347f 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -36,11 +36,11 @@ namespace olive { const QString Footage::kFilenameInput = QStringLiteral("file_in"); -const QString Footage::kStreamPropertiesFormat = QStringLiteral("stream_properties:%1"); #define super ViewerOutput Footage::Footage(const QString &filename) : + ViewerOutput(false), cancelled_(nullptr) { AddInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable)); @@ -55,12 +55,6 @@ void Footage::Retranslate() super::Retranslate(); SetInputName(kFilenameInput, tr("Filename")); - - for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) { - StreamReference ref = it.key(); - - SetInputName(it.value(), QStringLiteral("%1 %2").arg(GetStreamTypeName(ref.type()), QString::number(ref.index()))); - } } bool Footage::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled) @@ -82,8 +76,6 @@ void Footage::SaveCustom(QXmlStreamWriter *writer) const void Footage::InputValueChangedEvent(const QString &input, int element) { - Q_UNUSED(element) - if (input == kFilenameInput) { // Reset internal stream cache Clear(); @@ -128,11 +120,11 @@ void Footage::InputValueChangedEvent(const QString &input, int element) decoder_ = footage_info.decoder(); for (int i=0; i Footage::GetEnabledVideoStreams() const -{ - QVector list; - - for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) { - if (it.key().type() == Stream::kVideo) { - VideoParams vp = GetVideoParams(it.key().index()); - - if (vp.enabled()) { - list.append(vp); - } - } - } - - return list; -} - -QVector Footage::GetEnabledAudioStreams() const -{ - QVector list; - - for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) { - if (it.key().type() == Stream::kAudio) { - AudioParams ap = GetAudioParams(it.key().index()); - - if (ap.enabled()) { - list.append(ap); - } - } - } - - return list; -} - -QVector Footage::GetEnabledStreamsAsReferences() const -{ - QVector refs; - - for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) { - refs.append(StreamReference(it.key().type(), it.key().index())); - } - - return refs; -} - void Footage::Clear() { // Clear all dynamically created inputs - foreach (const QString& s, inputs_for_stream_properties_) { - RemoveInput(s); - } - inputs_for_stream_properties_.clear(); - - // Clear all dynamically created outputs - foreach (const QString& s, outputs_for_streams_) { - RemoveOutput(s); - } - outputs_for_streams_.clear(); + InputArrayResize(kVideoParamsInput, 0); + InputArrayResize(kAudioParamsInput, 0); // Clear decoder link decoder_.clear(); @@ -304,47 +204,17 @@ void Footage::set_timestamp(const qint64 &t) timestamp_ = t; } -QString Footage::GetStringFromReference(Stream::Type type, int index) +int Footage::GetStreamIndex(Track::Type type, int index) const { - QString type_string; - - if (type == Stream::kVideo) { - type_string = QStringLiteral("v"); - } else if (type == Stream::kAudio) { - type_string = QStringLiteral("a"); - } else { - return QString(); - } - - return QStringLiteral("%1:%2").arg(type_string, QString::number(index)); -} - -int Footage::GetStreamIndex(Stream::Type type, int index) const -{ - if (type == Stream::kVideo) { + if (type == Track::kVideo) { return GetVideoParams(index).stream_index(); - } else if (type == Stream::kAudio) { + } else if (type == Track::kAudio) { return GetAudioParams(index).stream_index(); } else { return -1; } } -Stream::Type Footage::GetTypeFromOutput(const QString &s) -{ - if (s.at(1) == ':') { - if (s.at(0) == 'v') { - // Video stream - return Stream::kVideo; - } else if (s.at(0) == 'a') { - // Audio stream - return Stream::kAudio; - } - } - - return Stream::kUnknown; -} - const QString &Footage::decoder() const { return decoder_; @@ -352,7 +222,7 @@ const QString &Footage::decoder() const QIcon Footage::icon() const { - if (valid_ && !inputs_for_stream_properties_.isEmpty()) { + if (valid_ && GetTotalStreamCount()) { // Prioritize video > audio > image VideoParams s = GetFirstEnabledVideoStream(); @@ -368,77 +238,6 @@ QIcon Footage::icon() const return icon::Error; } -QString Footage::duration() const -{ - // Try video first - VideoParams video = GetFirstEnabledVideoStream(); - - if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) { - int64_t duration = video.duration(); - rational frame_rate_timebase = video.frame_rate().flipped(); - - if (video.time_base() != frame_rate_timebase) { - // Convert from timebase to frame rate - duration = Timecode::rescale_timestamp_ceil(duration, video.time_base(), frame_rate_timebase); - } - - return Timecode::timestamp_to_timecode(duration, - frame_rate_timebase, - Core::instance()->GetTimecodeDisplay()); - } - - // Try audio second - AudioParams audio = GetFirstEnabledAudioStream(); - - if (audio.is_valid()) { - // If we're showing in a timecode, we prefer showing audio in seconds instead - Timecode::Display display = Core::instance()->GetTimecodeDisplay(); - if (display == Timecode::kTimecodeDropFrame - || display == Timecode::kTimecodeNonDropFrame) { - display = Timecode::kTimecodeSeconds; - } - - return Timecode::timestamp_to_timecode(audio.duration(), - audio.time_base(), - display); - } - - // Otherwise, return nothing - return QString(); -} - -QString Footage::rate() const -{ - if (inputs_for_stream_properties_.isEmpty()) { - return QString(); - } - - if (HasEnabledVideoStreams()) { - // This is a video editor, prioritize video streams - VideoParams video_stream = GetFirstEnabledVideoStream(); - - if (video_stream.video_type() != VideoParams::kVideoTypeStill) { - return tr("%1 FPS").arg(video_stream.frame_rate().toDouble()); - } - } else if (HasEnabledAudioStreams()) { - // No video streams, return audio - AudioParams audio_stream = GetFirstEnabledAudioStream(); - return tr("%1 Hz").arg(audio_stream.sample_rate()); - } - - return QString(); -} - -bool Footage::HasEnabledVideoStreams() const -{ - return GetFirstEnabledVideoStream().is_valid(); -} - -bool Footage::HasEnabledAudioStreams() const -{ - return GetFirstEnabledAudioStream().is_valid(); -} - QString Footage::DescribeVideoStream(const VideoParams ¶ms) { if (params.video_type() == VideoParams::kVideoTypeStill) { @@ -500,7 +299,7 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration super::Hash(output, hash, time); // Translate output ID to stream - StreamReference ref = GetReferenceFromOutput(output); + Track::Reference ref = Track::Reference::FromString(output); QString fn = filename(); @@ -519,7 +318,7 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration // Footage stream hash.addData(QString::number(ref.index()).toUtf8()); - if (ref.type() == Stream::kVideo) { + if (ref.type() == Track::kVideo) { // Current color config and space hash.addData(project()->color_manager()->GetConfigFilename().toUtf8()); hash.addData(GetColorspaceToUse(params).toUtf8()); @@ -547,7 +346,7 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration NodeValueTable Footage::Value(const QString &output, NodeValueDatabase &value) const { - StreamReference ref = GetReferenceFromOutput(output); + Track::Reference ref = Track::Reference::FromString(output); // Pop filename from table QString file = value[kFilenameInput].Take(NodeValue::kFile).toString(); @@ -561,7 +360,7 @@ NodeValueTable Footage::Value(const QString &output, NodeValueDatabase &value) c rational length; - if (ref.type() == Stream::kVideo) { + if (ref.type() == Track::kVideo) { VideoParams vp = GetVideoParams(ref.index()); // Ensure the colorspace is valid and not empty @@ -584,66 +383,85 @@ NodeValueTable Footage::Value(const QString &output, NodeValueDatabase &value) c return table; } -QString Footage::GetStreamTypeName(Stream::Type type) +QString Footage::GetStreamTypeName(Track::Type type) { switch (type) { - case Stream::kVideo: + case Track::kVideo: return tr("Video"); - case Stream::kAudio: + case Track::kAudio: return tr("Audio"); - case Stream::kSubtitle: + case Track::kSubtitle: return tr("Subtitle"); - case Stream::kData: - return tr("Data"); - case Stream::kAttachment: - return tr("Attachment"); - case Stream::kUnknown: + case Track::kNone: + case Track::kCount: break; } return tr("Unknown"); } +NodeOutput Footage::GetConnectedTextureOutput() +{ + QString output = Track::Reference(Track::kVideo, 0).ToString(); + if (HasOutputWithID(output)) { + return NodeOutput(this, output); + } else { + return NodeOutput(); + } +} + +NodeOutput Footage::GetConnectedSampleOutput() +{ + QString output = Track::Reference(Track::kAudio, 0).ToString(); + if (HasOutputWithID(output)) { + return NodeOutput(this, output); + } else { + return NodeOutput(); + } +} + void Footage::UpdateTooltip() { if (valid_) { QString tip = tr("Filename: %1").arg(filename()); - for (auto it=inputs_for_stream_properties_.cbegin(); it!=inputs_for_stream_properties_.cend(); it++) { - if (it.key().type() == Stream::kVideo) { - VideoParams p = GetVideoParams(it.key().index()); + int vp_sz = GetVideoStreamCount(); + for (int i=0; i(); value_type = NodeValue::kVideoParams; @@ -680,10 +498,10 @@ void Footage::AddStreamAsInput(Stream::Type type, int index, QVariant value) inputs_for_stream_properties_.insert(ref, input_id); // Create output for stream - QString output_id = GetStringFromReference(type, index); + QString output_id = Track::Reference(type, index).ToString(); AddOutput(output_id); outputs_for_streams_.insert(ref, output_id); -} +}*/ void Footage::CheckFootage() { @@ -702,7 +520,7 @@ void Footage::CheckFootage() } } -/*QString Footage::StreamReference::video_colorspace(bool default_if_empty) const +/*QString Track::Reference::video_colorspace(bool default_if_empty) const { if (IsValid()) { VideoParams params = footage_->GetVideoParams(index_); @@ -719,28 +537,4 @@ void Footage::CheckFootage() return QString(); }*/ -uint qHash(const Footage::StreamReference &ref, uint seed) -{ - return qHash(ref.type(), seed) ^ qHash(ref.index(), seed); -} - -QDataStream &operator<<(QDataStream &out, const Footage::StreamReference &ref) -{ - out << static_cast(ref.type()) << ref.index(); - - return out; -} - -QDataStream &operator>>(QDataStream &in, Footage::StreamReference &ref) -{ - int type; - int index; - - in >> type >> index; - - ref = Footage::StreamReference(static_cast(type), index); - - return in; -} - } diff --git a/app/project/item/footage/footage.h b/app/project/item/footage/footage.h index 88b61dd91..c4accdeaf 100644 --- a/app/project/item/footage/footage.h +++ b/app/project/item/footage/footage.h @@ -29,7 +29,6 @@ #include "node/output/viewer/viewer.h" #include "render/audioparams.h" #include "render/videoparams.h" -#include "stream.h" #include "timeline/timelinepoints.h" namespace olive { @@ -140,61 +139,13 @@ public: cancelled_ = c; } - static QString GetStringFromReference(Stream::Type type, int index); - static QString GetStringFromReference(const StreamReference& ref) - { - return GetStringFromReference(ref.type(), ref.index()); - } - - int GetStreamIndex(Stream::Type type, int index) const; - int GetStreamIndex(const StreamReference& ref) const + int GetStreamIndex(Track::Type type, int index) const; + int GetStreamIndex(const Track::Reference& ref) const { return GetStreamIndex(ref.type(), ref.index()); } - int GetTotalStreamCount() const - { - return inputs_for_stream_properties_.size(); - } - - StreamReference GetReferenceFromRealIndex(int real_index) const; - - static Stream::Type GetTypeFromOutput(const QString& output); - - StreamReference GetReferenceFromOutput(const QString& s) const; - static bool GetReferenceFromOutput(const QString& s, Stream::Type* type, int* index); - - VideoParams GetVideoParams(int index) const - { - return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kVideo, index))).value(); - } - - void SetVideoParams(int index, const VideoParams& p) - { - SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kVideo, index)), QVariant::fromValue(p)); - } - - VideoParams GetFirstEnabledVideoStream() const; - - AudioParams GetAudioParams(int index) const - { - return GetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index))).value(); - } - - void SetAudioParams(int index, const AudioParams& p) - { - SetStandardValue(inputs_for_stream_properties_.value(StreamReference(Stream::kAudio, index)), QVariant::fromValue(p)); - } - - AudioParams GetFirstEnabledAudioStream() const; - - QVector GetEnabledVideoStreams() const; - - QVector GetEnabledAudioStreams() const; - - Stream::Type GetStreamType(int index); - - QVector GetEnabledStreamsAsReferences() const; + Track::Reference GetReferenceFromRealIndex(int real_index) const; /** * @brief Get the Decoder ID set when this Footage was probed @@ -207,18 +158,11 @@ public: virtual QIcon icon() const override; - virtual QString duration() const override; - - virtual QString rate() const override; - virtual bool IsItem() const override { return true; } - bool HasEnabledVideoStreams() const; - bool HasEnabledAudioStreams() const; - static QString DescribeVideoStream(const VideoParams& params); static QString DescribeAudioStream(const AudioParams& params); @@ -229,10 +173,13 @@ public: virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const override; - static QString GetStreamTypeName(Stream::Type type); + static QString GetStreamTypeName(Track::Type type); + + virtual NodeOutput GetConnectedTextureOutput() override; + + virtual NodeOutput GetConnectedSampleOutput() override; static const QString kFilenameInput; - static const QString kStreamPropertiesFormat; protected: /** @@ -247,6 +194,8 @@ protected: virtual void InputValueChangedEvent(const QString &input, int element) override; + virtual rational GetCustomLength(Track::Type type) const override; + private: QString GetColorspaceToUse(const VideoParams& params) const; @@ -266,23 +215,6 @@ private: */ void UpdateTooltip(); - void AddStreamAsInput(Stream::Type type, int index, QVariant value); - - static QString GetInputIDOfIndex(Stream::Type type, int index) - { - return kStreamPropertiesFormat.arg(GetStringFromReference(type, index)); - } - - /** - * @brief List of dynamic inputs added for stream properties - */ - QMap inputs_for_stream_properties_; - - /** - * @brief List of dynamic outputs added for streams - */ - QMap outputs_for_streams_; - /** * @brief Internal timestamp object */ @@ -302,14 +234,6 @@ private slots: }; -uint qHash(const Footage::StreamReference& ref, uint seed = 0); - -QDataStream &operator<<(QDataStream &out, const Footage::StreamReference &ref); - -QDataStream &operator>>(QDataStream &in, Footage::StreamReference &ref); - } -Q_DECLARE_METATYPE(olive::Footage::StreamReference) - #endif // FOOTAGE_H diff --git a/app/project/item/footage/footagedescription.h b/app/project/item/footage/footagedescription.h index 4e0c50b26..f23e47ad6 100644 --- a/app/project/item/footage/footagedescription.h +++ b/app/project/item/footage/footagedescription.h @@ -21,9 +21,9 @@ #ifndef FOOTAGEDESCRIPTION_H #define FOOTAGEDESCRIPTION_H +#include "node/output/track/track.h" #include "render/audioparams.h" #include "render/videoparams.h" -#include "stream.h" namespace olive { @@ -59,14 +59,14 @@ public: audio_streams_.append(audio_params); } - Stream::Type GetTypeOfStream(int index) + Track::Type GetTypeOfStream(int index) { if (StreamIsVideo(index)) { - return Stream::kVideo; + return Track::kVideo; } else if (StreamIsAudio(index)) { - return Stream::kAudio; + return Track::kAudio; } else { - return Stream::kUnknown; + return Track::kNone; } } diff --git a/app/project/project.cpp b/app/project/project.cpp index 7730bdcfc..d16c78d94 100644 --- a/app/project/project.cpp +++ b/app/project/project.cpp @@ -295,8 +295,6 @@ void Project::ColorManagerValueChanged(const NodeInput &input, const TimeRange & foreach (Footage* item, footage) { item->InvalidateAll(QString()); - //static_cast(s)->ColorConfigChanged(); - //static_cast(s)->DefaultColorSpaceChanged(); } } diff --git a/app/project/projectviewmodel.cpp b/app/project/projectviewmodel.cpp index 1e2d79f8c..8c247aa23 100644 --- a/app/project/projectviewmodel.cpp +++ b/app/project/projectviewmodel.cpp @@ -269,10 +269,10 @@ QMimeData *ProjectViewModel::mimeData(const QModelIndexList &indexes) const // Check if we've dragged this item before if (!dragged_items.contains(index.internalPointer())) { // If not, add it to the stream (and also keep track of it in the vector) - Footage* footage = dynamic_cast(static_cast(index.internalPointer())); + ViewerOutput* footage = dynamic_cast(static_cast(index.internalPointer())); if (footage) { - QVector streams = footage->GetEnabledStreamsAsReferences(); + QVector streams = footage->GetEnabledStreamsAsReferences(); stream << streams << reinterpret_cast(footage); @@ -319,7 +319,7 @@ bool ProjectViewModel::dropMimeData(const QMimeData *data, Qt::DropAction action // Variables to deserialize into quintptr item_ptr; - QList streams; + QList streams; // Loop through all data MultiUndoCommand* move_command = new MultiUndoCommand(); @@ -430,6 +430,9 @@ void ProjectViewModel::ConnectItem(Node *n) connect(f, &Folder::BeginRemoveItem, this, &ProjectViewModel::FolderBeginRemoveItem); connect(f, &Folder::EndRemoveItem, this, &ProjectViewModel::FolderEndRemoveItem); + connect(f, &Folder::BeginInsertItem, this, &ProjectViewModel::ItemAdded); + connect(f, &Folder::BeginRemoveItem, this, &ProjectViewModel::ItemRemoved); + foreach (Node* c, f->children()) { ConnectItem(c); } @@ -447,6 +450,9 @@ void ProjectViewModel::DisconnectItem(Node *n) disconnect(f, &Folder::BeginRemoveItem, this, &ProjectViewModel::FolderBeginRemoveItem); disconnect(f, &Folder::EndRemoveItem, this, &ProjectViewModel::FolderEndRemoveItem); + disconnect(f, &Folder::BeginInsertItem, this, &ProjectViewModel::ItemAdded); + disconnect(f, &Folder::BeginRemoveItem, this, &ProjectViewModel::ItemRemoved); + foreach (Node* c, f->children()) { ConnectItem(c); } diff --git a/app/project/projectviewmodel.h b/app/project/projectviewmodel.h index 9a57c80da..1e396746a 100644 --- a/app/project/projectviewmodel.h +++ b/app/project/projectviewmodel.h @@ -104,6 +104,11 @@ public: */ QModelIndex CreateIndexFromItem(Node *item, int column = 0); +signals: + void ItemAdded(Node* node); + + void ItemRemoved(Node* node); + private: /** * @brief Retrieve the index of `item` in its parent diff --git a/app/render/job/footagejob.h b/app/render/job/footagejob.h index 72dbd54a6..5fede41cf 100644 --- a/app/render/job/footagejob.h +++ b/app/render/job/footagejob.h @@ -29,11 +29,11 @@ class FootageJob { public: FootageJob() : - type_(Stream::kUnknown) + type_(Track::kNone) { } - FootageJob(const QString& decoder, const QString& filename, Stream::Type type) : + FootageJob(const QString& decoder, const QString& filename, Track::Type type) : decoder_(decoder), filename_(filename), type_(type) @@ -50,7 +50,7 @@ public: return filename_; } - Stream::Type type() const + Track::Type type() const { return type_; } @@ -90,7 +90,7 @@ private: QString filename_; - Stream::Type type_; + Track::Type type_; VideoParams video_params_; diff --git a/app/render/previewautocacher.cpp b/app/render/previewautocacher.cpp index 5933ddadb..1a255bfc2 100644 --- a/app/render/previewautocacher.cpp +++ b/app/render/previewautocacher.cpp @@ -16,8 +16,7 @@ PreviewAutoCacher::PreviewAutoCacher() : use_custom_range_(false), single_frame_render_(nullptr), last_update_time_(0), - ignore_next_mouse_button_(false), - color_manager_(nullptr) + ignore_next_mouse_button_(false) { // Set default autocache range SetPlayhead(rational()); @@ -70,7 +69,7 @@ void PreviewAutoCacher::GenerateHashes(ViewerOutput *viewer, FrameHashCache* cac foreach (const rational& time, times) { // See if hash already exists in disk cache - QByteArray hash = RenderManager::Hash(viewer->GetConnectedNode(ViewerOutput::kTextureInput), viewer->video_params(), time); + QByteArray hash = RenderManager::Hash(viewer->GetConnectedNode(ViewerOutput::kTextureInput), viewer->GetVideoParams(), time); // Check memory list since disk checking is slow bool hash_exists = (std::find(existing_hashes.begin(), existing_hashes.end(), hash) != existing_hashes.end()); @@ -164,7 +163,7 @@ void PreviewAutoCacher::AudioRendered() watcher->GetTicket()->GetJobTime()); if (!valid_ranges.isEmpty()) { // Generate visual waveform in this background thread - track->waveform().set_channel_count(viewer_node_->audio_params().channel_count()); + track->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count()); foreach (const TimeRange& r, valid_ranges) { track->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length()); @@ -510,7 +509,7 @@ void PreviewAutoCacher::TryRender() single_frame_render_->Start(); watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, - color_manager_, + copied_color_manager_, single_frame_render_->property("time").value(), RenderMode::kOffline, viewer_node_->video_frame_cache(), @@ -556,7 +555,7 @@ void PreviewAutoCacher::RequeueFrames() connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::VideoRendered); video_tasks_.insert(watcher, hash); watcher->SetTicket(RenderManager::instance()->RenderFrame(copied_viewer_node_, - color_manager_, + copied_color_manager_, t, RenderMode::kOffline, viewer_node_->video_frame_cache(), @@ -662,10 +661,11 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node) // Find copied viewer node copied_viewer_node_ = static_cast(copy_map_.value(viewer_node_)); + copied_color_manager_ = static_cast(copy_map_.value(viewer_node_->project()->color_manager())); // Copy parameters - copied_viewer_node_->set_video_params(viewer_node_->video_params()); - copied_viewer_node_->set_audio_params(viewer_node_->audio_params()); + copied_viewer_node_->SetVideoParams(viewer_node_->GetVideoParams()); + copied_viewer_node_->SetAudioParams(viewer_node_->GetAudioParams()); // Add all connections foreach (Node* node, graph->nodes()) { diff --git a/app/render/previewautocacher.h b/app/render/previewautocacher.h index f07fd8c17..e4e01abdd 100644 --- a/app/render/previewautocacher.h +++ b/app/render/previewautocacher.h @@ -79,11 +79,6 @@ public: void ClearAudioQueue(bool wait = false); void ClearVideoDownloadQueue(bool wait = false); - void SetColorManager(ColorManager* manager) - { - color_manager_ = manager; - } - private: static void GenerateHashes(ViewerOutput *viewer, FrameHashCache *cache, const QVector& times, qint64 job_time); @@ -132,6 +127,7 @@ private: QVector graph_update_queue_; QHash copy_map_; ViewerOutput* copied_viewer_node_; + ColorManager* copied_color_manager_; QVector created_nodes_; bool paused_; @@ -160,8 +156,6 @@ private: bool ignore_next_mouse_button_; - ColorManager* color_manager_; - QTimer delayed_requeue_timer_; private slots: diff --git a/app/render/rendermanager.cpp b/app/render/rendermanager.cpp index 0ebf4c528..73449d9ea 100644 --- a/app/render/rendermanager.cpp +++ b/app/render/rendermanager.cpp @@ -107,8 +107,8 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c color_manager, time, mode, - viewer->video_params(), - viewer->audio_params(), + viewer->GetVideoParams(), + viewer->GetAudioParams(), QSize(0, 0), QMatrix4x4(), VideoParams::kFormatInvalid, @@ -158,7 +158,7 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange& r, bool generate_waveforms, bool prioritize) { - return RenderAudio(viewer, r, viewer->audio_params(), generate_waveforms, prioritize); + return RenderAudio(viewer, r, viewer->GetAudioParams(), generate_waveforms, prioritize); } RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange &r, const AudioParams ¶ms, bool generate_waveforms, bool prioritize) diff --git a/app/render/renderprocessor.cpp b/app/render/renderprocessor.cpp index 0490ff211..877d993c9 100644 --- a/app/render/renderprocessor.cpp +++ b/app/render/renderprocessor.cpp @@ -58,8 +58,12 @@ void RenderProcessor::Run() const VideoParams& video_params = ticket_->property("vparam").value(); rational time = ticket_->property("time").value(); - NodeValueTable table = ProcessInput(viewer, ViewerOutput::kTextureInput, - TimeRange(time, time + video_params.time_base())); + NodeValueTable table; + NodeOutput texture_output = viewer->GetConnectedTextureOutput(); + if (texture_output.IsValid()) { + table = GenerateTable(texture_output.node(), texture_output.output(), + TimeRange(time, time + video_params.time_base())); + } TexturePtr texture = table.Get(NodeValue::kTexture).value(); @@ -133,7 +137,11 @@ void RenderProcessor::Run() ViewerOutput* viewer = Node::ValueToPtr(ticket_->property("viewer")); TimeRange time = ticket_->property("time").value(); - NodeValueTable table = ProcessInput(viewer, ViewerOutput::kSamplesInput, time); + NodeValueTable table; + NodeOutput texture_output = viewer->GetConnectedSampleOutput(); + if (texture_output.IsValid()) { + table = GenerateTable(texture_output.node(), texture_output.output(), time); + } ticket_->Finish(table.Get(NodeValue::kSamples), IsCancelled()); break; diff --git a/app/task/export/export.cpp b/app/task/export/export.cpp index 41564a3d8..bc69e8a7c 100644 --- a/app/task/export/export.cpp +++ b/app/task/export/export.cpp @@ -76,14 +76,15 @@ bool ExportTask::Run() if (params_.video_enabled()) { // If a transformation matrix is applied to this video, create it here - if (viewer()->video_params().width() != params_.video_params().width() + VideoParams vp = viewer()->GetVideoParams(); + if (vp.width() != params_.video_params().width() || params_.video_params().height() != params_.video_params().height()) { video_force_size = QSize(params_.video_params().width(), params_.video_params().height()); if (params_.video_scaling_method() != ExportParams::kStretch) { video_force_matrix = ExportParams::GenerateMatrix(params_.video_scaling_method(), - viewer()->video_params().width(), - viewer()->video_params().height(), + vp.width(), + vp.height(), params_.video_params().width(), params_.video_params().height()); } @@ -162,7 +163,7 @@ void ExportTask::FrameDownloaded(FramePtr f, const QByteArray &hash, const QVect forever { rational real_time = Timecode::timestamp_to_time(frame_time_, - viewer()->video_params().time_base()); + viewer()->GetVideoParams().time_base()); if (!time_map_.contains(real_time)) { break; diff --git a/app/task/precache/precachetask.cpp b/app/task/precache/precachetask.cpp index 0a70efac7..e732603fd 100644 --- a/app/task/precache/precachetask.cpp +++ b/app/task/precache/precachetask.cpp @@ -25,10 +25,10 @@ namespace olive { PreCacheTask::PreCacheTask(Footage *footage, int index, Sequence* sequence) : - RenderTask(new ViewerOutput(), sequence->video_params(), sequence->audio_params()) + RenderTask(new ViewerOutput(), sequence->GetVideoParams(), sequence->GetAudioParams()) { - viewer()->set_video_params(sequence->video_params()); - viewer()->set_audio_params(sequence->audio_params()); + viewer()->SetVideoParams(sequence->GetVideoParams()); + viewer()->SetAudioParams(sequence->GetAudioParams()); // FIXME: I've been lazy and haven't included support for anything connected to a footage input. // At the moment, footage nodes have no connectable inputs so it's not a problem, but if diff --git a/app/task/project/import/import.cpp b/app/task/project/import/import.cpp index 28c852e61..773a9420a 100644 --- a/app/task/project/import/import.cpp +++ b/app/task/project/import/import.cpp @@ -214,7 +214,7 @@ void ProjectImportTask::ValidateImageSequence(Footage *footage, QFileInfoList& i video_stream.set_start_time(start_index); video_stream.set_duration(end_index - start_index + 1); - footage->SetVideoParams(0, video_stream); + footage->SetVideoParams(video_stream, 0); } } diff --git a/app/task/project/loadotio/loadotio.cpp b/app/task/project/loadotio/loadotio.cpp index 01ebd496e..f791969c7 100644 --- a/app/task/project/loadotio/loadotio.cpp +++ b/app/task/project/loadotio/loadotio.cpp @@ -169,15 +169,15 @@ bool LoadOTIOTask::Run() probed_item->setParent(project_->root()); } - Footage::StreamReference reference; + Track::Reference reference; if (track->type() == Track::kVideo) { - reference = Footage::StreamReference(Stream::kVideo, 0); + reference = Track::Reference(Track::kVideo, 0); } else { - reference = Footage::StreamReference(Stream::kAudio, 0); + reference = Track::Reference(Track::kAudio, 0); } - QString output_id = probed_item->GetStringFromReference(reference); + QString output_id = reference.ToString(); Node::ConnectEdge(NodeOutput(probed_item, output_id), NodeInput(block, ClipBlock::kBufferIn)); } diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp index 87a925405..45514ed91 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.cpp +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.cpp @@ -39,7 +39,7 @@ NodeParamViewArrayWidget::NodeParamViewArrayWidget(Node *node, const QString &in connect(node_, &Node::InputArraySizeChanged, this, &NodeParamViewArrayWidget::UpdateCounter); - UpdateCounter(input_, node_->InputArraySize(input_)); + UpdateCounter(input_, 0, node_->InputArraySize(input_)); } void NodeParamViewArrayWidget::mouseDoubleClickEvent(QMouseEvent *event) @@ -49,8 +49,9 @@ void NodeParamViewArrayWidget::mouseDoubleClickEvent(QMouseEvent *event) emit DoubleClicked(); } -void NodeParamViewArrayWidget::UpdateCounter(const QString& input, int new_size) +void NodeParamViewArrayWidget::UpdateCounter(const QString& input, int old_size, int new_size) { + Q_UNUSED(old_size) if (input == input_) { count_lbl_->setText(tr("%1 element(s)").arg(new_size)); } diff --git a/app/widget/nodeparamview/nodeparamviewarraywidget.h b/app/widget/nodeparamview/nodeparamviewarraywidget.h index a61f02235..5a7105674 100644 --- a/app/widget/nodeparamview/nodeparamviewarraywidget.h +++ b/app/widget/nodeparamview/nodeparamviewarraywidget.h @@ -70,7 +70,7 @@ private: QLabel* count_lbl_; private slots: - void UpdateCounter(const QString &input, int new_size); + void UpdateCounter(const QString &input, int old_size, int new_size); }; diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 306da4269..6c21cbdd1 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -418,8 +418,10 @@ void NodeParamViewItemBody::ArrayCollapseBtnPressed(bool checked) emit ArrayExpandedChanged(checked); } -void NodeParamViewItemBody::InputArraySizeChanged(const QString& input, int size) +void NodeParamViewItemBody::InputArraySizeChanged(const QString& input, int old_sz, int size) { + Q_UNUSED(old_sz) + Node* node = static_cast(sender()); ArrayUI& array_ui = array_ui_[{node, input}]; diff --git a/app/widget/nodeparamview/nodeparamviewitem.h b/app/widget/nodeparamview/nodeparamviewitem.h index 2d35a9ff8..2a72134ef 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.h +++ b/app/widget/nodeparamview/nodeparamviewitem.h @@ -137,7 +137,7 @@ private slots: void ArrayCollapseBtnPressed(bool checked); - void InputArraySizeChanged(const QString &input, int size); + void InputArraySizeChanged(const QString &input, int old_sz, int size); void ArrayAppendClicked(); diff --git a/app/widget/projectexplorer/projectexplorer.cpp b/app/widget/projectexplorer/projectexplorer.cpp index 51dc0e11f..c5f23b965 100644 --- a/app/widget/projectexplorer/projectexplorer.cpp +++ b/app/widget/projectexplorer/projectexplorer.cpp @@ -95,6 +95,8 @@ ProjectExplorer::ProjectExplorer(QWidget *parent) : connect(tree_view_, &ProjectExplorerTreeView::customContextMenuRequested, this, &ProjectExplorer::ShowContextMenu); connect(list_view_, &ProjectExplorerListView::customContextMenuRequested, this, &ProjectExplorer::ShowContextMenu); connect(icon_view_, &ProjectExplorerIconView::customContextMenuRequested, this, &ProjectExplorer::ShowContextMenu); + + connect(&model_, &ProjectViewModel::ItemRemoved, this, &ProjectExplorer::ItemRemoved); } const ProjectToolbar::ViewType &ProjectExplorer::view_type() const diff --git a/app/widget/projectexplorer/projectexplorer.h b/app/widget/projectexplorer/projectexplorer.h index 9c70ac07b..37dd78bde 100644 --- a/app/widget/projectexplorer/projectexplorer.h +++ b/app/widget/projectexplorer/projectexplorer.h @@ -100,6 +100,8 @@ signals: */ void DoubleClickedItem(Node* item); + void ItemRemoved(Node* node); + private: /** * @brief Get all the blocks that solely rely on an input node diff --git a/app/widget/timebased/timebasedwidget.cpp b/app/widget/timebased/timebasedwidget.cpp index 6a18af37d..64e584e52 100644 --- a/app/widget/timebased/timebasedwidget.cpp +++ b/app/widget/timebased/timebasedwidget.cpp @@ -103,10 +103,10 @@ void TimeBasedWidget::ConnectViewerNode(ViewerOutput *node) scrollbar_->ConnectTimelinePoints(viewer_node_->GetTimelinePoints()); if (auto_set_timebase_) { - if (!viewer_node_->video_params().time_base().isNull()) { - SetTimebase(viewer_node_->video_params().time_base()); - } else if (viewer_node_->audio_params().sample_rate() > 0) { - SetTimebase(viewer_node_->audio_params().time_base()); + if (!viewer_node_->GetVideoParams().time_base().isNull()) { + SetTimebase(viewer_node_->GetVideoParams().time_base()); + } else if (viewer_node_->GetAudioParams().sample_rate() > 0) { + SetTimebase(viewer_node_->GetAudioParams().time_base()); } else { SetTimebase(rational()); } diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 3f92d3be3..277034544 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -228,7 +228,7 @@ void TimelineWidget::ConnectNodeInternal(ViewerOutput *n) ruler()->SetPlaybackCache(n->video_frame_cache()); - SetTimebase(n->video_params().time_base()); + SetTimebase(n->GetVideoParams().time_base()); for (int i=0;i(i); @@ -522,12 +522,12 @@ void TimelineWidget::DecreaseTrackHeight() } } -void TimelineWidget::InsertFootageAtPlayhead(const QVector& footage) +void TimelineWidget::InsertFootageAtPlayhead(const QVector& footage) { import_tool_->PlaceAt(footage, GetTime(), true); } -void TimelineWidget::OverwriteFootageAtPlayhead(const QVector &footage) +void TimelineWidget::OverwriteFootageAtPlayhead(const QVector &footage) { import_tool_->PlaceAt(footage, GetTime(), false); } @@ -1005,7 +1005,7 @@ void TimelineWidget::SetViewTimestamp(const int64_t &ts) if (use_audio_time_units_ && i == Track::kAudio) { view->view()->SetTime(Timecode::rescale_timestamp(ts, timebase(), - GetConnectedNode()->audio_params().time_base())); + GetConnectedNode()->GetAudioParams().time_base())); } else { view->view()->SetTime(ts); } @@ -1016,7 +1016,7 @@ void TimelineWidget::ViewTimestampChanged(int64_t ts) { if (use_audio_time_units_ && sender() == views_.at(Track::kAudio)) { ts = Timecode::rescale_timestamp(ts, - GetConnectedNode()->audio_params().time_base(), + GetConnectedNode()->GetAudioParams().time_base(), timebase()); } @@ -1050,7 +1050,7 @@ void TimelineWidget::UpdateViewTimebases() TimelineAndTrackView* view = views_.at(i); if (use_audio_time_units_ && i == Track::kAudio) { - view->view()->SetTimebase(GetConnectedNode()->audio_params().time_base()); + view->view()->SetTimebase(GetConnectedNode()->GetAudioParams().time_base()); } else { view->view()->SetTimebase(timebase()); } diff --git a/app/widget/timelinewidget/timelinewidget.h b/app/widget/timelinewidget/timelinewidget.h index bcc667830..6aa32bfbc 100644 --- a/app/widget/timelinewidget/timelinewidget.h +++ b/app/widget/timelinewidget/timelinewidget.h @@ -75,9 +75,9 @@ public: void DecreaseTrackHeight(); - void InsertFootageAtPlayhead(const QVector &footage); + void InsertFootageAtPlayhead(const QVector &footage); - void OverwriteFootageAtPlayhead(const QVector &footage); + void OverwriteFootageAtPlayhead(const QVector &footage); void ToggleLinksOnSelected(); diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index 3b4a90f75..a2855ae07 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -40,25 +40,6 @@ namespace olive { -Track::Type TrackTypeFromStreamType(Stream::Type stream_type) -{ - switch (stream_type) { - case Stream::kVideo: - return Track::kVideo; - case Stream::kAudio: - return Track::kAudio; - case Stream::kSubtitle: - // Temporarily disabled until we figure out a better thing to do with this - //return Track::kSubtitle; - case Stream::kUnknown: - case Stream::kData: - case Stream::kAttachment: - break; - } - - return Track::kNone; -} - ImportTool::ImportTool(TimelineWidget *parent) : TimelineTool(parent) { @@ -81,7 +62,7 @@ void ImportTool::DragEnter(TimelineViewMouseEvent *event) // Variables to deserialize into quintptr item_ptr; - QVector enabled_streams; + QVector enabled_streams; // Set drag start position drag_start_ = event->GetCoordinates(); @@ -95,15 +76,11 @@ void ImportTool::DragEnter(TimelineViewMouseEvent *event) Node* item = reinterpret_cast(item_ptr); // Check if Item is Footage - if (dynamic_cast(item)) { - - Footage* f = static_cast(item); - - if (f->IsValid()) { - // If the Item is Footage, we can create a Ghost from it - dragged_footage_.insert(f, enabled_streams); - } + ViewerOutput* f = dynamic_cast(item); + if (f && f->GetTotalStreamCount()) { + // If the Item is Footage, we can create a Ghost from it + dragged_footage_.insert(f, enabled_streams); } } @@ -191,18 +168,18 @@ void ImportTool::DragDrop(TimelineViewMouseEvent *event) } } -void ImportTool::PlaceAt(const QVector &footage, const rational &start, bool insert) +void ImportTool::PlaceAt(const QVector &footage, const rational &start, bool insert) { - QMap > refs; + QMap > refs; - foreach (Footage* f, footage) { + foreach (ViewerOutput* f, footage) { refs.insert(f, f->GetEnabledStreamsAsReferences()); } PlaceAt(refs, start, insert); } -void ImportTool::PlaceAt(const QMap > &footage, const rational &start, bool insert) +void ImportTool::PlaceAt(const QMap > &footage, const rational &start, bool insert) { dragged_footage_ = footage; @@ -214,7 +191,7 @@ void ImportTool::PlaceAt(const QMap DropGhosts(insert); } -void ImportTool::FootageToGhosts(rational ghost_start, const QMap > &sorted, const rational& dest_tb, const int& track_start) +void ImportTool::FootageToGhosts(rational ghost_start, const QMap > &sorted, const rational& dest_tb, const int& track_start) { for (auto it=sorted.cbegin(); it!=sorted.cend(); it++) { // Each stream is offset by one track per track "type", we keep track of them in this vector @@ -225,12 +202,12 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QMapGetVideoParams(ref.index()).video_type() == VideoParams::kVideoTypeStill) { + if (ref.type() == Track::kVideo && it.key()->GetVideoParams(ref.index()).video_type() == VideoParams::kVideoTypeStill) { // Stream is essentially length-less - we may use the default still image length in config, // or we may use another stream's length depending on the circumstance contains_image_stream = true; @@ -245,7 +222,7 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QMapGetVideoParams(ref.index()); dur = vp.duration(); tb = vp.time_base(); @@ -265,7 +242,7 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QMapGetStringFromReference(ref)}; + TimelineViewGhostItem::AttachedFootage af = {it.key(), ref.ToString()}; ghost->SetData(TimelineViewGhostItem::kAttachedFootage, QVariant::fromValue(af)); ghost->SetMode(Timeline::kMove); @@ -365,7 +342,7 @@ void ImportTool::DropGhosts(bool insert) if (behavior == kDWSAuto) { - QVector footage_only; + QVector footage_only; for (auto it=dragged_footage_.cbegin(); it!=dragged_footage_.cend(); it++) { if (!footage_only.contains(it.key())) { @@ -393,7 +370,7 @@ void ImportTool::DropGhosts(bool insert) command->add_child(new FolderAddChild(Core::instance()->GetSelectedFolderInActiveProject(), new_sequence)); new_sequence->add_default_nodes(command); - FootageToGhosts(0, dragged_footage_, new_sequence->video_params().time_base(), 0); + FootageToGhosts(0, dragged_footage_, new_sequence->GetVideoParams().time_base(), 0); sequence = new_sequence; @@ -431,8 +408,8 @@ void ImportTool::DropGhosts(bool insert) command->add_child(new NodeAddCommand(dst_graph, clip)); command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(clip, footage_stream.footage, QPointF(2, 0))); - switch (footage_stream.footage->GetTypeFromOutput(footage_stream.output)) { - case Stream::kVideo: + switch (Track::Reference::TypeFromString(footage_stream.output)) { + case Track::kVideo: { TransformDistortNode* transform = new TransformDistortNode(); command->add_child(new NodeAddCommand(dst_graph, transform)); @@ -442,7 +419,7 @@ void ImportTool::DropGhosts(bool insert) command->add_child(new NodeSetPositionToOffsetOfAnotherNodeCommand(transform, clip, QPointF(-1, 0))); break; } - case Stream::kAudio: + case Track::kAudio: { VolumeNode* volume_node = new VolumeNode(); command->add_child(new NodeAddCommand(dst_graph, volume_node)); diff --git a/app/widget/timelinewidget/tool/import.h b/app/widget/timelinewidget/tool/import.h index 9c15592d0..f89aede89 100644 --- a/app/widget/timelinewidget/tool/import.h +++ b/app/widget/timelinewidget/tool/import.h @@ -35,8 +35,8 @@ public: virtual void DragLeave(QDragLeaveEvent *event) override; virtual void DragDrop(TimelineViewMouseEvent *event) override; - void PlaceAt(const QVector &footage, const rational& start, bool insert); - void PlaceAt(const QMap > &footage, const rational& start, bool insert); + void PlaceAt(const QVector &footage, const rational& start, bool insert); + void PlaceAt(const QMap > &footage, const rational& start, bool insert); enum DropWithoutSequenceBehavior { kDWSAsk, @@ -46,13 +46,13 @@ public: }; private: - void FootageToGhosts(rational ghost_start, const QMap > &footage, const rational &dest_tb, const int &track_start); + void FootageToGhosts(rational ghost_start, const QMap > &footage, const rational &dest_tb, const int &track_start); void PrepGhosts(const rational &frame, const int &track_index); void DropGhosts(bool insert); - QMap > dragged_footage_; + QMap > dragged_footage_; int import_pre_buffer_; diff --git a/app/widget/timelinewidget/view/timelineview.cpp b/app/widget/timelinewidget/view/timelineview.cpp index aa8b7492e..87dbd59c6 100644 --- a/app/widget/timelinewidget/view/timelineview.cpp +++ b/app/widget/timelinewidget/view/timelineview.cpp @@ -344,23 +344,6 @@ Track::Type TimelineView::ConnectedTrackType() return Track::kNone; } -Stream::Type TimelineView::TrackTypeToStreamType(Track::Type track_type) -{ - switch (track_type) { - case Track::kNone: - case Track::kCount: - break; - case Track::kVideo: - return Stream::kVideo; - case Track::kAudio: - return Stream::kAudio; - case Track::kSubtitle: - return Stream::kSubtitle; - } - - return Stream::kUnknown; -} - TimelineCoordinate TimelineView::ScreenToCoordinate(const QPoint& pt) { return SceneToCoordinate(mapToScene(pt)); diff --git a/app/widget/timelinewidget/view/timelineview.h b/app/widget/timelinewidget/view/timelineview.h index 9133a35a4..e9e610fac 100644 --- a/app/widget/timelinewidget/view/timelineview.h +++ b/app/widget/timelinewidget/view/timelineview.h @@ -114,7 +114,6 @@ protected: private: Track::Type ConnectedTrackType(); - Stream::Type TrackTypeToStreamType(Track::Type track_type); TimelineCoordinate ScreenToCoordinate(const QPoint& pt); TimelineCoordinate SceneToCoordinate(const QPointF& pt); diff --git a/app/widget/timelinewidget/view/timelineviewghostitem.h b/app/widget/timelinewidget/view/timelineviewghostitem.h index e65f03464..b84de8243 100644 --- a/app/widget/timelinewidget/view/timelineviewghostitem.h +++ b/app/widget/timelinewidget/view/timelineviewghostitem.h @@ -44,7 +44,7 @@ public: }; struct AttachedFootage { - Footage* footage; + ViewerOutput* footage; QString output; }; diff --git a/app/widget/viewer/footageviewer.cpp b/app/widget/viewer/footageviewer.cpp index 9f96b9680..a1971890e 100644 --- a/app/widget/viewer/footageviewer.cpp +++ b/app/widget/viewer/footageviewer.cpp @@ -42,6 +42,8 @@ FootageViewerWidget::FootageViewerWidget(QWidget *parent) : void FootageViewerWidget::ConnectNodeInternal(ViewerOutput *n) { + super::ConnectNodeInternal(n); + SetTimestamp(cached_timestamps_.value(n, 0)); } @@ -50,6 +52,8 @@ void FootageViewerWidget::DisconnectNodeInternal(ViewerOutput *n) // Cache timestamp in case this footage is opened again later cached_timestamps_.insert(n, GetTimestamp()); SetTimestamp(0); + + super::DisconnectNodeInternal(n); } void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enable_audio) @@ -64,15 +68,15 @@ void FootageViewerWidget::StartFootageDragInternal(bool enable_video, bool enabl QByteArray encoded_data; QDataStream data_stream(&encoded_data, QIODevice::WriteOnly); - QVector streams = GetConnectedNode()->GetEnabledStreamsAsReferences(); + QVector streams = GetConnectedNode()->GetEnabledStreamsAsReferences(); // Disable streams that have been disabled if (!enable_video || !enable_audio) { for (int i=0; ivideo_frame_cache(), &FrameHashCache::Shifted, this, &ViewerWidget::ViewerShiftedRange); connect(n, &ViewerOutput::TextureInputChanged, this, &ViewerWidget::UpdateStack); - InterlacingChangedSlot(n->video_params().interlacing()); + VideoParams vp = n->GetVideoParams(); + + InterlacingChangedSlot(vp.interlacing()); ruler()->SetPlaybackCache(n->video_frame_cache()); - SetViewerResolution(n->video_params().width(), n->video_params().height()); - SetViewerPixelAspect(n->video_params().pixel_aspect_ratio()); + SetViewerResolution(vp.width(), vp.height()); + SetViewerPixelAspect(vp.pixel_aspect_ratio()); last_length_ = rational(); LengthChangedSlot(n->GetLength()); - ColorManager* using_manager; - if (override_color_manager_) { - using_manager = override_color_manager_; - } else if (n->parent()) { - using_manager = n->project()->color_manager(); - } else { - qWarning() << "Failed to find a suitable color manager for the connected viewer node"; - using_manager = nullptr; - } + ColorManager* color_manager = n->project()->color_manager(); - auto_cacher_.SetColorManager(using_manager); - - display_widget_->ConnectColorManager(using_manager); + display_widget_->ConnectColorManager(color_manager); foreach (ViewerWindow* window, windows_) { - window->display_widget()->ConnectColorManager(using_manager); + window->display_widget()->ConnectColorManager(color_manager); } UpdateStack(); @@ -244,7 +235,6 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n) foreach (ViewerWindow* window, windows_) { window->display_widget()->DisconnectColorManager(); } - auto_cacher_.SetColorManager(nullptr); waveform_view_->SetViewer(nullptr); waveform_view_->ConnectTimelinePoints(nullptr); @@ -286,13 +276,6 @@ bool ViewerWidget::IsPlaying() const return playback_speed_ != 0; } -void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_manager) -{ - override_color_manager_ = color_manager; - - super::ConnectViewerNode(node); -} - void ViewerWidget::SetColorMenuEnabled(bool enabled) { color_menu_enabled_ = enabled; @@ -332,7 +315,7 @@ void ViewerWidget::SetFullScreen(QScreen *screen) connect(vw->display_widget(), &ViewerDisplayWidget::customContextMenuRequested, this, &ViewerWidget::ShowContextMenu); if (GetConnectedNode()) { - vw->SetVideoParams(GetConnectedNode()->video_params()); + vw->SetVideoParams(GetConnectedNode()->GetVideoParams()); vw->display_widget()->SetDeinterlacing(vw->display_widget()->IsDeinterlacing()); } @@ -541,26 +524,28 @@ void ViewerWidget::PauseInternal() void ViewerWidget::PushScrubbedAudio() { - if (!IsPlaying() && Config::Current()["AudioScrubbing"].toBool()) { + if (!IsPlaying() && GetConnectedNode() && Config::Current()["AudioScrubbing"].toBool()) { // Get audio src device from renderer - AudioPlaybackCache::PlaybackDevice* audio_src = GetConnectedNode()->audio_playback_cache()->CreatePlaybackDevice(); + const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters(); - if (audio_src->open(QIODevice::ReadOnly)) { - const AudioParams& params = GetConnectedNode()->audio_playback_cache()->GetParameters(); + if (params.is_valid()) { + AudioPlaybackCache::PlaybackDevice* audio_src = GetConnectedNode()->audio_playback_cache()->CreatePlaybackDevice(); - // FIXME: Hardcoded scrubbing interval (20ms) - int size_of_sample = params.time_to_bytes(rational(20, 1000)); + if (audio_src->open(QIODevice::ReadOnly)) { + // FIXME: Hardcoded scrubbing interval (20ms) + int size_of_sample = params.time_to_bytes(rational(20, 1000)); - // Push audio - audio_src->seek(params.time_to_bytes(GetTime())); - QByteArray frame_audio = audio_src->read(size_of_sample); - AudioManager::instance()->SetOutputParams(params); - AudioManager::instance()->PushToOutput(frame_audio); + // Push audio + audio_src->seek(params.time_to_bytes(GetTime())); + QByteArray frame_audio = audio_src->read(size_of_sample); + AudioManager::instance()->SetOutputParams(params); + AudioManager::instance()->PushToOutput(frame_audio); - audio_src->close(); + audio_src->close(); + } + + delete audio_src; } - - delete audio_src; } } @@ -656,10 +641,12 @@ void ViewerWidget::FinishPlayPreprocess() int64_t playback_start_time = ruler()->GetTime(); AudioPlaybackCache* audio_cache = GetConnectedNode()->audio_playback_cache(); - AudioManager::instance()->SetOutputParams(audio_cache->GetParameters()); - AudioManager::instance()->StartOutput(audio_cache, - audio_cache->GetParameters().time_to_bytes(GetTime()), - playback_speed_); + if (audio_cache->GetParameters().is_valid()) { + AudioManager::instance()->SetOutputParams(audio_cache->GetParameters()); + AudioManager::instance()->StartOutput(audio_cache, + audio_cache->GetParameters().time_to_bytes(GetTime()), + playback_speed_); + } playback_timer_.Start(playback_start_time, playback_speed_, timebase_dbl()); display_widget_->ResetFPSTimer(); @@ -869,7 +856,7 @@ void ViewerWidget::ShowContextMenu(const QPoint &pos) { // Deinterlace Option - if (GetConnectedNode()->video_params().interlacing() != VideoParams::kInterlaceNone) { + if (GetConnectedNode()->GetVideoParams().interlacing() != VideoParams::kInterlaceNone) { QAction* deinterlace_action = menu.addAction(tr("Deinterlace")); deinterlace_action->setCheckable(true); deinterlace_action->setChecked(display_widget_->IsDeinterlacing()); @@ -1176,9 +1163,11 @@ void ViewerWidget::InterlacingChangedSlot(VideoParams::Interlacing interlacing) void ViewerWidget::UpdateRendererVideoParameters() { - display_widget_->SetVideoParams(GetConnectedNode()->video_params()); + VideoParams vp = GetConnectedNode()->GetVideoParams(); + + display_widget_->SetVideoParams(vp); foreach (ViewerWindow* window, windows_) { - window->display_widget()->SetVideoParams(GetConnectedNode()->video_params()); + window->display_widget()->SetVideoParams(vp); } } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index e0e448471..5a275ceb3 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -63,8 +63,6 @@ public: bool IsPlaying() const; - void ConnectViewerNode(ViewerOutput *node, ColorManager *color_manager = nullptr); - /** * @brief Enable or disable the color management menu * @@ -155,7 +153,7 @@ protected: virtual void ConnectNodeInternal(ViewerOutput *) override; virtual void DisconnectNodeInternal(ViewerOutput *) override; - virtual void ConnectedNodeChanged(ViewerOutput*n) override; + virtual void ConnectedNodeChanged(ViewerOutput *) override; virtual void ScaleChangedEvent(const double& s) override; @@ -217,8 +215,6 @@ private: bool color_menu_enabled_; - ColorManager* override_color_manager_; - bool time_changed_from_timer_; bool play_in_to_out_only_; diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index d86ec6913..e3da4f81f 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -37,8 +37,10 @@ namespace olive { +#define super ManagedDisplayWidget + ViewerDisplayWidget::ViewerDisplayWidget(QWidget *parent) : - ManagedDisplayWidget(parent), + super(parent), deinterlace_texture_(nullptr), signal_cursor_color_(false), gizmos_(nullptr), @@ -219,7 +221,7 @@ void ViewerDisplayWidget::mousePressEvent(QMouseEvent *event) emit DragStarted(); } - ManagedDisplayWidget::mousePressEvent(event); + super::mousePressEvent(event); } } @@ -246,7 +248,7 @@ void ViewerDisplayWidget::mouseMoveEvent(QMouseEvent *event) } else { // Default behavior - ManagedDisplayWidget::mouseMoveEvent(event); + super::mouseMoveEvent(event); } } @@ -269,11 +271,29 @@ void ViewerDisplayWidget::mouseReleaseEvent(QMouseEvent *event) } else { // Default behavior - ManagedDisplayWidget::mouseReleaseEvent(event); + super::mouseReleaseEvent(event); } } +void ViewerDisplayWidget::dragEnterEvent(QDragEnterEvent *event) +{ + emit DragEntered(); + super::dragEnterEvent(event); +} + +void ViewerDisplayWidget::dragLeaveEvent(QDragLeaveEvent *event) +{ + emit DragLeft(); + super::dragLeaveEvent(event); +} + +void ViewerDisplayWidget::dropEvent(QDropEvent *event) +{ + emit Dropped(); + super::dropEvent(event); +} + void ViewerDisplayWidget::OnPaint() { // Clear background to empty @@ -402,7 +422,7 @@ void ViewerDisplayWidget::OnPaint() void ViewerDisplayWidget::OnDestroy() { - ManagedDisplayWidget::OnDestroy(); + super::OnDestroy(); texture_ = nullptr; } diff --git a/app/widget/viewer/viewerdisplay.h b/app/widget/viewer/viewerdisplay.h index e00af4af9..a765ebabd 100644 --- a/app/widget/viewer/viewerdisplay.h +++ b/app/widget/viewer/viewerdisplay.h @@ -170,6 +170,12 @@ signals: */ void CursorColor(const Color& reference, const Color& display); + void DragEntered(); + + void DragLeft(); + + void Dropped(); + protected: /** * @brief Override the mouse press event for the DragStarted() signal and gizmos @@ -186,6 +192,12 @@ protected: */ virtual void mouseReleaseEvent(QMouseEvent* event) override; + virtual void dragEnterEvent(QDragEnterEvent* event) override; + + virtual void dragLeaveEvent(QDragLeaveEvent* event) override; + + virtual void dropEvent(QDropEvent* event) override; + protected slots: /** * @brief Paint function to display the texture (received in SetTexture()) on screen. diff --git a/app/window/mainwindow/mainwindow.cpp b/app/window/mainwindow/mainwindow.cpp index 67ef4cdf6..ed69336c4 100644 --- a/app/window/mainwindow/mainwindow.cpp +++ b/app/window/mainwindow/mainwindow.cpp @@ -341,15 +341,9 @@ void MainWindow::ProjectClose(Project *p) } // Close any open footage in footage viewer - QVector footage_in_project = p->root()->ListChildrenOfType(); - QVector footage_in_viewer = footage_viewer_panel_->GetSelectedFootage(); - - if (!footage_in_viewer.isEmpty()) { - // FootageViewer only has the one footage item, check if it's in the project in which case - // we'll close it - if (footage_in_project.contains(footage_in_viewer.first())) { - footage_viewer_panel_->SetFootage(nullptr); - } + if (footage_viewer_panel_->GetConnectedViewer() + && footage_viewer_panel_->GetConnectedViewer()->project() == p) { + footage_viewer_panel_->DisconnectViewerNode(); } // Close any extra folder panels