Skip to content

Commit

Permalink
fixes #1635, fix flatten stack splits contiguous clips issue and add …
Browse files Browse the repository at this point in the history
…tests

Signed-off-by: Ramesh Maddhu <[email protected]>
Signed-off-by: Mark Reid <[email protected]>
  • Loading branch information
rameshbabuvfxtd authored and rameshm committed Dec 12, 2024
1 parent ee3e317 commit e875424
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 31 deletions.
105 changes: 75 additions & 30 deletions src/opentimelineio/stackAlgorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ namespace opentimelineio { namespace OPENTIMELINEIO_VERSION {
typedef std::map<Track*, std::map<Composable*, TimeRange>> RangeTrackMap;
typedef std::vector<SerializableObject::Retainer<Track>> TrackRetainerVector;

static bool
_is_not_visible(
SerializableObject::Retainer<Composable> thing)
{
auto item = dynamic_retainer_cast<Item>(thing);
if (!item)
return false;

return !item->visible();
}

static void
_flatten_next_item(
RangeTrackMap& range_track_map,
Expand Down Expand Up @@ -61,54 +72,88 @@ _flatten_next_item(
}
track_map = &result.first->second;
}
for (auto child: track->children())

auto children = track->children();
for (size_t i = 0; i < children.size(); i++)
{
auto item = dynamic_retainer_cast<Item>(child);
if (!item)
std::optional<TimeRange> trim = std::nullopt;
SerializableObject::Retainer<Composable> child = children[i];

// combine all non visible children into one continuous range
while(track_index > 0 && _is_not_visible(child))
{
if (!dynamic_retainer_cast<Transition>(child))
TimeRange child_trim = (*track_map)[child];

// offset ranges so they are in track range before being trimmed
if (trim_range)
{
if (error_status)
{
*error_status = ErrorStatus(
ErrorStatus::TYPE_MISMATCH,
"expected item of type Item* || Transition*",
child);
}
return;
trim = TimeRange(
child_trim.start_time() + trim_range->start_time(),
child_trim.duration());
(*track_map)[child] = child_trim;
}

if (trim.has_value())
{
trim = trim->duration_extended_by(child_trim.duration());
}
else
{
trim = child_trim;
}

if (i+1 < children.size())
{
child = children[i++];
}
else
{
// last item is not visible
child = nullptr;
break;
}
}

if (!item || item->visible() || track_index == 0)
if (trim.has_value())
{
flat_track->insert_child(
static_cast<int>(flat_track->children().size()),
static_cast<Composable*>(child->clone(error_status)),
_flatten_next_item(
range_track_map,
flat_track,
tracks,
track_index - 1,
trim,
error_status);

if (is_error(error_status))
{
return;
}
}
else

if (child)
{
TimeRange trim = (*track_map)[item];
if (trim_range)
auto item = dynamic_retainer_cast<Item>(child);
if (!item)
{
trim = TimeRange(
trim.start_time() + trim_range->start_time(),
trim.duration());
(*track_map)[item] = trim;
if (!dynamic_retainer_cast<Transition>(child))
{
if (error_status)
{
*error_status = ErrorStatus(
ErrorStatus::TYPE_MISMATCH,
"expected item of type Item* || Transition*",
child);
}
return;
}
}

_flatten_next_item(
range_track_map,
flat_track,
tracks,
track_index - 1,
trim,
flat_track->insert_child(
static_cast<int>(flat_track->children().size()),
static_cast<Composable*>(child->clone(error_status)),
error_status);
if (track == nullptr || is_error(error_status))

if (is_error(error_status))
{
return;
}
Expand Down
106 changes: 105 additions & 1 deletion tests/test_stack_algo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <opentimelineio/clip.h>
#include <opentimelineio/stack.h>
#include <opentimelineio/track.h>
#include <opentimelineio/gap.h>
#include <opentimelineio/stackAlgorithm.h>

#include <iostream>
Expand Down Expand Up @@ -213,6 +214,109 @@ main(int argc, char** argv)
assertEqual(result->duration().value(), 300);
});

tests.add_test(
"test_flatten_stack_04", [] {
using namespace otio;

otio::RationalTime rt_0_24{0, 24};
otio::RationalTime rt_150_24{150, 24};
otio::TimeRange tr_AB_150_24{rt_0_24, rt_150_24};

otio::TimeRange tr_C_150_24{rt_0_24, otio::RationalTime(300, 24)};

// A and B are Gaps placed over clip C
// C should remain uncut
// 0 150 300
// [ A | B ]
// [ C ]
//
// should flatten to:
// [ C ]

otio::SerializableObject::Retainer<otio::Gap> cl_A =
new otio::Gap(tr_AB_150_24, "track1_A");
otio::SerializableObject::Retainer<otio::Gap> cl_B =
new otio::Gap(tr_AB_150_24, "track1_B");
otio::SerializableObject::Retainer<otio::Clip> cl_C =
new otio::Clip("track2_C", nullptr, tr_C_150_24);

otio::SerializableObject::Retainer<otio::Track> tr_over =
new otio::Track();
tr_over->append_child(cl_A);
tr_over->append_child(cl_B);

otio::SerializableObject::Retainer<otio::Track> tr_under =
new otio::Track();
tr_under->append_child(cl_C);

otio::SerializableObject::Retainer<otio::Stack> st =
new otio::Stack();
st->append_child(tr_under);
st->append_child(tr_over);

auto result = flatten_stack(st);

assertEqual(result->children().size(), 1);
assertEqual(result->children()[0]->name(), std::string("track2_C"));
assertEqual(result->duration().value(), 300);
assertEqual(st->children().size(), 2);
assertEqual(dynamic_cast<otio::Track*>(st->children()[0].value)->children().size(), 1);
assertEqual(dynamic_cast<otio::Track*>(st->children()[1].value)->children().size(), 2);
});

tests.add_test(
"test_flatten_stack_05", [] {
using namespace otio;

otio::RationalTime rt_0_24{0, 24};
otio::RationalTime rt_150_24{150, 24};
otio::TimeRange tr_150_24{rt_0_24, rt_150_24};

// A and B are Gaps placed over clips C and D
// with a cut at the same location
// 0 150 300
// [ A | B ]
// [ C | D ]
//
// should flatten to:
// [ C | D ]

otio::SerializableObject::Retainer<otio::Gap> cl_A =
new otio::Gap(tr_150_24, "track1_A");
otio::SerializableObject::Retainer<otio::Gap> cl_B =
new otio::Gap(tr_150_24, "track1_B");
otio::SerializableObject::Retainer<otio::Clip> cl_C =
new otio::Clip("track2_C", nullptr, tr_150_24);
otio::SerializableObject::Retainer<otio::Clip> cl_D =
new otio::Clip("track2_D", nullptr, tr_150_24);


otio::SerializableObject::Retainer<otio::Track> tr_over =
new otio::Track();
tr_over->append_child(cl_A);
tr_over->append_child(cl_B);

otio::SerializableObject::Retainer<otio::Track> tr_under =
new otio::Track();
tr_under->append_child(cl_C);
tr_under->append_child(cl_D);

otio::SerializableObject::Retainer<otio::Stack> st =
new otio::Stack();
st->append_child(tr_under);
st->append_child(tr_over);

auto result = flatten_stack(st);

assertEqual(result->children().size(), 2);
assertEqual(result->children()[0]->name(), std::string("track2_C"));
assertEqual(result->children()[1]->name(), std::string("track2_D"));
assertEqual(result->duration().value(), 300);
assertEqual(st->children().size(), 2);
assertEqual(dynamic_cast<otio::Track*>(st->children()[0].value)->children().size(), 2);
assertEqual(dynamic_cast<otio::Track*>(st->children()[1].value)->children().size(), 2);
});

tests.run(argc, argv);
return 0;
}
}

0 comments on commit e875424

Please sign in to comment.