diff --git a/app/codec/otio/otiodecoder.cpp b/app/codec/otio/otiodecoder.cpp index 2485b9733..eb2fa06bc 100644 --- a/app/codec/otio/otiodecoder.cpp +++ b/app/codec/otio/otiodecoder.cpp @@ -20,8 +20,14 @@ #include "otiodecoder.h" +#include +#include #include +#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(); + + // 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(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 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(it->first); + + /* + if (otio_clip->media_reference()->schema_name() == "ExternalReference") { + QString footage_url = QString::fromStdString(static_cast(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; diff --git a/app/node/node.h b/app/node/node.h index ba05359ce..49d3c8738 100644 --- a/app/node/node.h +++ b/app/node/node.h @@ -276,11 +276,17 @@ public: */ QList TransformTimeTo(const TimeRange& time, Node* target, NodeParam::Type direction); + /** + * @brief Find nodes of a certain type that this Node takes inputs from + */ + template + QList FindInputNodes() const; + template /** * @brief Find a node of a certain type that this Node outputs to */ - T* FindOutputNode(); + QList FindOutputNode(); /** * @brief Convert a pointer to a value that can be sent between NodeParams @@ -471,6 +477,12 @@ private: void DisconnectInput(NodeInput* input); + template + static void FindInputNodeInternal(const Node* n, QList& list); + + template + static void FindOutputNodeInternal(const Node* n, QList& list); + QList GetDependenciesInternal(bool traverse, bool exclusive_only) const; QList params_; @@ -497,6 +509,35 @@ private: }; +template +void Node::FindInputNodeInternal(const Node* n, QList& list) +{ + QList inputs = n->GetInputsIncludingArrays(); + + foreach (NodeInput* input, inputs) { + if (input->is_connected()) { + Node* connected = input->get_connected_node(); + T* cast_test = dynamic_cast(connected); + + if (cast_test) { + list.append(cast_test); + } + + FindInputNodeInternal(connected, list); + } + } +} + +template +QList Node::FindInputNodes() const +{ + QList list; + + FindInputNodeInternal(this, list); + + return list; +} + template T* Node::ValueToPtr(const QVariant &ptr) { @@ -504,28 +545,27 @@ T* Node::ValueToPtr(const QVariant &ptr) } template -Node* FindOutputNodeInternal(Node* n) { +void Node::FindOutputNodeInternal(const Node* n, QList& list) { foreach (NodeEdgePtr edge, n->output()->edges()) { Node* connected = edge->input()->parentNode(); T* cast_test = dynamic_cast(connected); if (cast_test) { - return cast_test; - } else { - Node* drill_test = FindOutputNodeInternal(connected); - if (drill_test) { - return drill_test; - } + list.append(cast_test); } - } - return nullptr; + FindOutputNodeInternal(connected); + } } template -T* Node::FindOutputNode() +QList Node::FindOutputNode() { - return static_cast(FindOutputNodeInternal(this)); + QList list; + + FindOutputNodeInternal(this, list); + + return list; } OLIVE_NAMESPACE_EXIT diff --git a/app/task/project/exportotio/exportotiotask.cpp b/app/task/project/exportotio/exportotiotask.cpp index 5acef888b..193f1f84b 100644 --- a/app/task/project/exportotio/exportotiotask.cpp +++ b/app/task/project/exportotio/exportotiotask.cpp @@ -21,11 +21,12 @@ #include "exportotiotask.h" #include +#include #include -#include #include #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(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 media_nodes = block->FindInputNodes(); + 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(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 diff --git a/app/task/project/exportotio/exportotiotask.h b/app/task/project/exportotio/exportotiotask.h index 5f5f77684..e7defe029 100644 --- a/app/task/project/exportotio/exportotiotask.h +++ b/app/task/project/exportotio/exportotiotask.h @@ -21,6 +21,9 @@ #ifndef PROJECTSAVEASOTIOTASK_H #define PROJECTSAVEASOTIOTASK_H +#include +#include + #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_;