otio: implemented some basic import/export functionality
Certainly not complete, but mild import/export capability.
This commit is contained in:
@@ -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
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
Reference in New Issue
Block a user