streamlined viewable-based lengths
This commit is contained in:
@@ -187,6 +187,12 @@ public:
|
||||
return input_ids_;
|
||||
}
|
||||
|
||||
virtual QVector<QString> inputs_for_output(const QString& output) const
|
||||
{
|
||||
Q_UNUSED(output)
|
||||
return inputs();
|
||||
}
|
||||
|
||||
const QVector<QString>& outputs() const
|
||||
{
|
||||
return outputs_;
|
||||
|
||||
@@ -191,6 +191,11 @@ public:
|
||||
return ref;
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return type_ > kNone && type_ < kCount && index_ >= 0;
|
||||
}
|
||||
|
||||
private:
|
||||
Track::Type type_;
|
||||
|
||||
|
||||
@@ -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<QString> ViewerOutput::inputs_for_output(const QString &output) const
|
||||
{
|
||||
QVector<QString> 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_;
|
||||
|
||||
@@ -67,6 +67,8 @@ public:
|
||||
|
||||
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element, qint64 job_time) override;
|
||||
|
||||
virtual QVector<QString> inputs_for_output(const QString& output) const override;
|
||||
|
||||
VideoParams GetVideoParams(int index = 0) const
|
||||
{
|
||||
return GetStandardValue(kVideoParamsInput, index).value<VideoParams>();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -57,6 +57,12 @@ void Footage::Retranslate()
|
||||
SetInputName(kFilenameInput, tr("Filename"));
|
||||
}
|
||||
|
||||
QVector<QString> 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")) {
|
||||
|
||||
@@ -76,6 +76,8 @@ public:
|
||||
|
||||
virtual void Retranslate() override;
|
||||
|
||||
virtual QVector<QString> inputs_for_output(const QString &output) const override;
|
||||
|
||||
/**
|
||||
* @brief Reset Footage state ready for running through Probe() again
|
||||
*
|
||||
|
||||
@@ -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; j<item->childCount(); j++) {
|
||||
|
||||
@@ -198,60 +198,21 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QMap<ViewerOutput *
|
||||
QVector<int> track_offsets(Track::kCount);
|
||||
track_offsets.fill(track_start);
|
||||
|
||||
QVector<TimelineViewGhostItem*> 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<rational>();
|
||||
}
|
||||
|
||||
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<rational>();
|
||||
}
|
||||
|
||||
// Snap footage duration to timebase
|
||||
@@ -260,13 +221,27 @@ void ImportTool::FootageToGhosts(rational ghost_start, const QMap<ViewerOutput *
|
||||
footage_duration += snap_mvmt;
|
||||
}
|
||||
|
||||
foreach (TimelineViewGhostItem* ghost, footage_ghosts) {
|
||||
// Create ghosts
|
||||
foreach (const Track::Reference& ref, it.value()) {
|
||||
Track::Type track_type = ref.type();
|
||||
|
||||
TimelineViewGhostItem* ghost = new TimelineViewGhostItem();
|
||||
|
||||
ghost->SetIn(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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user