otio: implemented some basic import/export functionality

Certainly not complete, but mild import/export capability.
This commit is contained in:
itsmattkc
2020-09-27 00:39:51 +10:00
parent 6db591e89f
commit b220dd88e7
4 changed files with 223 additions and 60 deletions
+70 -1
View File
@@ -20,8 +20,14 @@
#include "otiodecoder.h"
#include <opentimelineio/clip.h>
#include <opentimelineio/externalReference.h>
#include <opentimelineio/timeline.h>
#include "node/block/clip/clip.h"
#include "node/block/gap/gap.h"
#include "project/item/sequence/sequence.h"
OLIVE_NAMESPACE_ENTER
OTIODecoder::OTIODecoder()
@@ -45,7 +51,70 @@ ItemPtr OTIODecoder::Probe(const QString& filename, const QAtomicInt* cancelled)
return nullptr;
}
qDebug() << "Found" << timeline->video_tracks().size() << "video tracks";
SequencePtr sequence = std::make_shared<Sequence>();
// FIXME: As far as I know, OTIO doesn't store video/audio parameters?
sequence->set_default_parameters();
sequence->set_name(QString::fromStdString(timeline->name()));
for (auto c : timeline->tracks()->children()) {
auto otio_track = static_cast<opentimelineio::v1_0::Track*>(c.value);
// Create a new track
TrackOutput* track = nullptr;
// Determine what kind of track it is
if (otio_track->kind() == "Video") {
track = sequence->viewer_output()->track_list(Timeline::kTrackTypeVideo)->AddTrack();
} else if (otio_track->kind() == "Audio") {
track = sequence->viewer_output()->track_list(Timeline::kTrackTypeAudio)->AddTrack();
} else {
qWarning() << "Found unknown track type:" << otio_track->kind().c_str();
continue;
}
// Get clips from track
std::map<opentimelineio::v1_0::Composable*, opentimelineio::v1_0::TimeRange> clip_map = otio_track->range_of_all_children(&es);
if (es != opentimelineio::v1_0::ErrorStatus::OK) {
return nullptr;
}
for (auto it=clip_map.cbegin(); it!=clip_map.cend(); it++) {
Block* block = nullptr;
if (it->first->schema_name() == "Clip") {
block = new ClipBlock();
auto otio_clip = static_cast<opentimelineio::v1_0::Clip*>(it->first);
/*
if (otio_clip->media_reference()->schema_name() == "ExternalReference") {
QString footage_url = QString::fromStdString(static_cast<opentimelineio::v1_0::ExternalReference*>(otio_clip->media_reference())->target_url());
ItemPtr footage = Decoder::ProbeMedia(footage_url, cancelled);
}
*/
} else if (it->first->schema_name() == "Gap") {
block = new GapBlock();
} else {
qWarning() << "Found unknown block type:" << it->first->schema_name().c_str();
}
block->SetLabel(QString::fromStdString(it->first->name()));
block->set_length_and_media_out(rational::fromDouble(it->second.duration().to_seconds()));
sequence->AddNode(block);
track->AppendBlock(block);
}
}
return sequence;
}
return nullptr;
+52 -12
View File
@@ -276,11 +276,17 @@ public:
*/
QList<TimeRange> TransformTimeTo(const TimeRange& time, Node* target, NodeParam::Type direction);
/**
* @brief Find nodes of a certain type that this Node takes inputs from
*/
template<class T>
QList<T*> FindInputNodes() const;
template<class T>
/**
* @brief Find a node of a certain type that this Node outputs to
*/
T* FindOutputNode();
QList<T *> FindOutputNode();
/**
* @brief Convert a pointer to a value that can be sent between NodeParams
@@ -471,6 +477,12 @@ private:
void DisconnectInput(NodeInput* input);
template<class T>
static void FindInputNodeInternal(const Node* n, QList<T *>& list);
template<class T>
static void FindOutputNodeInternal(const Node* n, QList<T *>& list);
QList<Node *> GetDependenciesInternal(bool traverse, bool exclusive_only) const;
QList<NodeParam *> params_;
@@ -497,6 +509,35 @@ private:
};
template<class T>
void Node::FindInputNodeInternal(const Node* n, QList<T *>& list)
{
QList<NodeInput*> inputs = n->GetInputsIncludingArrays();
foreach (NodeInput* input, inputs) {
if (input->is_connected()) {
Node* connected = input->get_connected_node();
T* cast_test = dynamic_cast<T*>(connected);
if (cast_test) {
list.append(cast_test);
}
FindInputNodeInternal<T>(connected, list);
}
}
}
template<class T>
QList<T *> Node::FindInputNodes() const
{
QList<T *> list;
FindInputNodeInternal<T>(this, list);
return list;
}
template<class T>
T* Node::ValueToPtr(const QVariant &ptr)
{
@@ -504,28 +545,27 @@ T* Node::ValueToPtr(const QVariant &ptr)
}
template<class T>
Node* FindOutputNodeInternal(Node* n) {
void Node::FindOutputNodeInternal(const Node* n, QList<T *>& list) {
foreach (NodeEdgePtr edge, n->output()->edges()) {
Node* connected = edge->input()->parentNode();
T* cast_test = dynamic_cast<T*>(connected);
if (cast_test) {
return cast_test;
} else {
Node* drill_test = FindOutputNodeInternal<T>(connected);
if (drill_test) {
return drill_test;
}
list.append(cast_test);
}
}
return nullptr;
FindOutputNodeInternal<T>(connected);
}
}
template<class T>
T* Node::FindOutputNode()
QList<T *> Node::FindOutputNode()
{
return static_cast<T*>(FindOutputNodeInternal<T>(this));
QList<T *> list;
FindOutputNodeInternal<T>(this, list);
return list;
}
OLIVE_NAMESPACE_EXIT
+94 -47
View File
@@ -21,11 +21,12 @@
#include "exportotiotask.h"
#include <opentimelineio/clip.h>
#include <opentimelineio/externalReference.h>
#include <opentimelineio/gap.h>
#include <opentimelineio/timeline.h>
#include <opentimelineio/transition.h>
#include "node/block/transition/transition.h"
#include "node/input/media/media.h"
OLIVE_NAMESPACE_ENTER
@@ -42,59 +43,107 @@ bool ExportOTIOTask::Run()
opentimelineio::v1_0::ErrorStatus es;
foreach (TrackOutput* track, sequence_->track_list(Timeline::kTrackTypeVideo)->GetTracks()) {
auto otio_track = new opentimelineio::v1_0::Track();
if (!SerializeTrackList(sequence_->track_list(Timeline::kTrackTypeVideo), otio_timeline)
|| !SerializeTrackList(sequence_->track_list(Timeline::kTrackTypeAudio), otio_timeline)) {
return false;
}
otio_timeline->to_json_file(filename_.toStdString(), &es);
return (es == opentimelineio::v1_0::ErrorStatus::OK);
}
opentimelineio::v1_0::Track *ExportOTIOTask::SerializeTrack(TrackOutput *track)
{
auto otio_track = new opentimelineio::v1_0::Track();
opentimelineio::v1_0::ErrorStatus es;
switch (track->track_type()) {
case Timeline::kTrackTypeVideo:
otio_track->set_kind("Video");
break;
case Timeline::kTrackTypeAudio:
otio_track->set_kind("Audio");
break;
default:
qWarning() << "Don't know OTIO track kind for native type" << track->track_type();
goto fail;
}
foreach (Block* block, track->Blocks()) {
opentimelineio::v1_0::Composable* otio_block = nullptr;
foreach (Block* block, track->Blocks()) {
opentimelineio::v1_0::Composable* otio_block = nullptr;
switch (block->type()) {
case Block::kClip:
{
auto otio_clip = new opentimelineio::v1_0::Clip();
switch (block->type()) {
case Block::kClip:
{
auto otio_clip = new opentimelineio::v1_0::Clip();
otio_clip->set_source_range(opentimelineio::v1_0::TimeRange(block->in().toRationalTime(),
block->length().toRationalTime()));
otio_clip->set_source_range(opentimelineio::v1_0::TimeRange(block->in().toRationalTime(),
block->length().toRationalTime()));
otio_block = otio_clip;
break;
}
case Block::kGap:
{
auto otio_gap = new opentimelineio::v1_0::Gap();
otio_gap->set_source_range(opentimelineio::v1_0::TimeRange(block->in().toRationalTime(),
block->length().toRationalTime()));
otio_block = otio_gap;
break;
}
case Block::kTransition:
{
auto otio_transition = new opentimelineio::v1_0::Transition();
TransitionBlock* our_transition = static_cast<TransitionBlock*>(block);
otio_transition->set_in_offset(our_transition->in_offset().toRationalTime());
otio_transition->set_out_offset(our_transition->out_offset().toRationalTime());
otio_block = new opentimelineio::v1_0::Transition();
break;
}
QList<MediaInput*> media_nodes = block->FindInputNodes<MediaInput>();
if (!media_nodes.isEmpty()) {
auto media_ref = new opentimelineio::v1_0::ExternalReference(media_nodes.first()->footage()->footage()->filename().toStdString());
otio_clip->set_media_reference(media_ref);
}
if (!otio_block) {
// We shouldn't ever get here, but catch without crashing if we ever do
return false;
}
otio_block = otio_clip;
break;
}
case Block::kGap:
{
auto otio_gap = new opentimelineio::v1_0::Gap();
otio_track->append_child(otio_block, &es);
otio_gap->set_source_range(opentimelineio::v1_0::TimeRange(block->in().toRationalTime(),
block->length().toRationalTime()));
if (es != opentimelineio::v1_0::ErrorStatus::OK) {
return false;
}
otio_block = otio_gap;
break;
}
case Block::kTransition:
{
auto otio_transition = new opentimelineio::v1_0::Transition();
TransitionBlock* our_transition = static_cast<TransitionBlock*>(block);
otio_transition->set_in_offset(our_transition->in_offset().toRationalTime());
otio_transition->set_out_offset(our_transition->out_offset().toRationalTime());
otio_block = new opentimelineio::v1_0::Transition();
break;
}
}
if (!otio_block) {
// We shouldn't ever get here, but catch without crashing if we ever do
goto fail;
}
otio_track->append_child(otio_block, &es);
if (es != opentimelineio::v1_0::ErrorStatus::OK) {
goto fail;
}
}
return otio_track;
fail:
otio_track->possibly_delete();
return nullptr;
}
bool ExportOTIOTask::SerializeTrackList(TrackList *list, opentimelineio::v1_0::Timeline* otio_timeline)
{
opentimelineio::v1_0::ErrorStatus es;
foreach (TrackOutput* track, list->GetTracks()) {
auto otio_track = SerializeTrack(track);
if (!otio_track) {
return false;
}
otio_timeline->tracks()->append_child(otio_track, &es);
@@ -104,9 +153,7 @@ bool ExportOTIOTask::Run()
}
}
otio_timeline->to_json_file(filename_.toStdString(), &es);
return (es == opentimelineio::v1_0::ErrorStatus::OK);
return true;
}
OLIVE_NAMESPACE_EXIT
@@ -21,6 +21,9 @@
#ifndef PROJECTSAVEASOTIOTASK_H
#define PROJECTSAVEASOTIOTASK_H
#include <opentimelineio/timeline.h>
#include <opentimelineio/track.h>
#include "project/item/sequence/sequence.h"
#include "task/task.h"
@@ -36,6 +39,10 @@ protected:
virtual bool Run() override;
private:
opentimelineio::v1_0::Track* SerializeTrack(TrackOutput* track);
bool SerializeTrackList(TrackList* list, opentimelineio::v1_0::Timeline *otio_timeline);
ViewerOutput* sequence_;
QString filename_;