implement entire project in nodes

Project items are now represented directly in nodes opening up more versatility and possibilities. This is the first iteration of this and will be buggy. Need to test thoroughly.
This commit is contained in:
itsmattkc
2021-02-15 21:31:57 +11:00
parent 155470bec1
commit d7c5ac4b44
174 changed files with 3515 additions and 4658 deletions
+1 -1
View File
@@ -209,7 +209,7 @@ void Block::Retranslate()
SetInputName(kSpeedInput, tr("Speed"));
}
void Block::Hash(QCryptographicHash &, const rational &) const
void Block::Hash(const QString &, QCryptographicHash &, const rational &) const
{
// A block does nothing by default, so we hash nothing
}
+1 -1
View File
@@ -149,7 +149,7 @@ public:
return block_links_;
}
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override;
static const QString kLengthInput;
static const QString kMediaInInput;
+2 -2
View File
@@ -114,12 +114,12 @@ void ClipBlock::Retranslate()
SetInputName(kBufferIn, tr("Buffer"));
}
void ClipBlock::Hash(QCryptographicHash &hash, const rational &time) const
void ClipBlock::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const
{
if (IsInputConnected(kBufferIn)) {
rational t = InputTimeAdjustment(kBufferIn, -1, TimeRange(time, time)).in();
GetConnectedNode(kBufferIn)->Hash(hash, t);
GetConnectedNode(kBufferIn)->Hash(output, hash, t);
}
}
+1 -1
View File
@@ -52,7 +52,7 @@ public:
virtual void Retranslate() override;
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override;
static const QString kBufferIn;
+2 -2
View File
@@ -121,9 +121,9 @@ double TransitionBlock::GetInProgress(const double &time) const
return clamp((GetInternalTransitionTime(time) - out_offset().toDouble()) / in_offset().toDouble(), 0.0, 1.0);
}
void TransitionBlock::Hash(QCryptographicHash &hash, const rational &time) const
void TransitionBlock::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const
{
Node::Hash(hash, time);
Node::Hash(output, hash, time);
double time_dbl = time.toDouble();
double all_prog = GetTotalProgress(time_dbl);
+1 -1
View File
@@ -45,7 +45,7 @@ public:
double GetOutProgress(const double &time) const;
double GetInProgress(const double &time) const;
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time) const override;
virtual NodeValueTable Value(const QString& output, NodeValueDatabase &value) const override;
+11 -3
View File
@@ -20,6 +20,8 @@
#include "factory.h"
#include <QCoreApplication>
#include "audio/pan/pan.h"
#include "audio/volume/volume.h"
#include "block/clip/clip.h"
@@ -35,13 +37,15 @@
#include "filter/blur/blur.h"
#include "filter/mosaic/mosaicfilternode.h"
#include "filter/stroke/stroke.h"
#include "input/media/media.h"
#include "input/time/timeinput.h"
#include "math/math/math.h"
#include "math/merge/merge.h"
#include "math/trigonometry/trigonometry.h"
#include "output/track/track.h"
#include "output/viewer/viewer.h"
#include "project/item/folder/folder.h"
#include "project/item/footage/footage.h"
#include "project/item/sequence/sequence.h"
namespace olive {
QList<Node*> NodeFactory::library_;
@@ -192,8 +196,6 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new MatrixGenerator();
case kTransformDistort:
return new TransformDistortNode();
case kFootageInput:
return new MediaInput();
case kTrackOutput:
return new Track();
case kViewerOutput:
@@ -226,6 +228,12 @@ Node *NodeFactory::CreateFromFactoryIndex(const NodeFactory::InternalID &id)
return new MosaicFilterNode();
case kCropDistort:
return new CropDistortNode();
case kProjectFootage:
return new Footage();
case kProjectFolder:
return new Folder();
case kProjectSequence:
return new Sequence();
case kInternalNodeCount:
break;
+3 -1
View File
@@ -38,7 +38,6 @@ public:
kPolygonGenerator,
kMatrixGenerator,
kTransformDistort,
kFootageInput,
kTrackOutput,
kAudioVolume,
kAudioPanning,
@@ -54,6 +53,9 @@ public:
kDipToColorTransition,
kMosaicFilter,
kCropDistort,
kProjectFootage,
kProjectFolder,
kProjectSequence,
// Count value
kInternalNodeCount
+5 -1
View File
@@ -20,8 +20,12 @@
#include "graph.h"
#include <QChildEvent>
namespace olive {
#define super QObject
NodeGraph::NodeGraph()
{
}
@@ -35,7 +39,7 @@ void NodeGraph::Clear()
void NodeGraph::childEvent(QChildEvent *event)
{
Item::childEvent(event);
super::childEvent(event);
Node* node = dynamic_cast<Node*>(event->child());
+1 -1
View File
@@ -32,7 +32,7 @@ namespace olive {
* This doesn't technically need to be a derivative of Item, but since both Item and NodeGraph need
* to be QObject derivatives, this simplifies Sequence.
*/
class NodeGraph : public Item
class NodeGraph : public QObject
{
Q_OBJECT
public:
-1
View File
@@ -14,7 +14,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
add_subdirectory(media)
add_subdirectory(time)
set(OLIVE_SOURCES
-22
View File
@@ -1,22 +0,0 @@
# 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/media/media.h
node/input/media/media.cpp
PARENT_SCOPE
)
-100
View File
@@ -1,100 +0,0 @@
/***
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 "media.h"
#include "common/timecodefunctions.h"
#include "common/tohex.h"
namespace olive {
const QString MediaInput::kFootageInput = QStringLiteral("footage_in");
MediaInput::MediaInput() :
connected_footage_(nullptr)
{
AddInput(kFootageInput, NodeValue::kFootage, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
}
QVector<Node::CategoryID> MediaInput::Category() const
{
return {kCategoryInput};
}
Stream *MediaInput::stream() const
{
return Node::ValueToPtr<Stream>(GetStandardValue(kFootageInput));
}
void MediaInput::SetStream(Stream* s)
{
SetStandardValue(kFootageInput, Node::PtrToValue(s));
}
void MediaInput::Retranslate()
{
SetInputName(kFootageInput, tr("Media"));
}
NodeValueTable MediaInput::Value(const QString &output, NodeValueDatabase &value) const
{
Q_UNUSED(output)
NodeValueTable table = value.Merge();
if (connected_footage_) {
rational media_duration = Timecode::timestamp_to_time(connected_footage_->duration(),
connected_footage_->timebase());
table.Push(NodeValue::kRational, QVariant::fromValue(media_duration), this, false, QStringLiteral("length"));
}
return table;
}
void MediaInput::InputValueChangedEvent(const QString &input, int element)
{
Q_UNUSED(element)
if (input == kFootageInput) {
Stream* new_footage = stream();
if (new_footage == connected_footage_) {
return;
}
if (connected_footage_) {
disconnect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
}
connected_footage_ = new_footage;
if (connected_footage_) {
connect(connected_footage_, &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged);
}
}
}
void MediaInput::FootageParametersChanged()
{
InvalidateCache(TimeRange(0, RATIONAL_MAX), kFootageInput);
}
}
-82
View File
@@ -1,82 +0,0 @@
/***
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 MEDIAINPUT_H
#define MEDIAINPUT_H
#include "codec/decoder.h"
#include "node/node.h"
#include "project/item/footage/stream.h"
namespace olive {
/**
* @brief A node that imports an image
*/
class MediaInput : public Node
{
Q_OBJECT
public:
MediaInput();
virtual QString Name() const override
{
return tr("Media");
}
virtual QString id() const override
{
return QStringLiteral("org.olivevideoeditor.Olive.mediainput");
}
virtual QString Description() const override
{
return tr("Import footage into the node graph.");
}
virtual Node* copy() const override
{
return new MediaInput();
}
virtual QVector<CategoryID> Category() const override;
Stream* stream() const;
void SetStream(Stream *s);
virtual void Retranslate() override;
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
static const QString kFootageInput;
protected:
virtual void InputValueChangedEvent(const QString& input, int element);
Stream* connected_footage_;
private slots:
void FootageParametersChanged();
};
}
#endif // MEDIAINPUT_H
+2 -2
View File
@@ -66,9 +66,9 @@ NodeValueTable TimeInput::Value(const QString &output, NodeValueDatabase &value)
return table;
}
void TimeInput::Hash(QCryptographicHash &hash, const rational &time) const
void TimeInput::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const
{
Node::Hash(hash, time);
Node::Hash(output, hash, time);
// Make sure time is hashed
hash.addData(NodeValue::ValueToBytes(NodeValue::kRational, QVariant::fromValue(time)));
+1 -1
View File
@@ -40,7 +40,7 @@ public:
virtual NodeValueTable Value(const QString& output, NodeValueDatabase& value) const override;
virtual void Hash(QCryptographicHash& hash, const rational& time) const override;
virtual void Hash(const QString& output, QCryptographicHash& hash, const rational& time) const override;
};
+3 -3
View File
@@ -100,7 +100,7 @@ NodeValueTable MergeNode::Value(const QString &output, NodeValueDatabase &value)
return table;
}
void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const
void MergeNode::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const
{
// We do some hash optimization here. If only one of the inputs is connected, this node
// functions as a passthrough so there's no alteration to the hash. The same is true if the
@@ -113,7 +113,7 @@ void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const
bool blend_changed_hash = false;
if (IsInputConnected(kBaseIn)) {
GetConnectedNode(kBaseIn)->Hash(hash, time);
GetConnectedNode(kBaseIn)->Hash(output, hash, time);
QByteArray post_base_hash = hash.result();
base_changed_hash = (post_base_hash != current_result);
@@ -121,7 +121,7 @@ void MergeNode::Hash(QCryptographicHash &hash, const rational &time) const
}
if(IsInputConnected(kBlendIn)) {
GetConnectedNode(kBlendIn)->Hash(hash, time);
GetConnectedNode(kBlendIn)->Hash(output, hash, time);
blend_changed_hash = (hash.result() != current_result);
}
+1 -1
View File
@@ -46,7 +46,7 @@ public:
static const QString kBaseIn;
static const QString kBlendIn;
virtual void Hash(QCryptographicHash &hash, const rational &time) const override;
virtual void Hash(const QString& output, QCryptographicHash &hash, const rational &time) const override;
private:
NodeInput* base_in_;
+48 -63
View File
@@ -33,8 +33,8 @@
#include "config/config.h"
#include "project/project.h"
#include "project/item/footage/footage.h"
#include "project/item/footage/videostream.h"
#include "ui/colorcoding.h"
#include "ui/icons/icons.h"
#include "widget/nodeview/nodeviewundo.h"
namespace olive {
@@ -76,7 +76,7 @@ NodeGraph *Node::parent() const
return static_cast<NodeGraph*>(QObject::parent());
}
void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled)
void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled)
{
while (XMLReadNextStartElement(reader)) {
if (cancelled && *cancelled) {
@@ -114,7 +114,7 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto
}
}
} else if (reader->name() == QStringLiteral("custom")) {
LoadInternal(reader, xml_node_data);
LoadInternal(reader, xml_node_data, version, cancelled);
} else if (reader->name() == QStringLiteral("connections")) {
// Load connections
while (XMLReadNextStartElement(reader)) {
@@ -199,6 +199,11 @@ void Node::Save(QXmlStreamWriter *writer) const
writer->writeEndElement(); // custom
}
Project* Node::project() const
{
return dynamic_cast<Project*>(parent());
}
QString Node::ShortName() const
{
return Name();
@@ -214,6 +219,12 @@ void Node::Retranslate()
{
}
QIcon Node::icon() const
{
// Just a meaningless default icon to be used where necessary
return icon::New;
}
Color Node::color() const
{
int c;
@@ -253,9 +264,8 @@ QBrush Node::brush(qreal top, qreal bottom) const
void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input)
{
// Ensure parameters exist on the nodes requested
Q_ASSERT(input.node()->HasInputWithID(input.input()));
Q_ASSERT(output.node()->HasOutputWithID(output.output()));
// Ensure graph is the same
Q_ASSERT(input.node()->parent() == output.node()->parent());
// Ensure a connection isn't getting overwritten
Q_ASSERT(input.node()->input_connections().find(input) == input.node()->input_connections().end());
@@ -264,8 +274,9 @@ void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input)
input.node()->input_connections_[input] = output;
output.node()->output_connections_.push_back(std::pair<NodeOutput, NodeInput>({output, input}));
// Call internal event
// Call internal events
input.node()->InputConnectedEvent(input.input(), input.element(), output);
output.node()->OutputConnectedEvent(output.output(), input);
// Emit signals
emit input.node()->InputConnected(output, input);
@@ -279,9 +290,8 @@ void Node::ConnectEdge(const NodeOutput &output, const NodeInput &input)
void Node::DisconnectEdge(const NodeOutput &output, const NodeInput &input)
{
// Ensure parameters exist on the nodes requested
Q_ASSERT(input.node()->HasInputWithID(input.input()));
Q_ASSERT(output.node()->HasOutputWithID(output.output()));
// Ensure graph is the same
Q_ASSERT(input.node()->parent() == output.node()->parent());
// Ensure connection exists
Q_ASSERT(input.node()->input_connections().at(input) == output);
@@ -293,8 +303,9 @@ void Node::DisconnectEdge(const NodeOutput &output, const NodeInput &input)
OutputConnections& outputs = output.node()->output_connections_;
outputs.erase(std::find(outputs.begin(), outputs.end(), std::pair<NodeOutput, NodeInput>({output, input})));
// Call internal event
// Call internal events
input.node()->InputDisconnectedEvent(input.input(), input.element(), output);
output.node()->OutputDisconnectedEvent(output.output(), input);
emit input.node()->InputDisconnected(output, input);
emit output.node()->OutputDisconnected(output, input);
@@ -1292,7 +1303,7 @@ void Node::IgnoreHashingFrom(const QString &input_id)
ignore_when_hashing_.append(input_id);
}
void Node::LoadInternal(QXmlStreamReader *reader, XMLNodeData &)
void Node::LoadInternal(QXmlStreamReader *reader, XMLNodeData &, uint, const QAtomicInt*)
{
reader->skipCurrentElement();
}
@@ -1337,10 +1348,13 @@ void Node::SetLabel(const QString &s)
}
}
void Node::Hash(QCryptographicHash &hash, const rational& time) const
void Node::Hash(const QString &output, QCryptographicHash &hash, const rational& time) const
{
// Add this Node's ID
Q_UNUSED(output)
// Add this Node's ID and output being used
hash.addData(id().toUtf8());
hash.addData(output.toUtf8());
foreach (const QString& input, input_ids_) {
// For each input, try to hash its value
@@ -1479,57 +1493,14 @@ void Node::HashInputElement(QCryptographicHash &hash, const QString& input, int
if (IsInputConnected(input, element)) {
// Traverse down this edge
GetConnectedNode(input, element)->Hash(hash, input_time);
NodeOutput output = GetConnectedOutput(input, element);
output.node()->Hash(output.output(), hash, input_time);
} else {
// Grab the value at this time
QVariant value = GetValueAtTime(input, input_time, element);
hash.addData(NodeValue::ValueToBytes(GetInputDataType(input), value));
}
// We have one exception for FOOTAGE types, since we resolve the footage into a frame in the renderer
if (GetInputDataType(input) == NodeValue::kFootage) {
Stream* stream = Node::ValueToPtr<Stream>(GetStandardValue(input, element));
if (stream) {
// Add footage details to hash
// Footage filename
hash.addData(stream->footage()->filename().toUtf8());
// Footage last modified date
hash.addData(QString::number(stream->footage()->timestamp()).toUtf8());
// Footage stream
hash.addData(QString::number(stream->index()).toUtf8());
if (stream->type() == Stream::kVideo) {
VideoStream* image_stream = static_cast<VideoStream*>(stream);
// Current color config and space
hash.addData(image_stream->footage()->project()->color_manager()->GetConfigFilename().toUtf8());
hash.addData(image_stream->colorspace().toUtf8());
// Alpha associated setting
hash.addData(QString::number(image_stream->premultiplied_alpha()).toUtf8());
// Pixel aspect ratio
hash.addData(reinterpret_cast<const char*>(&image_stream->pixel_aspect_ratio()), sizeof(rational));
}
// Footage timestamp
if (stream->type() == Stream::kVideo) {
VideoStream* video_stream = static_cast<VideoStream*>(stream);
int64_t video_ts = Timecode::time_to_timestamp(input_time, video_stream->timebase());
// Add timestamp in units of the video stream's timebase
hash.addData(reinterpret_cast<const char*>(&video_ts), sizeof(int64_t));
// Add start time - used for both image sequences and video streams
hash.addData(QString::number(video_stream->start_time()).toUtf8());
}
}
}
}
QVector<Node *> Node::GetDependencies() const
@@ -1700,6 +1671,8 @@ QString Node::GetCategoryName(const CategoryID &c)
return tr("Channel");
case kCategoryTransition:
return tr("Transition");
case kCategoryProject:
return tr("Project");
case kCategoryUnknown:
case kCategoryCount:
break;
@@ -2032,6 +2005,18 @@ void Node::InputDisconnectedEvent(const QString &input, int element, const NodeO
Q_UNUSED(output)
}
void Node::OutputConnectedEvent(const QString &output, const NodeInput &input)
{
Q_UNUSED(output)
Q_UNUSED(input)
}
void Node::OutputDisconnectedEvent(const QString &output, const NodeInput &input)
{
Q_UNUSED(output)
Q_UNUSED(input)
}
void Node::childEvent(QChildEvent *event)
{
super::childEvent(event);
@@ -2152,17 +2137,17 @@ void Node::InvalidateFromKeyframeTypeChanged()
Project *Node::ArrayInsertCommand::GetRelevantProject() const
{
return node_->parent()->project();
return node_->project();
}
Project *Node::ArrayRemoveCommand::GetRelevantProject() const
{
return node_->parent()->project();
return node_->project();
}
Project *Node::ArrayResizeCommand::GetRelevantProject() const
{
return node_->parent()->project();
return node_->project();
}
}
+12 -3
View File
@@ -79,6 +79,7 @@ public:
kCategoryChannels,
kCategoryTransition,
kCategoryDistort,
kCategoryProject,
kCategoryCount
};
@@ -100,10 +101,12 @@ public:
*/
NodeGraph* parent() const;
Project* project() const;
/**
* @brief Clear current node variables and replace them with
*/
void Load(QXmlStreamReader* reader, XMLNodeData &xml_node_data, const QAtomicInt *cancelled);
void Load(QXmlStreamReader* reader, XMLNodeData &xml_node_data, uint version, const QAtomicInt *cancelled);
/**
* @brief Save this node into a text/XML format
@@ -157,6 +160,8 @@ public:
*/
virtual void Retranslate();
virtual QIcon icon() const;
const QVector<QString>& inputs() const
{
return input_ids_;
@@ -685,7 +690,7 @@ public:
const QString& GetLabel() const;
void SetLabel(const QString& s);
virtual void Hash(QCryptographicHash& hash, const rational &time) const;
virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time) const;
void InvalidateAll(const QString& input, int element = -1);
@@ -768,7 +773,7 @@ protected:
void IgnoreHashingFrom(const QString& input_id);
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data);
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled);
virtual void SaveInternal(QXmlStreamWriter* writer) const;
@@ -798,6 +803,10 @@ protected:
virtual void InputDisconnectedEvent(const QString& input, int element, const NodeOutput& output);
virtual void OutputConnectedEvent(const QString& output, const NodeInput& input);
virtual void OutputDisconnectedEvent(const QString& output, const NodeInput& input);
virtual void childEvent(QChildEvent *event) override;
signals:
+5 -2
View File
@@ -36,7 +36,7 @@ void NodeCopyPasteService::CopyNodesToClipboard(const QVector<Node *> &nodes, vo
QXmlStreamWriter writer(&copy_str);
writer.setAutoFormatting(true);
writer.writeStartDocument();
writer.writeStartDocument(QString::number(Core::kProjectVersion));
writer.writeStartElement(QStringLiteral("olive"));
foreach (Node* n, nodes) {
@@ -65,12 +65,15 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
}
QXmlStreamReader reader(clipboard);
uint data_version = reader.documentVersion().toUInt();
QVector<Node*> pasted_nodes;
XMLNodeData xml_node_data;
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("olive")) {
// Default to current version - this may not be desirable?
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("node")) {
Node* node = nullptr;
@@ -83,7 +86,7 @@ QVector<Node *> NodeCopyPasteService::PasteNodesFromClipboard(NodeGraph *graph,
}
if (node) {
node->Load(&reader, xml_node_data, nullptr);
node->Load(&reader, xml_node_data, data_version, nullptr);
pasted_nodes.append(node);
}
+3 -3
View File
@@ -138,7 +138,7 @@ void Track::SetTrackHeight(const double &height)
emit TrackHeightChangedInPixels(GetTrackHeightInPixels());
}
void Track::LoadInternal(QXmlStreamReader *reader, XMLNodeData &)
void Track::LoadInternal(QXmlStreamReader *reader, XMLNodeData &, uint , const QAtomicInt* )
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("height")) {
@@ -567,13 +567,13 @@ bool Track::IsLocked() const
return locked_;
}
void Track::Hash(QCryptographicHash &hash, const rational &time) const
void Track::Hash(const QString &output, QCryptographicHash &hash, const rational &time) const
{
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(hash, TransformTimeForBlock(b, time));
b->Hash(output, hash, TransformTimeForBlock(b, time));
}
}
+2 -2
View File
@@ -274,7 +274,7 @@ public:
bool IsLocked() const;
virtual void Hash(QCryptographicHash& hash, const rational &time) const override;
virtual void Hash(const QString& output, QCryptographicHash& hash, const rational &time) const override;
AudioVisualWaveform& waveform()
{
@@ -335,7 +335,7 @@ signals:
void BlocksRefreshed();
protected:
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data) override;
virtual void LoadInternal(QXmlStreamReader* reader, XMLNodeData& xml_node_data, uint version, const QAtomicInt* cancelled) override;
virtual void SaveInternal(QXmlStreamWriter* writer) const override;
+4 -4
View File
@@ -27,7 +27,7 @@
namespace olive {
TrackList::TrackList(ViewerOutput *parent, const Track::Type &type, const QString &track_input) :
TrackList::TrackList(Sequence *parent, const Track::Type &type, const QString &track_input) :
QObject(parent),
track_input_(track_input),
type_(type)
@@ -136,7 +136,7 @@ void TrackList::UpdateTrackIndexesFrom(int index)
NodeGraph *TrackList::GetParentGraph() const
{
return static_cast<NodeGraph*>(parent()->parent());
return parent()->parent();
}
const QString& TrackList::track_input() const
@@ -149,9 +149,9 @@ NodeInput TrackList::track_input(int element) const
return NodeInput(parent(), track_input(), element);
}
ViewerOutput *TrackList::parent() const
Sequence *TrackList::parent() const
{
return static_cast<ViewerOutput*>(QObject::parent());
return static_cast<Sequence*>(QObject::parent());
}
int TrackList::ArraySize() const
+3 -3
View File
@@ -29,13 +29,13 @@
namespace olive {
class ViewerOutput;
class Sequence;
class TrackList : public QObject
{
Q_OBJECT
public:
TrackList(ViewerOutput *parent, const Track::Type& type, const QString& track_input);
TrackList(Sequence *parent, const Track::Type& type, const QString& track_input);
const Track::Type& type() const
{
@@ -64,7 +64,7 @@ public:
const QString &track_input() const;
NodeInput track_input(int element) const;
ViewerOutput* parent() const;
Sequence* parent() const;
int ArraySize() const;
+2 -271
View File
@@ -20,49 +20,13 @@
#include "viewer.h"
#include "node/traverser.h"
namespace olive {
const QString ViewerOutput::kTextureInput = QStringLiteral("tex_in");
const QString ViewerOutput::kSamplesInput = QStringLiteral("samples_in");
const QString ViewerOutput::kTrackInputFormat = QStringLiteral("track_in_%1");
#define super Sequence
ViewerOutput::ViewerOutput() :
video_frame_cache_(this),
audio_playback_cache_(this),
operation_stack_(0)
Sequence(true)
{
AddInput(kTextureInput, NodeValue::kTexture, InputFlags(kInputFlagNotKeyframable));
AddInput(kSamplesInput, NodeValue::kSamples, InputFlags(kInputFlagNotKeyframable));
// Create TrackList instances
track_lists_.resize(Track::kCount);
for (int i=0;i<Track::kCount;i++) {
// Create track input
QString track_input_id = kTrackInputFormat.arg(i);
AddInput(track_input_id, NodeValue::kNone, InputFlags(kInputFlagNotKeyframable | kInputFlagArray));
IgnoreInvalidationsFrom(track_input_id);
TrackList* list = new TrackList(this, static_cast<Track::Type>(i), track_input_id);
track_lists_.replace(i, list);
connect(list, &TrackList::TrackListChanged, this, &ViewerOutput::UpdateTrackCache);
connect(list, &TrackList::LengthChanged, this, &ViewerOutput::VerifyLength);
connect(list, &TrackList::TrackAdded, this, &ViewerOutput::TrackAdded);
connect(list, &TrackList::TrackRemoved, this, &ViewerOutput::TrackRemoved);
}
// Create UUID for this node
uuid_ = QUuid::createUuid();
}
ViewerOutput::~ViewerOutput()
{
DisconnectAll();
}
Node *ViewerOutput::copy() const
@@ -90,237 +54,4 @@ QString ViewerOutput::Description() const
return tr("Interface between a Viewer panel and the node system.");
}
void ViewerOutput::ShiftVideoCache(const rational &from, const rational &to)
{
video_frame_cache_.Shift(from, to);
}
void ViewerOutput::ShiftAudioCache(const rational &from, const rational &to)
{
audio_playback_cache_.Shift(from, to);
foreach (Track* track, track_lists_.at(Track::kAudio)->GetTracks()) {
track->waveform().Shift(from, to);
}
}
void ViewerOutput::ShiftCache(const rational &from, const rational &to)
{
ShiftVideoCache(from, to);
ShiftAudioCache(from, to);
}
void ViewerOutput::InvalidateCache(const TimeRange& range, const QString& from, int element)
{
Q_UNUSED(element)
if (operation_stack_ == 0) {
if (from == kTextureInput || from == kSamplesInput) {
TimeRange invalidated_range(qMax(rational(), range.in()),
qMin(GetLength(), range.out()));
if (invalidated_range.in() != invalidated_range.out()) {
if (from == kTextureInput) {
video_frame_cache_.Invalidate(invalidated_range);
} else {
audio_playback_cache_.Invalidate(invalidated_range);
}
}
}
VerifyLength();
}
Node::InvalidateCache(range, from);
}
void ViewerOutput::set_video_params(const VideoParams &video)
{
bool size_changed = video_params_.width() != video.width() || video_params_.height() != video.height();
bool timebase_changed = video_params_.time_base() != video.time_base();
bool pixel_aspect_changed = video_params_.pixel_aspect_ratio() != video.pixel_aspect_ratio();
bool interlacing_changed = video_params_.interlacing() != video.interlacing();
video_params_ = video;
if (size_changed) {
emit SizeChanged(video_params_.width(), video_params_.height());
}
if (pixel_aspect_changed) {
emit PixelAspectChanged(video_params_.pixel_aspect_ratio());
}
if (interlacing_changed) {
emit InterlacingChanged(video_params_.interlacing());
}
if (timebase_changed) {
video_frame_cache_.SetTimebase(video_params_.time_base());
emit TimebaseChanged(video_params_.time_base());
}
emit VideoParamsChanged();
video_frame_cache_.InvalidateAll();
}
void ViewerOutput::set_audio_params(const AudioParams &audio)
{
audio_params_ = audio;
emit AudioParamsChanged();
// This will automatically InvalidateAll
audio_playback_cache_.SetParameters(audio_params());
}
rational ViewerOutput::GetLength()
{
return last_length_;
}
QVector<Track *> ViewerOutput::GetUnlockedTracks() const
{
QVector<Track*> tracks = GetTracks();
for (int i=0;i<tracks.size();i++) {
if (tracks.at(i)->IsLocked()) {
tracks.removeAt(i);
i--;
}
}
return tracks;
}
void ViewerOutput::UpdateTrackCache()
{
track_cache_.clear();
foreach (TrackList* list, track_lists_) {
foreach (Track* track, list->GetTracks()) {
track_cache_.append(track);
}
}
}
void ViewerOutput::VerifyLength()
{
if (operation_stack_ != 0) {
return;
}
NodeTraverser traverser;
rational video_length, audio_length, subtitle_length;
{
video_length = track_lists_.at(Track::kVideo)->GetTotalLength();
if (video_length.isNull() && IsInputConnected(kTextureInput)) {
NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kTextureInput), TimeRange(0, 0));
video_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
}
video_frame_cache_.SetLength(video_length);
}
{
audio_length = track_lists_.at(Track::kAudio)->GetTotalLength();
if (audio_length.isNull() && IsInputConnected(kSamplesInput)) {
NodeValueTable t = traverser.GenerateTable(GetConnectedOutput(kSamplesInput), TimeRange(0, 0));
audio_length = t.Get(NodeValue::kRational, QStringLiteral("length")).value<rational>();
}
audio_playback_cache_.SetLength(audio_length);
}
{
subtitle_length = track_lists_.at(Track::kSubtitle)->GetTotalLength();
}
rational real_length = qMax(subtitle_length, qMax(video_length, audio_length));
if (real_length != last_length_) {
last_length_ = real_length;
emit LengthChanged(last_length_);
}
}
void ViewerOutput::Retranslate()
{
Node::Retranslate();
SetInputName(kTextureInput, tr("Texture"));
SetInputName(kSamplesInput, tr("Samples"));
for (int i=0;i<Track::kCount;i++) {
QString input_name;
switch (static_cast<Track::Type>(i)) {
case Track::kVideo:
input_name = tr("Video Tracks");
break;
case Track::kAudio:
input_name = tr("Audio Tracks");
break;
case Track::kSubtitle:
input_name = tr("Subtitle Tracks");
break;
case Track::kNone:
case Track::kCount:
break;
}
if (!input_name.isEmpty()) {
SetInputName(kTrackInputFormat.arg(i), input_name);
}
}
}
void ViewerOutput::BeginOperation()
{
operation_stack_++;
Node::BeginOperation();
}
void ViewerOutput::EndOperation()
{
operation_stack_--;
Node::EndOperation();
}
void ViewerOutput::InputConnectedEvent(const QString &input, int element, const NodeOutput &output)
{
if (input == kTextureInput) {
emit TextureInputChanged();
} else {
foreach (TrackList* list, track_lists_) {
if (list->track_input() == input) {
list->TrackConnected(output.node(), element);
break;
}
}
}
}
void ViewerOutput::InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output)
{
if (input == kTextureInput) {
emit TextureInputChanged();
} else {
foreach (TrackList* list, track_lists_) {
if (list->track_input() == input) {
list->TrackDisconnected(output.node(), element);
break;
}
}
}
}
}
+2 -128
View File
@@ -21,17 +21,7 @@
#ifndef VIEWER_H
#define VIEWER_H
#include <QUuid>
#include "node/block/block.h"
#include "node/output/track/track.h"
#include "node/output/track/tracklist.h"
#include "node/node.h"
#include "render/audioparams.h"
#include "render/audioplaybackcache.h"
#include "render/framehashcache.h"
#include "render/videoparams.h"
#include "timeline/timelinecommon.h"
#include "project/item/sequence/sequence.h"
namespace olive {
@@ -40,14 +30,12 @@ namespace olive {
*
* Receives update/time change signals from ViewerPanels and responds by sending them a texture of that frame
*/
class ViewerOutput : public Node
class ViewerOutput : public Sequence
{
Q_OBJECT
public:
ViewerOutput();
virtual ~ViewerOutput() override;
virtual Node* copy() const override;
virtual QString Name() const override;
@@ -55,120 +43,6 @@ public:
virtual QVector<CategoryID> Category() const override;
virtual QString Description() const override;
void ShiftVideoCache(const rational& from, const rational& to);
void ShiftAudioCache(const rational& from, const rational& to);
void ShiftCache(const rational& from, const rational& to);
virtual void InvalidateCache(const TimeRange& range, const QString& from, int element = -1) override;
const VideoParams& video_params() const
{
return video_params_;
}
const AudioParams& audio_params() const
{
return audio_params_;
}
void set_video_params(const VideoParams &video);
void set_audio_params(const AudioParams &audio);
rational GetLength();
const QUuid& uuid() const
{
return uuid_;
}
const QVector<Track *> &GetTracks() const
{
return track_cache_;
}
Track* GetTrackFromReference(const Track::Reference& track_ref) const
{
return track_lists_.at(track_ref.type())->GetTrackAt(track_ref.index());
}
/**
* @brief Same as GetTracks() but omits tracks that are locked.
*/
QVector<Track *> GetUnlockedTracks() const;
TrackList* track_list(Track::Type type) const
{
return track_lists_.at(type);
}
virtual void Retranslate() override;
FrameHashCache* video_frame_cache()
{
return &video_frame_cache_;
}
AudioPlaybackCache* audio_playback_cache()
{
return &audio_playback_cache_;
}
virtual void BeginOperation() override;
virtual void EndOperation() override;
static const QString kTextureInput;
static const QString kSamplesInput;
static const QString kTrackInputFormat;
signals:
void TimebaseChanged(const rational&);
void LengthChanged(const rational& length);
void SizeChanged(int width, int height);
void PixelAspectChanged(const rational& pixel_aspect);
void InterlacingChanged(VideoParams::Interlacing mode);
void VideoParamsChanged();
void AudioParamsChanged();
void TrackAdded(Track* track);
void TrackRemoved(Track* track);
void TextureInputChanged();
protected:
void InputConnectedEvent(const QString &input, int element, const NodeOutput &output) override;
void InputDisconnectedEvent(const QString &input, int element, const NodeOutput &output) override;
private:
QUuid uuid_;
VideoParams video_params_;
AudioParams audio_params_;
QVector<TrackList*> track_lists_;
QVector<Track*> track_cache_;
rational last_length_;
FrameHashCache video_frame_cache_;
AudioPlaybackCache audio_playback_cache_;
int operation_stack_;
private slots:
void UpdateTrackCache();
void VerifyLength();
};
}
+14 -22
View File
@@ -21,6 +21,7 @@
#include "traverser.h"
#include "node.h"
#include "render/job/footagejob.h"
namespace olive {
@@ -127,7 +128,7 @@ NodeValueTable NodeTraverser::GenerateBlockTable(const Track *track, const TimeR
return table;
}
QVariant NodeTraverser::ProcessVideoFootage(VideoStream *stream, const rational &input_time)
QVariant NodeTraverser::ProcessVideoFootage(const Footage::StreamReference& stream, const rational &input_time)
{
Q_UNUSED(stream)
Q_UNUSED(input_time)
@@ -135,7 +136,7 @@ QVariant NodeTraverser::ProcessVideoFootage(VideoStream *stream, const rational
return QVariant();
}
QVariant NodeTraverser::ProcessAudioFootage(AudioStream *stream, const TimeRange &input_time)
QVariant NodeTraverser::ProcessAudioFootage(const Footage::StreamReference& stream, const TimeRange &input_time)
{
Q_UNUSED(stream)
Q_UNUSED(input_time)
@@ -202,8 +203,7 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
}
// Strip out any jobs or footage
QList<NodeValue> video_footage_to_retrieve;
QList<NodeValue> audio_footage_to_retrieve;
QList<NodeValue> footage_jobs_to_run;
QList<NodeValue> shader_jobs_to_run;
QList<NodeValue> sample_jobs_to_run;
QList<NodeValue> generate_jobs_to_run;
@@ -212,16 +212,8 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
const NodeValue& v = output_params.at(i);
QList<NodeValue>* take_this_value_list = nullptr;
if (v.type() == NodeValue::kFootage) {
Stream* s = Node::ValueToPtr<Stream>(v.data());
if (s) {
if (s->type() == Stream::kVideo) {
take_this_value_list = &video_footage_to_retrieve;
} else if (s->type() == Stream::kAudio) {
take_this_value_list = &audio_footage_to_retrieve;
}
}
if (v.type() == NodeValue::kFootageJob) {
take_this_value_list = &footage_jobs_to_run;
} else if (v.type() == NodeValue::kShaderJob) {
take_this_value_list = &shader_jobs_to_run;
} else if (v.type() == NodeValue::kSampleJob) {
@@ -238,12 +230,12 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
if (!got_cached_frame) {
// Retrieve video frames
foreach (const NodeValue& v, video_footage_to_retrieve) {
foreach (const NodeValue& v, footage_jobs_to_run) {
// Assume this is a VideoStream, we did a type check earlier in the function
VideoStream* stream = Node::ValueToPtr<VideoStream>(v.data());
Footage::StreamReference job = v.data().value<Footage::StreamReference>();
if (stream->footage()->IsValid()) {
QVariant value = ProcessVideoFootage(stream, range.in());
if (job.IsValid() && job.type() == Stream::kVideo && job.footage()->IsValid()) {
QVariant value = ProcessVideoFootage(job, range.in());
if (!value.isNull()) {
output_params.Push(NodeValue::kTexture, value, node);
@@ -271,12 +263,12 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
}
// Retrieve audio samples
foreach (const NodeValue& v, audio_footage_to_retrieve) {
foreach (const NodeValue& v, footage_jobs_to_run) {
// Assume this is an AudioStream, we did a type check earlier in the function
AudioStream* stream = Node::ValueToPtr<AudioStream>(v.data());
Footage::StreamReference job = v.data().value<Footage::StreamReference>();
if (stream->footage()->IsValid()) {
QVariant value = ProcessAudioFootage(stream, range);
if (job.IsValid() && job.type() == Stream::kAudio && job.footage()->IsValid()) {
QVariant value = ProcessAudioFootage(job, range);
if (!value.isNull()) {
output_params.Push(NodeValue::kSamples, value, node);
+2 -3
View File
@@ -26,7 +26,6 @@
#include "codec/decoder.h"
#include "common/cancelableobject.h"
#include "node/output/track/track.h"
#include "project/item/footage/stream.h"
#include "value.h"
namespace olive {
@@ -49,9 +48,9 @@ protected:
virtual NodeValueTable GenerateBlockTable(const Track *track, const TimeRange& range);
virtual QVariant ProcessVideoFootage(VideoStream* stream, const rational &input_time);
virtual QVariant ProcessVideoFootage(const Footage::StreamReference &stream, const rational &input_time);
virtual QVariant ProcessAudioFootage(AudioStream* stream, const TimeRange &input_time);
virtual QVariant ProcessAudioFootage(const Footage::StreamReference &stream, const TimeRange &input_time);
virtual QVariant ProcessShader(const Node *node, const TimeRange &range, const ShaderJob& job);
+16 -5
View File
@@ -27,6 +27,7 @@
#include <QVector4D>
#include "common/tohex.h"
#include "project/item/footage/stream.h"
#include "render/color.h"
namespace olive {
@@ -64,8 +65,6 @@ QString NodeValue::ValueToString(Type data_type, const QVariant &value, bool val
QString::number(c.alpha()));
} else if (data_type == kRational) {
return value.value<rational>().toString();
} else if (data_type == kFootage) {
return QString::number(value.value<quintptr>());
} else if (data_type == kTexture
|| data_type == kSamples) {
// These data types need no XML representation
@@ -116,9 +115,14 @@ QByteArray NodeValue::ValueToBytes(NodeValue::Type type, const QVariant &value)
case kVec4: return ValueToBytesInternal<QVector4D>(value);
case kCombo: return ValueToBytesInternal<int>(value);
case kVideoStreamProperties:
case kAudioStreamProperties:
return value.value<Stream>().toBytes();
// These types have no persistent input
case kNone:
case kFootage:
case kFootageJob:
case kTexture:
case kSamples:
case kShaderJob:
@@ -177,6 +181,10 @@ QVector<QVariant> NodeValue::split_normal_value_into_track_values(Type type, con
QVariant NodeValue::combine_track_values_into_normal_value(Type type, const QVector<QVariant> &split)
{
if (split.isEmpty()) {
return QVariant();
}
switch (type) {
case kVec2:
{
@@ -293,15 +301,18 @@ QString NodeValue::GetPrettyDataTypeName(Type type)
return QCoreApplication::translate("NodeValue", "Texture");
case kSamples:
return QCoreApplication::translate("NodeValue", "Samples");
case kFootage:
return QCoreApplication::translate("NodeValue", "Footage");
case kVec2:
return QCoreApplication::translate("NodeValue", "Vector 2D");
case kVec3:
return QCoreApplication::translate("NodeValue", "Vector 3D");
case kVec4:
return QCoreApplication::translate("NodeValue", "Vector 4D");
case kVideoStreamProperties:
return QCoreApplication::translate("NodeValue", "Video Stream Properties");
case kAudioStreamProperties:
return QCoreApplication::translate("NodeValue", "Audio Stream Properties");
case kFootageJob:
case kShaderJob:
case kSampleJob:
case kGenerateJob:
+23 -7
View File
@@ -121,13 +121,6 @@ public:
*/
kSamples,
/**
* Footage stream identifier type
*
* Resolves to `StreamPtr`.
*/
kFootage,
/**
* Two-dimensional vector (XY) type
*
@@ -156,6 +149,29 @@ public:
*/
kCombo,
/**
* Properties pertaining to the video stream of a footage file
*
* Resolves to a `Stream` object.
*/
kVideoStreamProperties,
/**
* Properties pertaining to the audio stream of a footage file
*
* Resolves to a `Stream` object.
*/
kAudioStreamProperties,
/**
* Job type
*
* An internal type used to indicate to the renderer that a footage job needs to
* run. This value will usually be taken from a table and a kTexture or kSamples value will be
* pushed to take its place.
*/
kFootageJob,
/**
* Job type
*