From eef19ca0ff28976668ba78b35162c058733fdc45 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 8 Apr 2021 09:46:31 +1000 Subject: [PATCH] streamlined viewable-based lengths --- app/node/node.h | 6 ++ app/node/output/track/track.h | 5 ++ app/node/output/viewer/viewer.cpp | 55 ++++++++++------ app/node/output/viewer/viewer.h | 2 + app/node/traverser.cpp | 7 +- app/node/traverser.h | 2 +- app/project/item/footage/footage.cpp | 6 ++ app/project/item/footage/footage.h | 2 + app/widget/nodetableview/nodetableview.cpp | 2 +- app/widget/timelinewidget/tool/import.cpp | 77 ++++++++-------------- app/widget/viewer/viewerdisplay.cpp | 4 +- 11 files changed, 89 insertions(+), 79 deletions(-) diff --git a/app/node/node.h b/app/node/node.h index 4e957c57b..c0e922e32 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -187,6 +187,12 @@ public: return input_ids_; } + virtual QVector inputs_for_output(const QString& output) const + { + Q_UNUSED(output) + return inputs(); + } + const QVector& outputs() const { return outputs_; diff --git a/app/node/output/track/track.h b/app/node/output/track/track.h index 28af8b627..71c8a8841 100644 --- a/app/node/output/track/track.h +++ b/app/node/output/track/track.h @@ -191,6 +191,11 @@ public: return ref; } + bool IsValid() const + { + return type_ > kNone && type_ < kCount && index_ >= 0; + } + private: Track::Type type_; diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index 2dddad416..9a6334bc9 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -92,35 +92,34 @@ QString ViewerOutput::Description() const QString ViewerOutput::duration() const { - // Try video first + rational using_timebase; + Timecode::Display using_display = Core::instance()->GetTimecodeDisplay(); + + // Get first enabled streams VideoParams video = GetFirstEnabledVideoStream(); - - if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) { - rational frame_rate_timebase = video.frame_rate_as_time_base(); - - return Timecode::timestamp_to_timecode(Timecode::rescale_timestamp_ceil(video.duration(), video.time_base(), frame_rate_timebase), - frame_rate_timebase, - Core::instance()->GetTimecodeDisplay()); - } - - // Try audio second AudioParams audio = GetFirstEnabledAudioStream(); - if (audio.is_valid()) { + if (video.is_valid() && video.video_type() != VideoParams::kVideoTypeStill) { + // Prioritize video + using_timebase = video.frame_rate_as_time_base(); + } else if (audio.is_valid()) { + // Use audio as a backup // 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; + if (using_display == Timecode::kTimecodeDropFrame + || using_display == Timecode::kTimecodeNonDropFrame) { + using_display = Timecode::kTimecodeSeconds; } - return Timecode::timestamp_to_timecode(audio.duration(), - audio.time_base(), - display); + using_timebase = audio.sample_rate_as_time_base(); } - // Otherwise, return nothing - return QString(); + if (using_timebase.isNull()) { + // No timebase, return null + return QString(); + } else { + // Return time transformed to timecode + return Timecode::time_to_timecode(GetLength(), using_timebase, using_display); + } } QString ViewerOutput::rate() const @@ -248,6 +247,20 @@ void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, super::InvalidateCache(range, from, element, job_time); } +QVector ViewerOutput::inputs_for_output(const QString &output) const +{ + QVector inputs; + Track::Type type = Track::Reference::TypeFromString(output); + + if (type == Track::kVideo) { + inputs.append(kTextureInput); + } else if (type == Track::kAudio) { + inputs.append(kSamplesInput); + } + + return inputs; +} + const rational& ViewerOutput::GetLength() const { return last_length_; diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 423a32481..66f004bdc 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -67,6 +67,8 @@ public: virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override; + virtual QVector inputs_for_output(const QString& output) const override; + VideoParams GetVideoParams(int index = 0) const { return GetStandardValue(kVideoParamsInput, index).value(); diff --git a/app/node/traverser.cpp b/app/node/traverser.cpp index 8bf1cf3f1..0a5232867 100644 --- a/app/node/traverser.cpp +++ b/app/node/traverser.cpp @@ -25,12 +25,13 @@ namespace olive { -NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRange &range) +NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const QString& output, const TimeRange &range) { NodeValueDatabase database; // We need to insert tables into the database for each input - foreach (const QString& input, node->inputs()) { + auto inputs = node->inputs_for_output(output); + foreach (const QString& input, inputs) { if (IsCancelled()) { return NodeValueDatabase(); } @@ -104,7 +105,7 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const QString& output // FIXME: Cache certain values here if we've already processed them before // Generate database of input values of node - NodeValueDatabase database = GenerateDatabase(n, range); + NodeValueDatabase database = GenerateDatabase(n, output, range); // By this point, the node should have all the inputs it needs to render correctly NodeValueTable table = n->Value(output, database); diff --git a/app/node/traverser.h b/app/node/traverser.h index 462267e86..cb0da8f60 100644 --- a/app/node/traverser.h +++ b/app/node/traverser.h @@ -42,7 +42,7 @@ public: return GenerateTable(output.node(), output.output(), range); } - NodeValueDatabase GenerateDatabase(const Node *node, const TimeRange &range); + NodeValueDatabase GenerateDatabase(const Node *node, const QString &output, const TimeRange &range); protected: NodeValueTable ProcessInput(const Node *node, const QString &input, const TimeRange &range); diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index b26783311..1642d1e73 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -57,6 +57,12 @@ void Footage::Retranslate() SetInputName(kFilenameInput, tr("Filename")); } +QVector Footage::inputs_for_output(const QString &output) const +{ + Q_UNUSED(output) + return {kFilenameInput}; +} + bool Footage::LoadCustom(QXmlStreamReader *reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt* cancelled) { if (reader->name() == QStringLiteral("timestamp")) { diff --git a/app/project/item/footage/footage.h b/app/project/item/footage/footage.h index c4accdeaf..c7cc9ef70 100644 --- a/app/project/item/footage/footage.h +++ b/app/project/item/footage/footage.h @@ -76,6 +76,8 @@ public: virtual void Retranslate() override; + virtual QVector inputs_for_output(const QString &output) const override; + /** * @brief Reset Footage state ready for running through Probe() again * diff --git a/app/widget/nodetableview/nodetableview.cpp b/app/widget/nodetableview/nodetableview.cpp index be3d423aa..bee8ab9f9 100644 --- a/app/widget/nodetableview/nodetableview.cpp +++ b/app/widget/nodetableview/nodetableview.cpp @@ -71,7 +71,7 @@ void NodeTableView::SetTime(const rational &time) QTreeWidgetItem* item = i.value(); // Generate a value database for this node at this time - NodeValueDatabase db = traverser.GenerateDatabase(node, TimeRange(time, time)); + NodeValueDatabase db = traverser.GenerateDatabase(node, QString(), TimeRange(time, time)); // Delete any children of this item that aren't in this database for (int j=0; jchildCount(); j++) { diff --git a/app/widget/timelinewidget/tool/import.cpp b/app/widget/timelinewidget/tool/import.cpp index f40cc488c..31a8be681 100644 --- a/app/widget/timelinewidget/tool/import.cpp +++ b/app/widget/timelinewidget/tool/import.cpp @@ -198,60 +198,21 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QMap track_offsets(Track::kCount); track_offsets.fill(track_start); - QVector footage_ghosts; + ViewerOutput* footage = it.key(); rational footage_duration; - bool contains_image_stream = false; + rational ghost_in; - foreach (const Track::Reference& ref, it.value()) { - Track::Type track_type = ref.type(); + TimelineWorkArea* wk = footage->GetTimelinePoints()->workarea(); + if (wk->enabled()) { + footage_duration = wk->length(); + ghost_in = wk->in(); + } else { + footage_duration = footage->GetLength(); - TimelineViewGhostItem* ghost = new TimelineViewGhostItem(); - - 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; - } else { - // Rescale stream duration to timeline timebase - // Convert to rational time - TimelinePoints* tp = it.key()->GetTimelinePoints(); - if (tp->workarea()->enabled()) { - footage_duration = qMax(footage_duration, tp->workarea()->range().length()); - ghost->SetMediaIn(tp->workarea()->in()); - } else { - int64_t dur; - rational tb; - - if (ref.type() == Track::kVideo) { - VideoParams vp = it.key()->GetVideoParams(ref.index()); - dur = vp.duration(); - tb = vp.time_base(); - } else { - AudioParams ap = it.key()->GetAudioParams(ref.index()); - dur = ap.duration(); - tb = ap.time_base(); - } - - int64_t stream_duration = Timecode::rescale_timestamp_ceil(dur, tb, dest_tb); - footage_duration = qMax(footage_duration, Timecode::timestamp_to_time(stream_duration, dest_tb)); - } + if (footage_duration.isNull()) { + // Fallback to still length if legngth was 0 + footage_duration = Config::Current()[QStringLiteral("DefaultStillLength")].value(); } - - ghost->SetTrack(Track::Reference(track_type, track_offsets.at(track_type))); - - // Increment track count for this track type - track_offsets[track_type]++; - - TimelineViewGhostItem::AttachedFootage af = {it.key(), ref.ToString()}; - ghost->SetData(TimelineViewGhostItem::kAttachedFootage, QVariant::fromValue(af)); - ghost->SetMode(Timeline::kMove); - - footage_ghosts.append(ghost); - } - - if (contains_image_stream && footage_duration.isNull()) { - // Footage must ONLY be image streams so no duration value was found, use default in config - footage_duration = Config::Current()["DefaultStillLength"].value(); } // Snap footage duration to timebase @@ -260,13 +221,27 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QMapSetIn(ghost_start); ghost->SetOut(ghost_start + footage_duration); + ghost->SetMediaIn(ghost_in); + ghost->SetTrack(Track::Reference(track_type, track_offsets.at(track_type))); snap_points_.append(ghost->GetIn()); snap_points_.append(ghost->GetOut()); + // Increment track count for this track type + track_offsets[track_type]++; + + TimelineViewGhostItem::AttachedFootage af = {it.key(), ref.ToString()}; + ghost->SetData(TimelineViewGhostItem::kAttachedFootage, QVariant::fromValue(af)); + ghost->SetMode(Timeline::kMove); + parent()->AddGhost(ghost); } diff --git a/app/widget/viewer/viewerdisplay.cpp b/app/widget/viewer/viewerdisplay.cpp index 24d5a853a..e9374c44e 100644 --- a/app/widget/viewer/viewerdisplay.cpp +++ b/app/widget/viewer/viewerdisplay.cpp @@ -353,8 +353,8 @@ void ViewerDisplayWidget::OnPaint() rational node_time = GetGizmoTime(); - gizmo_db_ = gt.GenerateDatabase(gizmos_, TimeRange(node_time, - node_time + gizmo_params_.frame_rate_as_time_base())); + gizmo_db_ = gt.GenerateDatabase(gizmos_, QString(), + TimeRange(node_time, node_time + gizmo_params_.frame_rate_as_time_base())); QPainter p(inner_widget()); p.setWorldTransform(GenerateGizmoTransform());