project: improved loading and footage importing

This commit is contained in:
itsmattkc
2020-10-19 14:41:01 +11:00
parent 30e5a442a8
commit db8d321fa7
39 changed files with 630 additions and 329 deletions
+1 -1
View File
@@ -811,7 +811,7 @@ uint64_t FFmpegDecoder::ValidateChannelLayout(AVStream* stream)
bool FFmpegDecoder::StreamUsesMultipleInstances(StreamPtr stream)
{
return stream->type() == Stream::kVideo
&& !std::static_pointer_cast<VideoStream>(stream)->is_image_sequence();
&& std::static_pointer_cast<VideoStream>(stream)->video_type() != VideoStream::kVideoTypeStill;
}
FramePtr FFmpegDecoder::BuffersToNativeFrame(int divider, int width, int height, const rational& ts, uint8_t** input_data, int* input_linesize)
+2 -50
View File
@@ -26,49 +26,6 @@
OLIVE_NAMESPACE_ENTER
Node* XMLLoadNode(QXmlStreamReader* reader)
{
QString node_id;
quintptr node_ptr = 0;
QPointF node_pos;
QString node_label;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
node_id = attr.value().toString();
} else if (attr.name() == QStringLiteral("ptr")) {
node_ptr = attr.value().toULongLong();
} else if (attr.name() == QStringLiteral("pos")) {
QStringList pos = attr.value().toString().split(':');
// Protection in case this file has been messed with
if (pos.size() == 2) {
node_pos.setX(pos.at(0).toDouble());
node_pos.setY(pos.at(1).toDouble());
}
} else if (attr.name() == QStringLiteral("label")) {
node_label = attr.value().toString();
}
}
if (node_id.isEmpty()) {
qWarning() << "Found node with no ID";
return nullptr;
}
Node* node = NodeFactory::CreateFromID(node_id);
if (node) {
node->setProperty("xml_ptr", node_ptr);
node->SetPosition(node_pos);
node->SetLabel(node_label);
} else {
qWarning() << "Failed to load" << node_id << "- no node with that ID is installed";
}
return node;
}
void XMLConnectNodes(const XMLNodeData &xml_node_data, QUndoCommand *command)
{
foreach (const XMLNodeData::SerializedConnection& con, xml_node_data.desired_connections) {
@@ -102,13 +59,8 @@ bool XMLReadNextStartElement(QXmlStreamReader *reader)
void XMLLinkBlocks(const XMLNodeData &xml_node_data)
{
foreach (const XMLNodeData::BlockLink& l1, xml_node_data.block_links) {
foreach (const XMLNodeData::BlockLink& l2, xml_node_data.block_links) {
if (l1.link == l2.block->property("xml_ptr")) {
Block::Link(l1.block, l2.block);
break;
}
}
foreach (const XMLNodeData::BlockLink& l, xml_node_data.block_links) {
Block::Link(l.block, static_cast<Block*>(xml_node_data.node_ptrs.value(l.link)));
}
}
+1 -5
View File
@@ -39,8 +39,6 @@ class Item;
QXmlStreamAttributes __attributes = reader->attributes(); \
foreach (const QXmlStreamAttribute& item, __attributes)
Node *XMLLoadNode(QXmlStreamReader* reader);
struct XMLNodeData {
struct SerializedConnection {
NodeInput* input;
@@ -57,6 +55,7 @@ struct XMLNodeData {
quintptr link;
};
QHash<quintptr, Node*> node_ptrs;
QHash<quintptr, NodeOutput*> output_ptrs;
QList<SerializedConnection> desired_connections;
QHash<quintptr, StreamPtr> footage_ptrs;
@@ -64,9 +63,6 @@ struct XMLNodeData {
QList<BlockLink> block_links;
QHash<quintptr, Item*> item_ptrs;
QString real_project_url;
QString saved_project_url;
};
void XMLConnectNodes(const XMLNodeData& xml_node_data, QUndoCommand* command = nullptr);
+56 -6
View File
@@ -40,6 +40,7 @@
#include "config/config.h"
#include "dialog/about/about.h"
#include "dialog/export/export.h"
#include "dialog/footagerelink/footagerelinkdialog.h"
#include "dialog/sequence/sequence.h"
#include "dialog/task/task.h"
#include "dialog/preferences/preferences.h"
@@ -482,12 +483,14 @@ void Core::AddOpenProject(ProjectPtr p)
void Core::AddOpenProjectFromTask(Task *task)
{
QList<ProjectPtr> projects = static_cast<ProjectLoadTask*>(task)->GetLoadedProjects();
QList<MainWindowLayoutInfo> layouts = static_cast<ProjectLoadTask*>(task)->GetLoadedLayouts();
ProjectLoadTask* load_task = static_cast<ProjectLoadTask*>(task);
for (int i=0; i<projects.size(); i++) {
AddOpenProject(projects.at(i));
main_window_->LoadLayout(layouts.at(i));
ProjectPtr project = load_task->GetLoadedProject();
MainWindowLayoutInfo layout = load_task->GetLoadedLayout();
if (ValidateFootageInLoadedProject(project, load_task->GetFilenameProjectWasSavedAs())) {
AddOpenProject(project);
main_window_->LoadLayout(layout);
}
}
@@ -560,7 +563,7 @@ bool Core::StartHeadlessExport()
CLITaskDialog task_dialog(&plm);
if (task_dialog.Run()) {
ProjectPtr p = plm.GetLoadedProjects().first();
ProjectPtr p = plm.GetLoadedProject();
QList<ItemPtr> items = p->get_items_of_type(Item::kSequence);
// Check if this project contains sequences
@@ -1222,6 +1225,53 @@ void Core::CacheActiveSequence(bool in_out_only)
}
}
bool Core::ValidateFootageInLoadedProject(ProjectPtr project, const QString& project_saved_url)
{
QList<FootagePtr> footage_we_couldnt_validate;
QList<ItemPtr> project_footage = project->get_items_of_type(Item::kFootage);
foreach (ItemPtr item, project_footage) {
FootagePtr footage = std::static_pointer_cast<Footage>(item);
if (!QFileInfo::exists(footage->filename())) {
// If the footage doesn't exist, it might have moved with the project
const QString& project_current_url = project->filename();
if (project_current_url != project_saved_url) {
// Project has definitely moved, try to resolve relative paths
QDir saved_dir(QFileInfo(project_saved_url).dir());
QDir true_dir(QFileInfo(project_current_url).dir());
QString relative_filename = saved_dir.relativeFilePath(footage->filename());
QString transformed_abs_filename = true_dir.filePath(relative_filename);
if (QFileInfo::exists(transformed_abs_filename)) {
// Use this file instead
qInfo() << "Resolved" << footage->filename() << "relatively to" << transformed_abs_filename;
footage->set_filename(transformed_abs_filename);
}
}
}
// Heuristically compare footage to file
if (Footage::CompareFootageToItsFilename(footage)) {
footage->SetValid();
} else {
footage_we_couldnt_validate.append(footage);
}
}
if (!footage_we_couldnt_validate.isEmpty()) {
FootageRelinkDialog frd(footage_we_couldnt_validate, main_window_);
if (frd.exec() == QDialog::Rejected) {
return false;
}
}
return true;
}
bool Core::CloseAllProjects()
{
return CloseAllProjects(true);
+5
View File
@@ -268,6 +268,11 @@ public:
*/
void CacheActiveSequence(bool in_out_only);
/**
* @brief Check each footage object for whether it still exists or has changed
*/
bool ValidateFootageInLoadedProject(ProjectPtr project, const QString &project_saved_url);
public slots:
/**
* @brief Starts an open file dialog to load a project from file
+1
View File
@@ -20,6 +20,7 @@ add_subdirectory(color)
add_subdirectory(diskcache)
add_subdirectory(export)
add_subdirectory(footageproperties)
add_subdirectory(footagerelink)
add_subdirectory(keyframeproperties)
add_subdirectory(preferences)
add_subdirectory(progress)
+22
View File
@@ -0,0 +1,22 @@
# Olive - Non-Linear Video Editor
# Copyright (C) 2019 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}
dialog/footagerelink/footagerelinkdialog.h
dialog/footagerelink/footagerelinkdialog.cpp
PARENT_SCOPE
)
@@ -0,0 +1,103 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 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 "footagerelinkdialog.h"
#include <QDialogButtonBox>
#include <QFileDialog>
#include <QFileInfo>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
OLIVE_NAMESPACE_ENTER
FootageRelinkDialog::FootageRelinkDialog(const QList<FootagePtr>& footage, QWidget* parent) :
QDialog(parent),
footage_(footage)
{
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("The following files couldn't be found. Clips using them will be "
"unplayable until they're relinked."));
table_ = new QTreeWidget();
table_->setColumnCount(3);
table_->setHeaderLabels({tr("Footage"), tr("Filename"), tr("Actions")});
table_->setRootIsDecorated(false);
for (int i=0; i<footage.size(); i++) {
FootagePtr f = footage.at(i);
QTreeWidgetItem* item = new QTreeWidgetItem();
QWidget* item_actions = new QWidget();
QHBoxLayout* item_actions_layout = new QHBoxLayout(item_actions);
QPushButton* item_browse_btn = new QPushButton(tr("Browse"));
item_browse_btn->setProperty("index", i);
connect(item_browse_btn, &QPushButton::clicked, this, &FootageRelinkDialog::BrowseForFootage);
item_actions_layout->addWidget(item_browse_btn);
item->setIcon(0, f->icon());
item->setText(0, f->name());
item->setText(1, f->filename());
table_->addTopLevelItem(item);
table_->setItemWidget(item, 2, item_actions);
}
layout->addWidget(table_);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(buttons, &QDialogButtonBox::accepted, this, &FootageRelinkDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &FootageRelinkDialog::reject);
layout->addWidget(buttons);
setWindowTitle(tr("Relink Footage"));
}
void FootageRelinkDialog::BrowseForFootage()
{
int index = sender()->property("index").toInt();
FootagePtr f = footage_.at(index);
QFileInfo info(f->filename());
QString new_fn = QFileDialog::getOpenFileName(this,
tr("Relink \"%1\"").arg(f->name()),
info.absolutePath(),
QStringLiteral("%1;;%2 (**)").arg(info.fileName(), tr("All Files")));
if (!new_fn.isEmpty()) {
f->set_filename(new_fn);
if (Footage::CompareFootageToItsFilename(f)) {
// Set footage to valid and update icon
f->SetValid();
QTreeWidgetItem* item = table_->topLevelItem(index);
item->setIcon(0, f->icon());
item->setText(1, f->filename());
}
}
}
OLIVE_NAMESPACE_EXIT
@@ -0,0 +1,49 @@
/***
Olive - Non-Linear Video Editor
Copyright (C) 2019 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 FOOTAGERELINKDIALOG_H
#define FOOTAGERELINKDIALOG_H
#include <QDialog>
#include <QTreeWidget>
#include "project/item/footage/footage.h"
OLIVE_NAMESPACE_ENTER
class FootageRelinkDialog : public QDialog
{
Q_OBJECT
public:
FootageRelinkDialog(const QList<FootagePtr>& footage, QWidget* parent = nullptr);
private:
QTreeWidget* table_;
QList<FootagePtr> footage_;
private slots:
void BrowseForFootage();
};
OLIVE_NAMESPACE_EXIT
#endif // FOOTAGERELINKDIALOG_H
+6 -4
View File
@@ -220,10 +220,12 @@ rational Block::MediaToSequenceTime(const rational &media_time) const
void Block::LoadInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data)
{
if (reader->name() == QStringLiteral("link")) {
xml_node_data.block_links.append({this, reader->readElementText().toULongLong()});
} else {
Node::LoadInternal(reader, xml_node_data);
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("link")) {
xml_node_data.block_links.append({this, reader->readElementText().toULongLong()});
} else {
reader->skipCurrentElement();
}
}
}
+6 -6
View File
@@ -88,7 +88,7 @@ void NodeInput::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const
}
if (attr.name() == QStringLiteral("keyframing")) {
set_is_keyframing(attr.value() == QStringLiteral("1"));
set_is_keyframing(attr.value().toInt());
}
}
}
@@ -199,16 +199,16 @@ void NodeInput::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const
set_property(QStringLiteral("col_view"), reader->readElementText());
} else if (reader->name() == QStringLiteral("cslook")) {
set_property(QStringLiteral("col_look"), reader->readElementText());
} else {
} else if (reader->name() == QStringLiteral("custom")) {
LoadInternal(reader, xml_node_data, cancelled);
} else {
reader->skipCurrentElement();
}
}
}
void NodeInput::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement("input");
writer->writeAttribute("id", id());
writer->writeAttribute("keyframing", QString::number(keyframing_));
@@ -258,9 +258,9 @@ void NodeInput::Save(QXmlStreamWriter *writer) const
SaveConnections(writer);
writer->writeStartElement(QStringLiteral("custom"));
SaveInternal(writer);
writer->writeEndElement(); // input
writer->writeEndElement(); // custom
}
void NodeInput::SaveConnections(QXmlStreamWriter *writer) const
+14 -10
View File
@@ -187,17 +187,19 @@ void NodeInputArray::RemoveAt(int index)
void NodeInputArray::LoadInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt* cancelled)
{
if (reader->name() == QStringLiteral("subparameters")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("input")) {
Append();
At(GetSize() - 1)->Load(reader, xml_node_data, cancelled);
} else {
reader->skipCurrentElement();
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("subparameters")) {
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("input")) {
Append();
At(GetSize() - 1)->Load(reader, xml_node_data, cancelled);
} else {
reader->skipCurrentElement();
}
}
} else {
reader->skipCurrentElement();
}
} else {
NodeInput::Load(reader, xml_node_data, cancelled);
}
}
@@ -206,7 +208,9 @@ void NodeInputArray::SaveInternal(QXmlStreamWriter *writer) const
writer->writeStartElement("subparameters");
foreach (NodeInput* sub, sub_params_) {
sub->Save(writer);
writer->writeStartElement(QStringLiteral("input"));
sub->Save(writer);
writer->writeEndElement();
}
writer->writeEndElement(); // subparameters
+42 -15
View File
@@ -95,34 +95,61 @@ void Node::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QAto
}
param->Load(reader, xml_node_data, cancelled);
} else {
} else if (reader->name() == QStringLiteral("ptr")) {
xml_node_data.node_ptrs.insert(reader->readElementText().toULongLong(), this);
} else if (reader->name() == QStringLiteral("pos")) {
QPointF p;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("x")) {
p.setX(reader->readElementText().toDouble());
} else if (reader->name() == QStringLiteral("y")) {
p.setY(reader->readElementText().toDouble());
} else {
reader->skipCurrentElement();
}
}
SetPosition(p);
} else if (reader->name() == QStringLiteral("label")) {
SetLabel(reader->readElementText());
} else if (reader->name() == QStringLiteral("custom")) {
LoadInternal(reader, xml_node_data);
} else {
reader->skipCurrentElement();
}
}
}
void Node::Save(QXmlStreamWriter *writer, const QString &custom_name) const
void Node::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement(custom_name.isEmpty() ? QStringLiteral("node") : custom_name);
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeAttribute(QStringLiteral("id"), id());
writer->writeStartElement(QStringLiteral("pos"));
writer->writeTextElement(QStringLiteral("x"), QString::number(GetPosition().x()));
writer->writeTextElement(QStringLiteral("y"), QString::number(GetPosition().y()));
writer->writeEndElement(); // pos
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeAttribute(QStringLiteral("pos"),
QStringLiteral("%1:%2").arg(QString::number(GetPosition().x()),
QString::number(GetPosition().y())));
writer->writeAttribute(QStringLiteral("label"),
GetLabel());
writer->writeTextElement(QStringLiteral("label"), GetLabel());
foreach (NodeParam* param, parameters()) {
switch (param->type()) {
case NodeParam::kInput:
writer->writeStartElement(QStringLiteral("input"));
break;
case NodeParam::kOutput:
writer->writeStartElement(QStringLiteral("output"));
break;
}
param->Save(writer);
writer->writeEndElement(); // input/output
}
writer->writeStartElement(QStringLiteral("custom"));
SaveInternal(writer);
writer->writeEndElement(); // node
writer->writeEndElement(); // custom
}
QString Node::ShortName() const
@@ -361,7 +388,7 @@ void Node::Hash(QCryptographicHash &hash, const rational& time) const
hash.addData(stream->footage()->filename().toUtf8());
// Footage last modified date
hash.addData(stream->footage()->timestamp().toString().toUtf8());
hash.addData(QString::number(stream->footage()->timestamp()).toUtf8());
// Footage stream
hash.addData(QString::number(stream->index()).toUtf8());
+1 -1
View File
@@ -95,7 +95,7 @@ public:
/**
* @brief Save this node into a text/XML format
*/
void Save(QXmlStreamWriter* writer, const QString& custom_name = QString()) const;
void Save(QXmlStreamWriter* writer) const;
/**
* @brief Return the name of the node
-4
View File
@@ -63,13 +63,9 @@ void NodeOutput::Load(QXmlStreamReader* reader, XMLNodeData &xml_node_data, cons
void NodeOutput::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement("output");
writer->writeAttribute("id", id());
writer->writeAttribute("ptr", QString::number(reinterpret_cast<quintptr>(this)));
writer->writeEndElement(); // output
}
OLIVE_NAMESPACE_EXIT
+7 -5
View File
@@ -111,12 +111,14 @@ void TrackOutput::SetTrackHeight(const double &height)
emit TrackHeightChangedInPixels(GetTrackHeightInPixels());
}
void TrackOutput::LoadInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data)
void TrackOutput::LoadInternal(QXmlStreamReader *reader, XMLNodeData &)
{
if (reader->name() == QStringLiteral("height")) {
SetTrackHeight(reader->readElementText().toDouble());
} else {
Node::LoadInternal(reader, xml_node_data);
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("height")) {
SetTrackHeight(reader->readElementText().toDouble());
} else {
reader->skipCurrentElement();
}
}
}
+14 -6
View File
@@ -212,10 +212,14 @@ 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) {
QVariant value = ProcessVideoFootage(v.data().value<StreamPtr>(), range.in());
StreamPtr stream = v.data().value<StreamPtr>();
if (!value.isNull()) {
output_params.Push(NodeParam::kTexture, value, node);
if (stream->footage()->IsValid()) {
QVariant value = ProcessVideoFootage(stream, range.in());
if (!value.isNull()) {
output_params.Push(NodeParam::kTexture, value, node);
}
}
}
@@ -240,10 +244,14 @@ void NodeTraverser::PostProcessTable(const Node *node, const TimeRange &range, N
// Retrieve audio samples
foreach (const NodeValue& v, audio_footage_to_retrieve) {
QVariant value = ProcessAudioFootage(v.data().value<StreamPtr>(), range);
StreamPtr stream = v.data().value<StreamPtr>();
if (!value.isNull()) {
output_params.Push(NodeParam::kSamples, value, node);
if (stream->footage()->IsValid()) {
QVariant value = ProcessAudioFootage(v.data().value<StreamPtr>(), range);
if (!value.isNull()) {
output_params.Push(NodeParam::kSamples, value, node);
}
}
}
+15 -5
View File
@@ -81,17 +81,27 @@ void Folder::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const QA
void Folder::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement(QStringLiteral("folder"));
writer->writeAttribute(QStringLiteral("name"), name());
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
foreach (ItemPtr child, children()) {
child->Save(writer);
}
switch (child->type()) {
case Item::kFootage:
writer->writeStartElement(QStringLiteral("footage"));
break;
case Item::kSequence:
writer->writeStartElement(QStringLiteral("sequence"));
break;
case Item::kFolder:
writer->writeStartElement(QStringLiteral("folder"));
break;
}
writer->writeEndElement(); // folder
child->Save(writer);
writer->writeEndElement(); // footage/folder/sequence
}
}
OLIVE_NAMESPACE_EXIT
+24
View File
@@ -20,6 +20,8 @@
#include "audiostream.h"
#include "common/xmlutils.h"
OLIVE_NAMESPACE_ENTER
AudioStream::AudioStream()
@@ -101,4 +103,26 @@ QIcon AudioStream::icon() const
return icon::Audio;
}
void AudioStream::LoadCustomParameters(QXmlStreamReader *reader)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("channels")) {
set_channels(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("layout")) {
set_channel_layout(reader->readElementText().toULongLong());
} else if (reader->name() == QStringLiteral("rate")) {
set_sample_rate(reader->readElementText().toInt());
} else {
reader->skipCurrentElement();
}
}
}
void AudioStream::SaveCustomParameters(QXmlStreamWriter *writer) const
{
writer->writeTextElement(QStringLiteral("channels"), QString::number(channels_));
writer->writeTextElement(QStringLiteral("layout"), QString::number(layout_));
writer->writeTextElement(QStringLiteral("rate"), QString::number(sample_rate_));
}
OLIVE_NAMESPACE_EXIT
+5
View File
@@ -55,6 +55,11 @@ public:
virtual QIcon icon() const override;
protected:
virtual void LoadCustomParameters(QXmlStreamReader *reader) override;
virtual void SaveCustomParameters(QXmlStreamWriter* writer) const override;
signals:
void ConformAppended(OLIVE_NAMESPACE::AudioParams params);
+49 -68
View File
@@ -24,6 +24,7 @@
#include <QDir>
#include "codec/decoder.h"
#include "common/filefunctions.h"
#include "common/xmlutils.h"
#include "config/config.h"
#include "core.h"
@@ -43,91 +44,45 @@ Footage::~Footage()
void Footage::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt* cancelled)
{
/*
QXmlStreamAttributes attributes = reader->attributes();
foreach (const QXmlStreamAttribute& attr, attributes) {
if (attr.name() == QStringLiteral("name")) {
set_name(attr.value().toString());
} else if (attr.name() == QStringLiteral("filename")) {
set_filename(attr.value().toString());
}
}
// Validate filename
if (!QFileInfo::exists(filename_)) {
// Absolute filename does not exist, use some heuristics to try relocating the file
if (xml_node_data.real_project_url != xml_node_data.saved_project_url) {
// Project path has changed, check if the file we're looking for is the same relative to the
// new project path
QDir saved_dir(QFileInfo(xml_node_data.saved_project_url).dir());
QDir true_dir(QFileInfo(xml_node_data.real_project_url).dir());
QString relative_filename = saved_dir.relativeFilePath(filename_);
QString transformed_abs_filename = true_dir.filePath(relative_filename);
if (QFileInfo::exists(transformed_abs_filename)) {
// Use this file instead
qInfo() << "Footage" << filename_ << "doesn't exist, using relative file" << transformed_abs_filename;
set_filename(transformed_abs_filename);
}
}
}
Decoder::ProbeMedia(this, cancelled);
while (XMLReadNextStartElement(reader)) {
if (cancelled && *cancelled) {
return;
}
if (reader->name() == QStringLiteral("stream")) {
int stream_index = -1;
quintptr stream_ptr = 0;
XMLAttributeLoop(reader, attr) {
if (cancelled && *cancelled) {
return;
}
if (attr.name() == QStringLiteral("index")) {
stream_index = attr.value().toInt();
} else if (attr.name() == QStringLiteral("ptr")) {
stream_ptr = attr.value().toULongLong();
}
}
if (stream_index > -1 && stream_ptr > 0) {
xml_node_data.footage_ptrs.insert(stream_ptr, stream(stream_index));
stream(stream_index)->Load(reader);
} else {
qWarning() << "Invalid stream found in project file";
}
if (reader->name() == QStringLiteral("name")) {
set_name(reader->readElementText());
} else if (reader->name() == QStringLiteral("filename")) {
set_filename(reader->readElementText());
} else if (reader->name() == QStringLiteral("stream")) {
add_stream(Stream::Load(reader, xml_node_data, cancelled));
} else if (reader->name() == QStringLiteral("timestamp")) {
set_timestamp(reader->readElementText().toLongLong());
} else if (reader->name() == QStringLiteral("decoder")) {
set_decoder(reader->readElementText());
} else if (reader->name() == QStringLiteral("points")) {
TimelinePoints::Load(reader);
} else {
reader->skipCurrentElement();
}
}
*/
}
void Footage::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement("footage");
writer->writeTextElement(QStringLiteral("name"), name());
writer->writeTextElement(QStringLiteral("filename"), filename());
writer->writeTextElement(QStringLiteral("timestamp"), QString::number(timestamp_));
writer->writeTextElement(QStringLiteral("decoder"), decoder_);
writer->writeAttribute("name", name());
writer->writeAttribute("filename", filename());
TimelinePoints::Save(writer);
writer->writeStartElement(QStringLiteral("points"));
TimelinePoints::Save(writer);
writer->writeEndElement(); // points
foreach (StreamPtr stream, streams_) {
stream->Save(writer);
writer->writeStartElement(QStringLiteral("stream"));
stream->Save(writer);
writer->writeEndElement(); // stream
}
writer->writeEndElement(); // footage
}
void Footage::Clear()
@@ -154,12 +109,12 @@ void Footage::set_filename(const QString &s)
filename_ = s;
}
const QDateTime &Footage::timestamp() const
const qint64 &Footage::timestamp() const
{
return timestamp_;
}
void Footage::set_timestamp(const QDateTime &t)
void Footage::set_timestamp(const qint64 &t)
{
timestamp_ = t;
}
@@ -341,6 +296,32 @@ StreamPtr Footage::get_first_stream_of_type(const Stream::Type &type) const
return nullptr;
}
bool Footage::CompareFootageToItsFilename(FootagePtr footage)
{
// Heuristic to determine if file has changed
QFileInfo info(footage->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.
ItemPtr item = Decoder::ProbeMedia(footage->filename(), nullptr);
if (item && item->type() == footage->type()) {
// Item is the same type, that's a good sign. Let's look for any differences.
// FIXME: Implement this
return true;
}
}
}
// Footage file couldn't be found or resolved to something we didn't expect
return false;
}
void Footage::UpdateTooltip()
{
if (valid_) {
+8 -5
View File
@@ -32,6 +32,9 @@
OLIVE_NAMESPACE_ENTER
class Footage;
using FootagePtr = std::shared_ptr<Footage>;
/**
* @brief A reference to an external media file with metadata in a project structure
*
@@ -109,7 +112,7 @@ public:
* The file's last modified timestamp is stored for potential organization in the ProjectExplorer. It can be
* retrieved here.
*/
const QDateTime& timestamp() const;
const qint64 &timestamp() const;
/**
* @brief Set the last modified time/date
@@ -120,7 +123,7 @@ public:
*
* New last modified time/date
*/
void set_timestamp(const QDateTime& t);
void set_timestamp(const qint64 &t);
/**
* @brief Add a stream metadata object to this footage
@@ -199,6 +202,8 @@ public:
StreamPtr get_first_stream_of_type(const Stream::Type& type) const;
static bool CompareFootageToItsFilename(FootagePtr footage);
private:
/**
* @brief Internal function to delete all Stream children and empty the array
@@ -229,7 +234,7 @@ private:
/**
* @brief Internal timestamp object
*/
QDateTime timestamp_;
qint64 timestamp_;
/**
* @brief Internal streams array
@@ -245,8 +250,6 @@ private:
};
using FootagePtr = std::shared_ptr<Footage>;
OLIVE_NAMESPACE_EXIT
#endif // FOOTAGE_H
+55 -6
View File
@@ -37,22 +37,71 @@ Stream::~Stream()
{
}
void Stream::Load(QXmlStreamReader *reader)
StreamPtr Stream::Load(QXmlStreamReader *reader, XMLNodeData &xml_node_data, const QAtomicInt* cancelled)
{
LoadCustomParameters(reader);
StreamPtr stream;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("type")) {
Stream::Type type = static_cast<Stream::Type>(attr.value().toInt());
switch (type) {
case Stream::kVideo:
stream = std::make_shared<VideoStream>();
break;
case Stream::kAudio:
stream = std::make_shared<AudioStream>();
break;
default:
stream = std::make_shared<Stream>();
stream->set_type(type);
break;
}
// This is the only attribute we need
break;
}
}
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("ptr")) {
xml_node_data.footage_ptrs.insert(reader->readElementText().toULongLong(), stream);
} else if (reader->name() == QStringLiteral("index")) {
stream->set_index(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("timebase")) {
stream->set_timebase(rational::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("duration")) {
stream->set_duration(reader->readElementText().toLongLong());
} else if (reader->name() == QStringLiteral("enabled")) {
stream->set_enabled(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("custom")) {
stream->LoadCustomParameters(reader);
} else {
reader->skipCurrentElement();
}
}
return stream;
}
void Stream::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement("stream");
writer->writeAttribute(QStringLiteral("type"), QString::number(type_));
writer->writeAttribute("ptr", QString::number(reinterpret_cast<quintptr>(this)));
writer->writeTextElement(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
writer->writeAttribute("index", QString::number(index_));
writer->writeTextElement(QStringLiteral("index"), QString::number(index_));
writer->writeTextElement(QStringLiteral("timebase"), timebase_.toString());
writer->writeTextElement(QStringLiteral("duration"), QString::number(duration_));
writer->writeTextElement(QStringLiteral("enabled"), QString::number(enabled_));
writer->writeStartElement(QStringLiteral("custom"));
SaveCustomParameters(writer);
writer->writeEndElement(); // stream
writer->writeEndElement();
}
QString Stream::description() const
+5 -4
View File
@@ -33,6 +33,9 @@
OLIVE_NAMESPACE_ENTER
class Footage;
class Stream;
using StreamPtr = std::shared_ptr<Stream>;
struct XMLNodeData;
/**
* @brief A base class for keeping metadata about a media stream.
@@ -64,9 +67,9 @@ public:
/**
* @brief Required virtual destructor, serves no purpose
*/
virtual ~Stream();
virtual ~Stream() override;
void Load(QXmlStreamReader* reader);
static StreamPtr Load(QXmlStreamReader* reader, XMLNodeData& xml_node_data, const QAtomicInt* cancelled);
void Save(QXmlStreamWriter *writer) const;
@@ -119,8 +122,6 @@ private:
};
using StreamPtr = std::shared_ptr<Stream>;
OLIVE_NAMESPACE_EXIT
#include <QMetaType>
+30 -14
View File
@@ -35,8 +35,7 @@ VideoStream::VideoStream() :
interlacing_(VideoParams::kInterlaceNone),
video_type_(VideoStream::kVideoTypeVideo),
pixel_aspect_ratio_(1),
start_time_(0),
is_image_sequence_(false)
start_time_(0)
{
set_type(Stream::kVideo);
}
@@ -75,16 +74,6 @@ void VideoStream::set_start_time(const int64_t &start_time)
emit ParametersChanged();
}
bool VideoStream::is_image_sequence() const
{
return is_image_sequence_;
}
void VideoStream::set_image_sequence(bool e)
{
is_image_sequence_ = e;
}
int64_t VideoStream::get_time_in_timebase_units(const rational &time) const
{
return Timecode::time_to_timestamp(time, timebase()) + start_time();
@@ -102,8 +91,26 @@ QIcon VideoStream::icon() const
void VideoStream::LoadCustomParameters(QXmlStreamReader *reader)
{
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("colorspace")) {
if (reader->name() == QStringLiteral("width")) {
set_width(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("height")) {
set_height(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("premultiplied")) {
set_premultiplied_alpha(reader->readElementText().toInt());
} else if (reader->name() == QStringLiteral("colorspace")) {
set_colorspace(reader->readElementText());
} else if (reader->name() == QStringLiteral("interlacing")) {
set_interlacing(static_cast<VideoParams::Interlacing>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("type")) {
set_video_type(static_cast<VideoType>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("format")) {
set_format(static_cast<PixelFormat::Format>(reader->readElementText().toInt()));
} else if (reader->name() == QStringLiteral("pixelaspect")) {
set_pixel_aspect_ratio(rational::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("framerate")) {
set_frame_rate(rational::fromString(reader->readElementText()));
} else if (reader->name() == QStringLiteral("starttime")) {
set_start_time(reader->readElementText().toLongLong());
} else {
reader->skipCurrentElement();
}
@@ -112,7 +119,16 @@ void VideoStream::LoadCustomParameters(QXmlStreamReader *reader)
void VideoStream::SaveCustomParameters(QXmlStreamWriter *writer) const
{
writer->writeTextElement("colorspace", colorspace_);
writer->writeTextElement(QStringLiteral("width"), QString::number(width_));
writer->writeTextElement(QStringLiteral("height"), QString::number(height_));
writer->writeTextElement(QStringLiteral("premultiplied"), QString::number(premultiplied_alpha_));
writer->writeTextElement(QStringLiteral("colorspace"), colorspace_);
writer->writeTextElement(QStringLiteral("interlacing"), QString::number(interlacing_));
writer->writeTextElement(QStringLiteral("type"), QString::number(video_type_));
writer->writeTextElement(QStringLiteral("format"), QString::number(format_));
writer->writeTextElement(QStringLiteral("pixelaspect"), pixel_aspect_ratio_.toString());
writer->writeTextElement(QStringLiteral("framerate"), frame_rate_.toString());
writer->writeTextElement(QStringLiteral("starttime"), QString::number(start_time_));
}
bool VideoStream::premultiplied_alpha() const
-5
View File
@@ -132,9 +132,6 @@ public:
const int64_t& start_time() const;
void set_start_time(const int64_t& start_time);
bool is_image_sequence() const;
void set_image_sequence(bool e);
int64_t get_time_in_timebase_units(const rational& time) const;
virtual QIcon icon() const override;
@@ -166,8 +163,6 @@ private:
int64_t start_time_;
bool is_image_sequence_;
};
using VideoStreamPtr = std::shared_ptr<VideoStream>;
+19 -8
View File
@@ -121,7 +121,14 @@ void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const
Node* node;
if (reader->name() == QStringLiteral("node")) {
node = XMLLoadNode(reader);
node = nullptr;
XMLAttributeLoop(reader, attr) {
if (attr.name() == QStringLiteral("id")) {
node = NodeFactory::CreateFromID(attr.value().toString());
break;
}
}
} else {
node = viewer_output_;
}
@@ -143,7 +150,7 @@ void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const
XMLLinkBlocks(xml_node_data);
// Ensure this and all children are in the main thread
// (FIXME: Weird place for this? This should probably be in ProjectLoadManager somehow)
// NOTE: It might be good to move the Item system to QObjects so they inherit their thread
if (thread() != qApp->thread()) {
moveToThread(qApp->thread());
}
@@ -151,8 +158,6 @@ void Sequence::Load(QXmlStreamReader *reader, XMLNodeData& xml_node_data, const
void Sequence::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement(QStringLiteral("sequence"));
writer->writeAttribute(QStringLiteral("name"), name());
writer->writeAttribute(QStringLiteral("ptr"), QString::number(reinterpret_cast<quintptr>(this)));
@@ -178,17 +183,23 @@ void Sequence::Save(QXmlStreamWriter *writer) const
writer->writeEndElement(); // audio
// Write TimelinePoints
TimelinePoints::Save(writer);
writer->writeStartElement(QStringLiteral("points"));
TimelinePoints::Save(writer);
writer->writeEndElement(); // points
foreach (Node* node, nodes()) {
if (node != viewer_output_) {
writer->writeStartElement(QStringLiteral("node"));
writer->writeAttribute(QStringLiteral("id"), node->id());
node->Save(writer);
writer->writeEndElement(); // node;
}
}
viewer_output_->Save(writer, QStringLiteral("viewer"));
writer->writeEndElement(); // sequence
writer->writeStartElement(QStringLiteral("viewer"));
writer->writeAttribute(QStringLiteral("id"), viewer_output_->id());
viewer_output_->Save(writer);
writer->writeEndElement(); // viewer;
}
void Sequence::add_default_nodes()
+7 -20
View File
@@ -47,13 +47,9 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, const
{
XMLNodeData xml_node_data;
// Set project filename (hacky)
xml_node_data.real_project_url = static_cast<QFile*>(reader->device())->fileName();
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("folder")) {
if (reader->name() == QStringLiteral("root")) {
// Assume this folder is our root
root_.Load(reader, xml_node_data, cancelled);
} else if (reader->name() == QStringLiteral("colormanagement")) {
@@ -82,11 +78,6 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, const
*layout = MainWindowLayoutInfo::fromXml(reader, xml_node_data);
} else if (reader->name() == QStringLiteral("url")) {
// This should be read in before most other elements
xml_node_data.saved_project_url = reader->readElementText();
} else {
// Skip this
@@ -104,27 +95,23 @@ void Project::Load(QXmlStreamReader *reader, MainWindowLayoutInfo* layout, const
void Project::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement("project");
writer->writeTextElement("url", filename_);
writer->writeTextElement("cachepath", cache_path(false));
writer->writeTextElement(QStringLiteral("cachepath"), cache_path(false));
writer->writeStartElement(QStringLiteral("root"));
root_.Save(writer);
writer->writeEndElement();
writer->writeStartElement("colormanagement");
writer->writeStartElement(QStringLiteral("colormanagement"));
writer->writeTextElement("config", color_manager_.GetConfigFilename());
writer->writeTextElement(QStringLiteral("config"), color_manager_.GetConfigFilename());
writer->writeTextElement("default", color_manager_.GetDefaultInputColorSpace());
writer->writeTextElement(QStringLiteral("default"), color_manager_.GetDefaultInputColorSpace());
writer->writeEndElement(); // colormanagement
// Save main window project layout
MainWindowLayoutInfo main_window_info = Core::instance()->main_window()->SaveLayout();
main_window_info.toXml(writer);
writer->writeEndElement(); // project
}
Folder *Project::root()
+1 -2
View File
@@ -124,7 +124,7 @@ void ProjectImportTask::Import(Folder *folder, QFileInfoList import, int &counte
FootagePtr footage = std::static_pointer_cast<Footage>(item);
footage->set_filename(file_path);
footage->set_timestamp(file_info.lastModified());
footage->set_timestamp(file_info.lastModified().toMSecsSinceEpoch());
// See if this footage is an image sequence
ValidateImageSequence(footage, import, i);
@@ -223,7 +223,6 @@ void ProjectImportTask::ValidateImageSequence(ItemPtr item, QFileInfoList& info_
rational default_timebase = Config::Current()["DefaultSequenceFrameRate"].value<rational>();
video_stream->set_timebase(default_timebase);
video_stream->set_frame_rate(default_timebase.flipped());
video_stream->set_image_sequence(true);
video_stream->set_start_time(start_index);
video_stream->set_duration(end_index - start_index + 1);
+7 -11
View File
@@ -46,22 +46,18 @@ bool ProjectLoadTask::Run()
while(XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("version")) {
qDebug() << "Project version:" << reader.readElementText();
} else if (reader.name() == QStringLiteral("url")) {
project_saved_url_ = reader.readElementText();
} else if (reader.name() == QStringLiteral("project")) {
ProjectPtr project = std::make_shared<Project>();
project_ = std::make_shared<Project>();
project->set_filename(filename_);
project_->set_filename(filename_);
MainWindowLayoutInfo layout;
project->Load(&reader, &layout, &IsCancelled());
project_->Load(&reader, &layout_info_, &IsCancelled());
// Ensure project is in main thread
project->moveToThread(qApp->thread());
if (!IsCancelled()) {
projects_.append(project);
layout_info_.append(layout);
}
project_->moveToThread(qApp->thread());
break;
} else {
reader.skipCurrentElement();
}
+17 -5
View File
@@ -33,23 +33,35 @@ class ProjectLoadTask : public Task
public:
ProjectLoadTask(const QString& filename);
const QList<ProjectPtr>& GetLoadedProjects() const
ProjectPtr GetLoadedProject() const
{
return projects_;
return project_;
}
const QList<MainWindowLayoutInfo>& GetLoadedLayouts() const
MainWindowLayoutInfo GetLoadedLayout() const
{
return layout_info_;
}
/**
* @brief Returns the filename the project was saved as, but not necessarily where it is now
*
* May help for resolving relative paths.
*/
const QString& GetFilenameProjectWasSavedAs() const
{
return project_saved_url_;
}
protected:
virtual bool Run() override;
private:
QList<ProjectPtr> projects_;
ProjectPtr project_;
QList<MainWindowLayoutInfo> layout_info_;
MainWindowLayoutInfo layout_info_;
QString project_saved_url_;
QString filename_;
+6
View File
@@ -51,8 +51,14 @@ bool ProjectSaveTask::Run()
writer.writeTextElement("version", "0.2.0");
writer.writeTextElement("url", project_->filename());
writer.writeStartElement(QStringLiteral("project"));
project_->Save(&writer);
writer.writeEndElement(); // project
writer.writeEndElement(); // olive
writer.writeEndDocument();
-4
View File
@@ -55,8 +55,6 @@ void TimelineMarker::set_name(const QString &name)
void TimelineMarkerList::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement(QStringLiteral("markers"));
foreach (TimelineMarker* marker, markers_) {
writer->writeStartElement(QStringLiteral("marker"));
@@ -67,8 +65,6 @@ void TimelineMarkerList::Save(QXmlStreamWriter *writer) const
writer->writeEndElement(); // marker
}
writer->writeEndElement(); // markers
}
TimelineMarkerList::~TimelineMarkerList()
+6 -6
View File
@@ -54,13 +54,13 @@ void TimelinePoints::Load(QXmlStreamReader *reader)
void TimelinePoints::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement(QStringLiteral("points"));
writer->writeStartElement(QStringLiteral("workarea"));
workarea_.Save(writer);
writer->writeEndElement(); // workarea
workarea_.Save(writer);
markers_.Save(writer);
writer->writeEndElement(); // points
writer->writeStartElement(QStringLiteral("markers"));
markers_.Save(writer);
writer->writeEndElement(); // markers
}
TimelineWorkArea *TimelinePoints::workarea()
-4
View File
@@ -81,13 +81,9 @@ void TimelineWorkArea::Load(QXmlStreamReader *reader)
void TimelineWorkArea::Save(QXmlStreamWriter *writer) const
{
writer->writeStartElement(QStringLiteral("workarea"));
writer->writeAttribute(QStringLiteral("enabled"), QString::number(workarea_enabled_));
writer->writeAttribute(QStringLiteral("in"), workarea_range_.in().toString());
writer->writeAttribute(QStringLiteral("out"), workarea_range_.out().toString());
writer->writeEndElement(); // workarea
}
const rational &TimelineWorkArea::in() const
+18 -3
View File
@@ -23,6 +23,7 @@
#include <QMessageBox>
#include "core.h"
#include "node/factory.h"
#include "widget/nodeview/nodeviewundo.h"
#include "window/mainwindow/mainwindow.h"
@@ -39,10 +40,15 @@ void NodeCopyPasteWidget::CopyNodesToClipboard(const QList<Node *> &nodes, void
writer.writeStartElement(QStringLiteral("olive"));
foreach (Node* n, nodes) {
writer.writeStartElement(QStringLiteral("node"));
writer.writeAttribute(QStringLiteral("id"), n->id());
n->Save(&writer);
writer.writeEndElement(); // node
}
writer.writeStartElement(QStringLiteral("custom"));
CopyNodesToClipboardInternal(&writer, userdata);
writer.writeEndElement(); // custom
writer.writeEndElement(); // olive
writer.writeEndDocument();
@@ -67,15 +73,24 @@ QList<Node *> NodeCopyPasteWidget::PasteNodesFromClipboard(Sequence *graph, QUnd
if (reader.name() == QStringLiteral("olive")) {
while (XMLReadNextStartElement(&reader)) {
if (reader.name() == QStringLiteral("node")) {
Node* node = XMLLoadNode(&reader);
Node* node = nullptr;
XMLAttributeLoop((&reader), attr) {
if (attr.name() == QStringLiteral("id")) {
node = NodeFactory::CreateFromID(attr.value().toString());
break;
}
}
if (node) {
node->Load(&reader, xml_node_data, nullptr);
pasted_nodes.append(node);
}
} else if (reader.name() == QStringLiteral("custom")) {
PasteNodesFromClipboardInternal(&reader, xml_node_data, userdata);
} else {
PasteNodesFromClipboardInternal(&reader, userdata);
reader.skipCurrentElement();
}
}
} else {
@@ -157,7 +172,7 @@ void NodeCopyPasteWidget::CopyNodesToClipboardInternal(QXmlStreamWriter*, void*)
{
}
void NodeCopyPasteWidget::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, void*)
void NodeCopyPasteWidget::PasteNodesFromClipboardInternal(QXmlStreamReader* reader, XMLNodeData &xml_node_data, void*)
{
reader->skipCurrentElement();
}
+1 -1
View File
@@ -41,7 +41,7 @@ protected:
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata);
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, void* userdata);
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata);
};
+25 -43
View File
@@ -285,8 +285,6 @@ void TimelineWidget::DisconnectNodeInternal(ViewerOutput *n)
void TimelineWidget::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata)
{
writer->writeStartElement(QStringLiteral("timeline"));
// Cache the earliest in point so all copied clips have a "relative" in point that can be pasted anywhere
QList<TimelineViewBlockItem*>& selected = *static_cast<QList<TimelineViewBlockItem*>*>(userdata);
rational earliest_in = RATIONAL_MAX;
@@ -314,38 +312,32 @@ void TimelineWidget::CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void
writer->writeEndElement();
}
writer->writeEndElement(); // timeline
}
void TimelineWidget::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, void *userdata)
void TimelineWidget::PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData& xml_node_data, void *userdata)
{
if (reader->name() == QStringLiteral("timeline")) {
QList<BlockPasteData>& paste_data = *static_cast<QList<BlockPasteData>*>(userdata);
QList<BlockPasteData>& paste_data = *static_cast<QList<BlockPasteData>*>(userdata);
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("block")) {
BlockPasteData bpd;
while (XMLReadNextStartElement(reader)) {
if (reader->name() == QStringLiteral("block")) {
BlockPasteData bpd;
foreach (QXmlStreamAttribute attr, reader->attributes()) {
if (attr.name() == QStringLiteral("ptr")) {
bpd.ptr = attr.value().toULongLong();
} else if (attr.name() == QStringLiteral("in")) {
bpd.in = rational::fromString(attr.value().toString());
} else if (attr.name() == QStringLiteral("tracktype")) {
bpd.track_type = static_cast<Timeline::TrackType>(attr.value().toInt());
} else if (attr.name() == QStringLiteral("trackindex")) {
bpd.track_index = attr.value().toInt();
}
foreach (QXmlStreamAttribute attr, reader->attributes()) {
if (attr.name() == QStringLiteral("ptr")) {
bpd.block = static_cast<Block*>(xml_node_data.node_ptrs.value(attr.value().toULongLong()));
} else if (attr.name() == QStringLiteral("in")) {
bpd.in = rational::fromString(attr.value().toString());
} else if (attr.name() == QStringLiteral("tracktype")) {
bpd.track_type = static_cast<Timeline::TrackType>(attr.value().toInt());
} else if (attr.name() == QStringLiteral("trackindex")) {
bpd.track_index = attr.value().toInt();
}
paste_data.append(bpd);
reader->skipCurrentElement();
}
paste_data.append(bpd);
reader->skipCurrentElement();
}
} else {
NodeCopyPasteWidget::PasteNodesFromClipboardInternal(reader, userdata);
}
}
@@ -681,12 +673,7 @@ void TimelineWidget::Paste(bool insert)
rational paste_end = GetTime();
foreach (const BlockPasteData& bpd, paste_data) {
foreach (Node* n, pasted) {
if (n->property("xml_ptr") == bpd.ptr) {
paste_end = qMax(paste_end, paste_start + bpd.in + static_cast<Block*>(n)->length());
break;
}
}
paste_end = qMax(paste_end, paste_start + bpd.in + bpd.block->length());
}
if (paste_end != paste_start) {
@@ -695,17 +682,12 @@ void TimelineWidget::Paste(bool insert)
}
foreach (const BlockPasteData& bpd, paste_data) {
foreach (Node* n, pasted) {
if (n->property("xml_ptr") == bpd.ptr) {
qDebug() << "Placing" << n;
new TrackPlaceBlockCommand(GetConnectedNode()->track_list(bpd.track_type),
bpd.track_index,
static_cast<Block*>(n),
paste_start + bpd.in,
command);
break;
}
}
qDebug() << "Placing" << bpd.block;
new TrackPlaceBlockCommand(GetConnectedNode()->track_list(bpd.track_type),
bpd.track_index,
bpd.block,
paste_start + bpd.in,
command);
}
Core::instance()->undo_stack()->pushIfHasChildren(command);
+2 -2
View File
@@ -119,10 +119,10 @@ protected:
virtual void DisconnectNodeInternal(ViewerOutput* n) override;
virtual void CopyNodesToClipboardInternal(QXmlStreamWriter *writer, void* userdata) override;
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, void* userdata) override;
virtual void PasteNodesFromClipboardInternal(QXmlStreamReader *reader, XMLNodeData &xml_node_data, void* userdata) override;
struct BlockPasteData {
quintptr ptr;
Block* block;
rational in;
Timeline::TrackType track_type;
int track_index;