Merge branch 'master' into rational_slider

This commit is contained in:
ThomasWilshaw
2021-04-12 10:42:26 +01:00
committed by GitHub
80 changed files with 1266 additions and 491 deletions
+45 -30
View File
@@ -32,6 +32,7 @@ const QString Block::kLengthInput = QStringLiteral("length_in");
const QString Block::kMediaInInput = QStringLiteral("media_in_in");
const QString Block::kEnabledInput = QStringLiteral("enabled_in");
const QString Block::kSpeedInput = QStringLiteral("speed_in");
const QString Block::kReverseInput = QStringLiteral("reverse_in");
Block::Block() :
previous_(nullptr),
@@ -51,10 +52,14 @@ Block::Block() :
AddInput(kEnabledInput, NodeValue::kBoolean, true, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kSpeedInput, NodeValue::kFloat, 1.0);
AddInput(kSpeedInput, NodeValue::kFloat, 1.0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
SetInputProperty(kSpeedInput, QStringLiteral("view"), FloatSlider::kPercentage);
SetInputProperty(kSpeedInput, QStringLiteral("min"), 0.0);
IgnoreHashingFrom(kSpeedInput);
AddInput(kReverseInput, NodeValue::kBoolean, false, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
IgnoreHashingFrom(kReverseInput);
// A block's length must be greater than 0
set_length_and_media_out(1);
}
@@ -77,6 +82,11 @@ void Block::set_length_and_media_out(const rational &length)
return;
}
if (reverse()) {
// Calculate media_in adjustment
set_media_in(SequenceToMediaTime(length - this->length(), true));
}
set_length_internal(length);
}
@@ -88,8 +98,10 @@ void Block::set_length_and_media_in(const rational &length)
return;
}
// Calculate media_in adjustment
set_media_in(SequenceToMediaTime(this->length() - length));
if (!reverse()) {
// Calculate media_in adjustment
set_media_in(SequenceToMediaTime(this->length() - length));
}
// Set the length without setting media out
set_length_internal(length);
@@ -117,7 +129,7 @@ void Block::set_enabled(bool e)
emit EnabledChanged();
}
rational Block::SequenceToMediaTime(const rational &sequence_time) const
rational Block::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse) const
{
// These constants are not considered "values" per se, so we don't modify them
if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) {
@@ -126,22 +138,23 @@ rational Block::SequenceToMediaTime(const rational &sequence_time) const
rational local_time = sequence_time;
// FIXME: Doesn't handle reversing
if (IsInputStatic(kSpeedInput)) {
double speed_value = GetStandardValue(kSpeedInput).toDouble();
double speed_value = speed();
if (qIsNull(speed_value)) {
// Effectively holds the frame at the in point
local_time = 0;
} else if (!qFuzzyCompare(speed_value, 1.0)) {
// Multiply time
local_time = rational::fromDouble(local_time.toDouble() * speed_value);
}
} else {
// FIXME: We'll need to calculate the speed hoo boy
if (qIsNull(speed_value)) {
// Effectively holds the frame at the in point
local_time = 0;
} else if (!qFuzzyCompare(speed_value, 1.0)) {
// Multiply time
local_time = rational::fromDouble(local_time.toDouble() * speed_value);
}
return local_time + media_in();
rational media_time = local_time + media_in();
if (reverse() && !ignore_reverse) {
media_time = length() - media_time;
}
return media_time;
}
rational Block::MediaToSequenceTime(const rational &media_time) const
@@ -151,21 +164,22 @@ rational Block::MediaToSequenceTime(const rational &media_time) const
return media_time;
}
rational sequence_time = media_time - media_in();
rational sequence_time = media_time;
// FIXME: Doesn't handle reversing
if (IsInputKeyframing(kSpeedInput) || IsInputConnected(kSpeedInput)) {
// FIXME: We'll need to calculate the speed hoo boy
} else {
double speed_value = GetStandardValue(kSpeedInput).toDouble();
if (reverse()) {
sequence_time = length() - sequence_time;
}
if (qIsNull(speed_value)) {
// Effectively holds the frame at the in point, also prevents divide by zero
sequence_time = 0;
} else if (!qFuzzyCompare(speed_value, 1.0)) {
// Multiply time
sequence_time = rational::fromDouble(sequence_time.toDouble() / speed_value);
}
sequence_time -= media_in();
double speed_value = speed();
if (qIsNull(speed_value)) {
// Effectively holds the frame at the in point, also prevents divide by zero
sequence_time = 0;
} else if (!qFuzzyCompare(speed_value, 1.0)) {
// Multiply time
sequence_time = rational::fromDouble(sequence_time.toDouble() / speed_value);
}
return sequence_time;
@@ -208,6 +222,7 @@ void Block::Retranslate()
SetInputName(kMediaInInput, tr("Media In"));
SetInputName(kEnabledInput, tr("Enabled"));
SetInputName(kSpeedInput, tr("Speed"));
SetInputName(kReverseInput, tr("Reverse"));
}
void Block::Hash(const QString &, QCryptographicHash &, const rational &) const
+12 -1
View File
@@ -151,12 +151,23 @@ public:
return block_links_;
}
double speed() const
{
return GetStandardValue(kSpeedInput).toDouble();
}
bool reverse() const
{
return GetStandardValue(kReverseInput).toBool();
}
virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override;
static const QString kLengthInput;
static const QString kMediaInInput;
static const QString kEnabledInput;
static const QString kSpeedInput;
static const QString kReverseInput;
public slots:
@@ -166,7 +177,7 @@ signals:
void LengthChanged();
protected:
rational SequenceToMediaTime(const rational& sequence_time) const;
rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false) const;
rational MediaToSequenceTime(const rational& media_time) const;
+5 -2
View File
@@ -114,12 +114,15 @@ void ClipBlock::Retranslate()
SetInputName(kBufferIn, tr("Buffer"));
}
void ClipBlock::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const
void ClipBlock::Hash(const QString &out, QCryptographicHash &hash, const rational &time) const
{
Q_UNUSED(out)
if (IsInputConnected(kBufferIn)) {
rational t = InputTimeAdjustment(kBufferIn, -1, TimeRange(time, time)).in();
GetConnectedNode(kBufferIn)->Hash(output, hash, t);
NodeOutput output = GetConnectedOutput(kBufferIn);
output.node()->Hash(output.output(), hash, t);
}
}
+3
View File
@@ -46,6 +46,7 @@
#include "project/folder/folder.h"
#include "project/footage/footage.h"
#include "project/sequence/sequence.h"
#include "node/input/value/valuenode.h"
namespace olive {
QList<Node*> NodeFactory::library_;
@@ -234,6 +235,8 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new Folder();
case kProjectSequence:
return new Sequence();
case kValueNode:
return new ValueNode();
case kInternalNodeCount:
break;
+1
View File
@@ -56,6 +56,7 @@ public:
kProjectFootage,
kProjectFolder,
kProjectSequence,
kValueNode,
// Count value
kInternalNodeCount
+8 -1
View File
@@ -30,10 +30,17 @@ NodeGraph::NodeGraph()
{
}
NodeGraph::~NodeGraph()
{
Clear();
}
void NodeGraph::Clear()
{
// By deleting the last nodes first, we assume that nodes that are most important are deleted last
// (e.g. Project's ColorManager or ProjectSettingsNode.
while (!node_children_.isEmpty()) {
delete node_children_.first();
delete node_children_.last();
}
}
+5
View File
@@ -40,6 +40,11 @@ public:
*/
NodeGraph();
/**
* @brief NodeGraph Destructor
*/
virtual ~NodeGraph() override;
/**
* @brief Destructively destroys all nodes in the graph
*/
+1
View File
@@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(time)
add_subdirectory(value)
set(OLIVE_SOURCES
${OLIVE_SOURCES}
+6
View File
@@ -0,0 +1,6 @@
#include "multicamnode.h"
MultiCamNode::MultiCamNode()
{
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef MULTICAMNODE_H
#define MULTICAMNODE_H
class MultiCamNode
{
public:
MultiCamNode();
};
#endif // MULTICAMNODE_H
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2020 Olive Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
set(OLIVE_SOURCES
${OLIVE_SOURCES}
node/input/value/valuenode.h
node/input/value/valuenode.cpp
PARENT_SCOPE
)
+81
View File
@@ -0,0 +1,81 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#include "valuenode.h"
namespace olive {
const QString ValueNode::kTypeInput = QStringLiteral("type_in");
const QString ValueNode::kValueInput = QStringLiteral("value_in");
const QVector<NodeValue::Type> ValueNode::kSupportedTypes = {
NodeValue::kFloat,
NodeValue::kInt,
NodeValue::kRational,
NodeValue::kVec2,
NodeValue::kVec3,
NodeValue::kVec4,
NodeValue::kColor,
NodeValue::kText,
NodeValue::kMatrix,
NodeValue::kFont,
};
#define super Node
ValueNode::ValueNode()
{
AddInput(kTypeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
AddInput(kValueInput, kSupportedTypes.first(), QVariant(), InputFlags(kInputFlagNotConnectable));
}
void ValueNode::Retranslate()
{
SetInputName(kTypeInput, QStringLiteral("Type"));
SetInputName(kValueInput, QStringLiteral("Value"));
QStringList type_names;
type_names.reserve(kSupportedTypes.size());
foreach (NodeValue::Type type, kSupportedTypes) {
type_names.append(NodeValue::GetPrettyDataTypeName(type));
}
SetComboBoxStrings(kTypeInput, type_names);
}
NodeValueTable ValueNode::Value(const QString &output, NodeValueDatabase &value) const
{
Q_UNUSED(output)
// Pop combobox value off table because no other node will need it
value[kTypeInput].Take(NodeValue::kCombo);
return value.Merge();
}
void ValueNode::InputValueChangedEvent(const QString &input, int element)
{
if (input == kTypeInput) {
SetInputDataType(kValueInput, kSupportedTypes.at(GetStandardValue(kTypeInput).toInt()));
}
super::InputValueChangedEvent(input, element);
}
}
+78
View File
@@ -0,0 +1,78 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2020 Olive Team
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
***/
#ifndef VALUENODE_H
#define VALUENODE_H
#include "node/node.h"
namespace olive {
class ValueNode : public Node
{
Q_OBJECT
public:
ValueNode();
NODE_DEFAULT_DESTRUCTOR(ValueNode)
virtual Node* copy() const override
{
return new ValueNode();
}
virtual QString Name() const override
{
return tr("Value");
}
virtual QString id() const override
{
return QStringLiteral("org.olivevideoeditor.Olive.value");
}
virtual QVector<CategoryID> Category() const override
{
return {kCategoryInput};
}
virtual QString Description() const override
{
return tr("Create a single value that can be connected to various other inputs.");
}
static const QString kTypeInput;
static const QString kValueInput;
virtual void Retranslate() override;
virtual NodeValueTable Value(const QString &output, NodeValueDatabase &value) const override;
protected:
virtual void InputValueChangedEvent(const QString &input, int element) override;
private:
static const QVector<NodeValue::Type> kSupportedTypes;
};
}
#endif // VALUENODE_H
+18 -5
View File
@@ -36,7 +36,7 @@ bool NodeInputDragger::IsStarted() const
return input_.IsValid();
}
void NodeInputDragger::Start(const NodeKeyframeTrackReference &input, const rational &time)
void NodeInputDragger::Start(const NodeKeyframeTrackReference &input, const rational &time, bool create_key_on_all_tracks)
{
Q_ASSERT(!IsStarted());
@@ -52,9 +52,8 @@ void NodeInputDragger::Start(const NodeKeyframeTrackReference &input, const rati
// Determine whether we are creating a keyframe or not
if (input_.input().IsKeyframing()) {
dragging_key_ = node->GetKeyframeAtTimeOnTrack(input_, time);
drag_created_key_ = !dragging_key_;
if (drag_created_key_) {
if (!dragging_key_) {
dragging_key_ = new NodeKeyframe(time,
start_value_,
node->GetBestKeyframeTypeForTimeOnTrack(input_, time),
@@ -62,6 +61,19 @@ void NodeInputDragger::Start(const NodeKeyframeTrackReference &input, const rati
input_.input().element(),
input_.input().input(),
node);
created_keys_.append(dragging_key_);
if (create_key_on_all_tracks) {
int nb_tracks = NodeValue::get_number_of_keyframe_tracks(input.input().node()->GetInputDataType(input.input().input()));
for (int i=0; i<nb_tracks; i++) {
if (i != input.track()) {
NodeKeyframeTrackReference this_ref(input.input(), i);
created_keys_.append(new NodeKeyframe(time, node->GetSplitValueAtTimeOnTrack(this_ref, time),
node->GetBestKeyframeTypeForTimeOnTrack(this_ref, time),
i, input.input().element(), input.input().input(), node));
}
}
}
}
}
}
@@ -112,9 +124,9 @@ void NodeInputDragger::End()
MultiUndoCommand* command = new MultiUndoCommand();
if (input_.input().node()->IsInputKeyframing(input_.input())) {
if (drag_created_key_) {
for (int i=0; i<created_keys_.size(); i++) {
// We created a keyframe in this process
command->add_child(new NodeParamInsertKeyframeCommand(input_.input().node(), dragging_key_));
command->add_child(new NodeParamInsertKeyframeCommand(input_.input().node(), created_keys_.at(i)));
}
// We just set a keyframe's value
@@ -129,6 +141,7 @@ void NodeInputDragger::End()
Core::instance()->undo_stack()->push(command);
input_.Reset();
created_keys_.clear();
}
}
+2 -3
View File
@@ -34,7 +34,7 @@ public:
bool IsStarted() const;
void Start(const NodeKeyframeTrackReference& input, const rational& time);
void Start(const NodeKeyframeTrackReference& input, const rational& time, bool create_key_on_all_tracks = true);
void Drag(QVariant value);
@@ -50,8 +50,7 @@ private:
QVariant end_value_;
NodeKeyframe* dragging_key_;
bool drag_created_key_;
QVector<NodeKeyframe*> created_keys_;
};
+12 -6
View File
@@ -27,14 +27,10 @@
namespace olive {
NodeInputImmediate::NodeInputImmediate(NodeValue::Type type, const SplitValue &default_val) :
default_value_(default_val),
keyframing_(false)
{
int track_size = NodeValue::get_number_of_keyframe_tracks(type);
keyframe_tracks_.resize(track_size);
standard_value_.resize(track_size);
set_split_standard_value(default_val);
set_data_type(type);
}
void NodeInputImmediate::set_standard_value_on_track(const QVariant &value, int track)
@@ -179,6 +175,16 @@ bool NodeInputImmediate::has_keyframe_at_time(const rational &time) const
return false;
}
void NodeInputImmediate::set_data_type(NodeValue::Type type)
{
int track_size = NodeValue::get_number_of_keyframe_tracks(type);
keyframe_tracks_.resize(track_size);
standard_value_.resize(track_size);
set_split_standard_value(default_value_);
}
NodeKeyframe *NodeInputImmediate::get_earliest_keyframe() const
{
NodeKeyframe* earliest = nullptr;
+7
View File
@@ -151,12 +151,19 @@ public:
return (!is_keyframing() || keyframe_tracks_.at(track).isEmpty());
}
void set_data_type(NodeValue::Type type);
private:
/**
* @brief Non-keyframed value
*/
SplitValue standard_value_;
/**
* @brief Default value
*/
SplitValue default_value_;
/**
* @brief Internal keyframe array
*
+6 -2
View File
@@ -107,13 +107,16 @@ void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rati
// connected node happens to return nothing (a gap for instance). Therefore we only add our
// fingerprint if the base AND the blend change the hash. Otherwise, we assume it's a passthrough.
Q_UNUSED(output)
QByteArray current_result = hash.result();
bool base_changed_hash = false;
bool blend_changed_hash = false;
if (IsInputConnected(kBaseIn)) {
GetConnectedNode(kBaseIn)->Hash(output, hash, time);
NodeOutput base_output = GetConnectedOutput(kBaseIn);
base_output.node()->Hash(base_output.output(), hash, time);
QByteArray post_base_hash = hash.result();
base_changed_hash = (post_base_hash != current_result);
@@ -121,7 +124,8 @@ void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rati
}
if(IsInputConnected(kBlendIn)) {
GetConnectedNode(kBlendIn)->Hash(output, hash, time);
NodeOutput blend_output = GetConnectedOutput(kBlendIn);
blend_output.node()->Hash(blend_output.output(), hash, time);
blend_changed_hash = (hash.result() != current_result);
}
+23 -9
View File
@@ -48,7 +48,8 @@ Node::Node(bool create_default_output) :
override_color_(-1),
last_change_time_(0),
folder_(nullptr),
operation_stack_(0)
operation_stack_(0),
cache_result_(false)
{
if (create_default_output) {
AddOutput();
@@ -504,10 +505,15 @@ NodeValue::Type Node::GetInputDataType(const QString &id) const
void Node::SetInputDataType(const QString &id, const NodeValue::Type &type)
{
Input* i = GetInternalInputData(id);
Input* input_meta = GetInternalInputData(id);
if (i) {
i->type = type;
if (input_meta) {
input_meta->type = type;
int array_sz = InputArraySize(id);
for (int i=-1; i<array_sz; i++) {
GetImmediate(id, i)->set_data_type(type);
}
emit InputDataTypeChanged(id, type);
} else {
@@ -692,7 +698,12 @@ SplitValue Node::GetSplitDefaultValue(const QString &input) const
QVariant Node::GetSplitDefaultValueOnTrack(const QString &input, int track) const
{
return GetSplitDefaultValue(input).at(track);
SplitValue val = GetSplitDefaultValue(input);
if (track < val.size()) {
return val.at(track);
} else {
return QVariant();
}
}
const QVector<NodeKeyframeTrack> &Node::GetKeyframeTracks(const QString &input, int element) const
@@ -2285,12 +2296,15 @@ void NodeSetPositionAndShiftSurroundingsCommand::redo()
foreach (Node* surrounding, node_->parent()->nodes()) {
if (bounding_rect.contains(surrounding->GetPosition()) && surrounding != node_) {
QPointF new_pos = surrounding->GetPosition();
if (surrounding->GetPosition().y() > position_.y()) {
new_pos.setY(new_pos.y() + 0.5);
} else {
new_pos.setY(new_pos.y() - 0.5);
qreal move_rate = 0.50;
if (surrounding->GetPosition().y() < position_.y()) {
move_rate = -move_rate;
}
new_pos.setY(new_pos.y() + move_rate);
auto sur_command = new NodeSetPositionAndShiftSurroundingsCommand(surrounding, new_pos, true);
sur_command->redo();
commands_.append(sur_command);
+12
View File
@@ -756,6 +756,16 @@ public:
folder_ = folder;
}
bool GetCacheTextures() const
{
return cache_result_;
}
void SetCacheTextures(bool e)
{
cache_result_ = e;
}
static const QString kDefaultOutput;
protected:
@@ -1191,6 +1201,8 @@ private:
int operation_stack_;
bool cache_result_;
private slots:
/**
* @brief Slot when a keyframe's time changes to keep the keyframes correctly sorted by time
+3 -1
View File
@@ -574,11 +574,13 @@ bool Track::IsLocked() const
void Track::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const
{
Q_UNUSED(output)
Block* b = BlockAtTime(time);
// Defer to block at this time, don't add any of our own information to the hash
if (b) {
b->Hash(output, hash, TransformTimeForBlock(b, time));
b->Hash(kDefaultOutput, hash, TransformTimeForBlock(b, time));
}
}
+36 -16
View File
@@ -38,7 +38,8 @@ const uint64_t ViewerOutput::kVideoParamEditMask = VideoParamEdit::kWidthHeight
ViewerOutput::ViewerOutput(bool create_default_streams) :
video_frame_cache_(this),
audio_playback_cache_(this)
audio_playback_cache_(this),
cache_enabled_(true)
{
AddInput(kVideoParamsInput, NodeValue::kVideoParams, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable | kInputFlagArray));
SetInputProperty(kVideoParamsInput, QStringLiteral("mask"), QVariant::fromValue(kVideoParamEditMask));
@@ -195,14 +196,18 @@ void ViewerOutput::set_default_parameters()
void ViewerOutput::ShiftVideoCache(const rational &from, const rational &to)
{
video_frame_cache_.Shift(from, to);
if (cache_enabled_) {
video_frame_cache_.Shift(from, to);
}
ShiftVideoEvent(from, to);
}
void ViewerOutput::ShiftAudioCache(const rational &from, const rational &to)
{
audio_playback_cache_.Shift(from, to);
if (cache_enabled_) {
audio_playback_cache_.Shift(from, to);
}
ShiftAudioEvent(from, to);
}
@@ -217,16 +222,18 @@ void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from,
{
Q_UNUSED(element)
if (from == kTextureInput || from == kSamplesInput
|| from == kVideoParamsInput || from == kAudioParamsInput) {
TimeRange invalidated_range(qMax(rational(), range.in()),
qMin(GetLength(), range.out()));
if (cache_enabled_) {
if (from == kTextureInput || from == kSamplesInput
|| from == kVideoParamsInput || from == kAudioParamsInput) {
TimeRange invalidated_range(qMax(rational(), range.in()),
qMin(GetLength(), range.out()));
if (invalidated_range.in() != invalidated_range.out()) {
if (from == kTextureInput || from == kVideoParamsInput) {
video_frame_cache_.Invalidate(invalidated_range, job_time);
} else {
audio_playback_cache_.Invalidate(invalidated_range, job_time);
if (invalidated_range.in() != invalidated_range.out()) {
if (from == kTextureInput || from == kVideoParamsInput) {
video_frame_cache_.Invalidate(invalidated_range, job_time);
} else {
audio_playback_cache_.Invalidate(invalidated_range, job_time);
}
}
}
}
@@ -307,7 +314,9 @@ void ViewerOutput::VerifyLength()
video_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
}
video_frame_cache_.SetLength(video_length);
if (cache_enabled_) {
video_frame_cache_.SetLength(video_length);
}
}
{
@@ -318,7 +327,9 @@ void ViewerOutput::VerifyLength()
audio_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
}
audio_playback_cache_.SetLength(audio_length);
if (cache_enabled_) {
audio_playback_cache_.SetLength(audio_length);
}
}
{
@@ -392,7 +403,9 @@ void ViewerOutput::InputValueChangedEvent(const QString &input, int element)
}
if (frame_rate_changed) {
video_frame_cache_.SetTimebase(new_video_params.frame_rate_as_time_base());
if (cache_enabled_) {
video_frame_cache_.SetTimebase(new_video_params.frame_rate_as_time_base());
}
emit FrameRateChanged(new_video_params.frame_rate());
}
@@ -412,7 +425,9 @@ void ViewerOutput::InputValueChangedEvent(const QString &input, int element)
emit AudioParamsChanged();
audio_playback_cache_.SetParameters(GetAudioParams());
if (cache_enabled_) {
audio_playback_cache_.SetParameters(GetAudioParams());
}
cached_audio_params_ = new_audio_params;
@@ -514,6 +529,11 @@ int ViewerOutput::AddStream(Track::Type type, const QVariant& value)
return index;
}
void ViewerOutput::SetViewerCacheEnabled(bool e)
{
cache_enabled_ = e;
}
void ViewerOutput::InputResized(const QString &input, int old_size, int new_size)
{
if (input == kVideoParamsInput || input == kAudioParamsInput) {
+4
View File
@@ -188,6 +188,8 @@ protected:
int AddStream(Track::Type type, const QVariant &value);
void SetViewerCacheEnabled(bool e);
private:
rational last_length_;
@@ -203,6 +205,8 @@ private:
TimelinePoints timeline_points_;
bool cache_enabled_;
private slots:
void InputResized(const QString& input, int old_size, int new_size);
+12 -116
View File
@@ -48,6 +48,9 @@ Footage::Footage(const QString &filename) :
Clear();
set_filename(filename);
SetCacheTextures(true);
SetViewerCacheEnabled(false);
}
void Footage::Retranslate()
@@ -298,66 +301,27 @@ QString Footage::DescribeAudioStream(const AudioParams &params)
QString::number(params.sample_rate()));
}
bool Footage::CompareFootageToFile(Footage *footage, const QString &filename)
{
// Heuristic to determine if file has changed
QFileInfo info(filename);
if (info.exists()) {
/*if (info.lastModified().toMSecsSinceEpoch() == footage->timestamp()) {
// Footage has not been modified and is where we expect
return true;
} else {
// Footage may have changed and we'll have to re-probe it. It also may not have, in which
// case nothing needs to change.
DecoderPtr decoder = Decoder::CreateFromID(footage->decoder());
Streams probed_streams = decoder->Probe(filename, nullptr);
if (probed_streams == footage->streams_) {
return true;
}
}*/
Q_UNUSED(footage)
// Simplified, since our footage node is much more tolerant, we'll try this
return true;
}
// Footage file couldn't be found or resolved to something we didn't expect
return false;
}
bool Footage::CompareFootageToItsFilename(Footage *footage)
{
return CompareFootageToFile(footage, footage->filename());
}
void Footage::Hash(const QString& output, QCryptographicHash &hash, const rational &time) const
{
super::Hash(output, hash, time);
// Footage last modified date
hash.addData(QString::number(timestamp()).toUtf8());
// Translate output ID to stream
Track::Reference ref = Track::Reference::FromString(output);
QString fn = filename();
if (!fn.isEmpty()) {
if (ref.type() == Track::kVideo) {
VideoParams params = GetVideoParams(ref.index());
if (params.is_valid()) {
// Add footage details to hash
// Footage filename
hash.addData(filename().toUtf8());
// Footage last modified date
hash.addData(QString::number(timestamp()).toUtf8());
QString fn = filename();
// Footage stream
hash.addData(QString::number(ref.index()).toUtf8());
if (ref.type() == Track::kVideo) {
if (!fn.isEmpty()) {
// Current color config and space
hash.addData(project()->color_manager()->GetConfigFilename().toUtf8());
hash.addData(GetColorspaceToUse(params).toUtf8());
@@ -373,10 +337,11 @@ void Footage::Hash(const QString& output, QCryptographicHash &hash, const ration
int64_t video_ts = Timecode::time_to_timestamp(time, params.time_base());
// Add timestamp in units of the video stream's timebase
hash.addData(reinterpret_cast<const char*>(&video_ts), sizeof(int64_t));
hash.addData(reinterpret_cast<const char*>(&video_ts), sizeof(video_ts));
// Add start time - used for both image sequences and video streams
hash.addData(QString::number(params.start_time()).toUtf8());
auto start_time = params.start_time();
hash.addData(reinterpret_cast<const char*>(&start_time), sizeof(start_time));
}
}
}
@@ -490,58 +455,6 @@ void Footage::UpdateTooltip()
}
}
/*void Footage::AddStreamAsInput(Track::Type type, int index, QVariant value)
{
QString input_id = GetInputIDOfIndex(type, index);
Track::Reference ref(type, index);
// Create input for parameters
NodeValue::Type value_type;
uint64_t param_mask = 0;
if (type == Track::kVideo) {
VideoParams vp = value.value<VideoParams>();
value_type = NodeValue::kVideoParams;
// Universal parameters for video/image footage
param_mask |= VideoParamEdit::kEnabled;
param_mask |= VideoParamEdit::kColorspace;
param_mask |= VideoParamEdit::kPixelAspect;
param_mask |= VideoParamEdit::kInterlacing;
if (vp.channel_count() == VideoParams::kRGBAChannelCount) {
// If this has an alpha channel, add a premultiplied optino
param_mask |= VideoParamEdit::kPremultipliedAlpha;
}
if (vp.video_type() != VideoParams::kVideoTypeVideo) {
// This is either a still image or an image sequence, add properties for those
param_mask |= VideoParamEdit::kIsImageSequence;
param_mask |= VideoParamEdit::kStartTime;
param_mask |= VideoParamEdit::kEndTime;
param_mask |= VideoParamEdit::kFrameRate;
} else {
// Ensure timebase isn't overwritten by the frame rate field
param_mask |= VideoParamEdit::kFrameRateIsNotTimebase;
}
} else {
value_type = NodeValue::kAudioParams;
param_mask = 0;
}
AddInput(input_id, value_type,
InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
SetStandardValue(input_id, value);
SetInputProperty(input_id, QStringLiteral("mask"), QVariant::fromValue(param_mask));
inputs_for_stream_properties_.insert(ref, input_id);
// Create output for stream
QString output_id = Track::Reference(type, index).ToString();
AddOutput(output_id);
outputs_for_streams_.insert(ref, output_id);
}*/
void Footage::CheckFootage()
{
QString fn = filename();
@@ -559,21 +472,4 @@ void Footage::CheckFootage()
}
}
/*QString Track::Reference::video_colorspace(bool default_if_empty) const
{
if (IsValid()) {
VideoParams params = footage_->GetVideoParams(index_);
if (params.is_valid()) {
if (params.colorspace().isEmpty() && default_if_empty) {
} else {
return params.colorspace();
}
}
}
return QString();
}*/
}
-3
View File
@@ -170,9 +170,6 @@ public:
static QString DescribeVideoStream(const VideoParams& params);
static QString DescribeAudioStream(const AudioParams& params);
static bool CompareFootageToFile(Footage* footage, const QString& filename);
static bool CompareFootageToItsFilename(Footage* footage);
virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override;
virtual NodeValueTable Value(const QString &output, NodeValueDatabase& value) const override;
-10
View File
@@ -220,16 +220,6 @@ void Project::set_filename(const QString &s)
emit NameChanged();
}
ColorManager *Project::color_manager()
{
return color_manager_;
}
bool Project::is_modified() const
{
return is_modified_;
}
void Project::set_modified(bool e)
{
is_modified_ = e;
+3 -2
View File
@@ -62,9 +62,10 @@ public:
QString pretty_filename() const;
void set_filename(const QString& s);
ColorManager* color_manager();
ColorManager* color_manager() { return color_manager_; }
ProjectSettingsNode* settings() { return settings_; }
bool is_modified() const;
bool is_modified() const { return is_modified_; }
void set_modified(bool e);
bool has_autorecovery_been_saved() const;
+1 -1
View File
@@ -454,7 +454,7 @@ void ProjectViewModel::DisconnectItem(Node *n)
disconnect(f, &Folder::BeginRemoveItem, this, &ProjectViewModel::ItemRemoved);
foreach (Node* c, f->children()) {
ConnectItem(c);
DisconnectItem(c);
}
}
}
+27 -10
View File
@@ -22,6 +22,7 @@
#include "node.h"
#include "render/job/footagejob.h"
#include "render/rendermanager.h"
namespace olive {
@@ -110,7 +111,7 @@ NodeValueTable NodeTraverser::GenerateTable(const Node *n, const QString& output
// By this point, the node should have all the inputs it needs to render correctly
NodeValueTable table = n->Value(output, database);
PostProcessTable(n, range, table);
PostProcessTable(n, output, range, table);
return table;
}
@@ -171,10 +172,15 @@ QVariant NodeTraverser::ProcessFrameGeneration(const Node *node, const GenerateJ
return QVariant();
}
QVariant NodeTraverser::GetCachedFrame(const Node *node, const rational &time)
void NodeTraverser::SaveCachedTexture(const QByteArray &hash, const QVariant &texture)
{
Q_UNUSED(node)
Q_UNUSED(time)
Q_UNUSED(hash)
Q_UNUSED(texture)
}
QVariant NodeTraverser::GetCachedTexture(const QByteArray& hash)
{
Q_UNUSED(hash)
return QVariant();
}
@@ -190,17 +196,23 @@ void NodeTraverser::AddGlobalsToDatabase(NodeValueDatabase &db, const TimeRange&
db.Insert(QStringLiteral("global"), global);
}
void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, NodeValueTable &output_params)
void NodeTraverser::PostProcessTable(const Node *node, const QString& output, const TimeRange &range, NodeValueTable &output_params)
{
bool got_cached_frame = false;
QByteArray cached_node_hash;
// Convert footage to image/sample buffers
QVariant cached_frame = GetCachedFrame(node, range.in());
if (!cached_frame.isNull()) {
output_params.Push(NodeValue::kTexture, cached_frame, node);
if (CanCacheFrames() && node->GetCacheTextures()) {
// This node is set to cache the result, see if we can retrieved a previously cached version
cached_node_hash = RenderManager::Hash(node, output, GetCacheVideoParams(), range.in());
// No more to do here
got_cached_frame = true;
QVariant cached_frame = GetCachedTexture(cached_node_hash);
if (!cached_frame.isNull()) {
output_params.Push(NodeValue::kTexture, cached_frame, node);
// No more to do here
got_cached_frame = true;
}
}
// Strip out any jobs or footage
@@ -285,6 +297,11 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
output_params.Push(NodeValue::kSamples, value, node);
}
}
if (CanCacheFrames() && node->GetCacheTextures() && !got_cached_frame) {
// Save cached texture
SaveCachedTexture(cached_node_hash, output_params.Get(NodeValue::kTexture));
}
}
}
+14 -2
View File
@@ -59,7 +59,19 @@ protected:
virtual QVariant ProcessFrameGeneration(const Node *node, const GenerateJob& job);
virtual QVariant GetCachedFrame(const Node *node, const rational &time);
virtual QVariant GetCachedTexture(const QByteArray& hash);
virtual void SaveCachedTexture(const QByteArray& hash, const QVariant& texture);
virtual bool CanCacheFrames()
{
return false;
}
virtual VideoParams GetCacheVideoParams()
{
return VideoParams();
}
void AddGlobalsToDatabase(NodeValueDatabase& db, const TimeRange &range) const;
@@ -69,7 +81,7 @@ protected:
}
private:
void PostProcessTable(const Node *node, const TimeRange &range, NodeValueTable &output_params);
void PostProcessTable(const Node *node, const QString &output, const TimeRange &range, NodeValueTable &output_params);
};