Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Media Multi-Reference Feature #1241

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/tutorials/otio-serialized-schema-only-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,13 +131,14 @@ parameters:

## Module: opentimelineio.schema

### Clip.1
### Clip.2

parameters:
- *active_media_reference*
- *effects*
- *enabled*
- *markers*
- *media_reference*
- *media_references*
- *metadata*
- *name*
- *source_range*
Expand Down
5 changes: 3 additions & 2 deletions docs/tutorials/otio-serialized-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ parameters:

## Module: opentimelineio.schema

### Clip.1
### Clip.2

*full module path*: `opentimelineio.schema.Clip`

Expand All @@ -271,10 +271,11 @@ None
```

parameters:
- *active_media_reference*:
- *effects*:
- *enabled*: If true, an Item contributes to compositions. Analogous to Mute in various NLEs.
- *markers*:
- *media_reference*:
- *media_references*:
- *metadata*:
- *name*:
- *source_range*:
Expand Down
70 changes: 59 additions & 11 deletions src/opentimelineio/clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@

namespace opentimelineio { namespace OPENTIMELINEIO_VERSION {

char constexpr Clip::MediaRepresentation::default_media[];
char constexpr Clip::MediaRepresentation::high_resolution_media[];
char constexpr Clip::MediaRepresentation::proxy_resolution_media[];

Clip::Clip(
std::string const& name,
MediaReference* media_reference,
optional<TimeRange> const& source_range,
AnyDictionary const& metadata)
AnyDictionary const& metadata,
std::string const& active_media_reference)
: Parent{ name, source_range, metadata }
, _active_media_reference(active_media_reference)
{
set_media_reference(media_reference);
}
Expand All @@ -19,33 +25,74 @@ Clip::~Clip()
MediaReference*
Clip::media_reference() const noexcept
{
return _media_reference;
auto active = _media_references.find(_active_media_reference);
return active == _media_references.end() || !active->second
? nullptr
: active->second;
}

Clip::MediaReferences
Clip::media_references() const noexcept
rogernelson marked this conversation as resolved.
Show resolved Hide resolved
{
MediaReferences result;
for (auto const& m: _media_references)
{
result.insert(
{ m.first, dynamic_retainer_cast<MediaReference>(m.second) });
}
meshula marked this conversation as resolved.
Show resolved Hide resolved
return result;
}

void
Clip::set_media_references(MediaReferences const& media_references) noexcept
rogernelson marked this conversation as resolved.
Show resolved Hide resolved
{
_media_references.clear();
for (auto const& m: media_references)
{
_media_references[m.first] = m.second;
meshula marked this conversation as resolved.
Show resolved Hide resolved
}
}

std::string
Clip::active_media_reference() const noexcept
{
return _active_media_reference;
}

void
Clip::set_active_media_reference(std::string const& new_active) noexcept
rogernelson marked this conversation as resolved.
Show resolved Hide resolved
{
_active_media_reference = new_active;
}

void
Clip::set_media_reference(MediaReference* media_reference)
{
_media_reference = media_reference ? media_reference : new MissingReference;
_media_references[_active_media_reference] =
media_reference ? media_reference : new MissingReference;
}

bool
Clip::read_from(Reader& reader)
{
return reader.read("media_reference", &_media_reference) &&
return reader.read("media_references", &_media_references) &&
reader.read("active_media_reference", &_active_media_reference) &&
Parent::read_from(reader);
}

void
Clip::write_to(Writer& writer) const
{
Parent::write_to(writer);
writer.write("media_reference", _media_reference);
writer.write("media_references", _media_references);
writer.write("active_media_reference", _active_media_reference);
}

TimeRange
Clip::available_range(ErrorStatus* error_status) const
{
if (!_media_reference)
auto active_media = media_reference();
if (!active_media)
meshula marked this conversation as resolved.
Show resolved Hide resolved
{
if (error_status)
{
Expand All @@ -57,7 +104,7 @@ Clip::available_range(ErrorStatus* error_status) const
return TimeRange();
}

if (!_media_reference->available_range())
if (!active_media->available_range())
{
if (error_status)
{
Expand All @@ -69,13 +116,14 @@ Clip::available_range(ErrorStatus* error_status) const
return TimeRange();
}

return _media_reference->available_range().value();
return active_media->available_range().value();
}

optional<Imath::Box2d>
Clip::available_image_bounds(ErrorStatus* error_status) const
{
if (!_media_reference)
auto active_media = media_reference();
if (!active_media)
meshula marked this conversation as resolved.
Show resolved Hide resolved
{
*error_status = ErrorStatus(
ErrorStatus::CANNOT_COMPUTE_BOUNDS,
Expand All @@ -84,7 +132,7 @@ Clip::available_image_bounds(ErrorStatus* error_status) const
return optional<Imath::Box2d>();
}

if (!_media_reference.value->available_image_bounds())
if (!active_media->available_image_bounds())
{
*error_status = ErrorStatus(
ErrorStatus::CANNOT_COMPUTE_BOUNDS,
Expand All @@ -93,7 +141,7 @@ Clip::available_image_bounds(ErrorStatus* error_status) const
return optional<Imath::Box2d>();
}

return _media_reference.value->available_image_bounds();
return active_media->available_image_bounds();
}

}} // namespace opentimelineio::OPENTIMELINEIO_VERSION
28 changes: 23 additions & 5 deletions src/opentimelineio/clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,18 @@ namespace opentimelineio { namespace OPENTIMELINEIO_VERSION {
class Clip : public Item
{
public:
struct MediaRepresentation
rogernelson marked this conversation as resolved.
Show resolved Hide resolved
{
static char constexpr default_media[] = "DEFAULT_MEDIA";
static char constexpr high_resolution_media[] = "HIGH_RESOLUTION_MEDIA";
static char constexpr proxy_resolution_media[] =
"PROXY_RESOLUTION_MEDIA";
};
rogernelson marked this conversation as resolved.
Show resolved Hide resolved

struct Schema
{
static auto constexpr name = "Clip";
static int constexpr version = 1;
static int constexpr version = 2;
};

using Parent = Item;
Expand All @@ -21,12 +29,21 @@ class Clip : public Item
std::string const& name = std::string(),
MediaReference* media_reference = nullptr,
optional<TimeRange> const& source_range = nullopt,
AnyDictionary const& metadata = AnyDictionary());

void set_media_reference(MediaReference* media_reference);
AnyDictionary const& metadata = AnyDictionary(),
std::string const& active_media_reference =
MediaRepresentation::default_media);

void set_media_reference(MediaReference* media_reference);
MediaReference* media_reference() const noexcept;

using MediaReferences = std::map<std::string, MediaReference*>;

MediaReferences media_references() const noexcept;
void set_media_references(MediaReferences const& media_references) noexcept;

std::string active_media_reference() const noexcept;
void set_active_media_reference(std::string const& new_active) noexcept;
rogernelson marked this conversation as resolved.
Show resolved Hide resolved

virtual TimeRange
available_range(ErrorStatus* error_status = nullptr) const;

Expand All @@ -40,7 +57,8 @@ class Clip : public Item
virtual void write_to(Writer&) const;

private:
Retainer<MediaReference> _media_reference;
std::map<std::string, Retainer<MediaReference>> _media_references;
std::string _active_media_reference;
};

}} // namespace opentimelineio::OPENTIMELINEIO_VERSION
21 changes: 21 additions & 0 deletions src/opentimelineio/typeRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,27 @@ TypeRegistry::TypeRegistry()
(*d)["marked_range"] = (*d)["range"];
d->erase("range");
});

register_upgrade_function(Clip::Schema::name, 2, [](AnyDictionary* d) {
auto media_ref = (*d)["media_reference"];

// The default ctor of Clip used to set media_reference to
// MissingReference. To preserve the same behaviour, if we don't have a
// valid MediaReference, do it here too.
if (media_ref.type() != typeid(SerializableObject::Retainer<>))
{
media_ref = SerializableObject::Retainer<>(new MissingReference);
}

(*d)["media_references"] = AnyDictionary{
{ Clip::MediaRepresentation::default_media, media_ref }
};

(*d)["active_media_reference"] =
std::string(Clip::MediaRepresentation::default_media);

d->erase("media_reference");
});
}

bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,16 +402,28 @@ static void define_items_and_compositions(py::module m) {
"markers"_a = py::none(),
py::arg_v("metadata"_a = py::none()));

py::class_<Clip, Item, managing_ptr<Clip>>(m, "Clip", py::dynamic_attr())
auto clip_class = py::class_<Clip, Item, managing_ptr<Clip>>(m, "Clip", py::dynamic_attr())
.def(py::init([](std::string name, MediaReference* media_reference,
optional<TimeRange> source_range, py::object metadata) {
return new Clip(name, media_reference, source_range, py_to_any_dictionary(metadata));
optional<TimeRange> source_range, py::object metadata,
const std::string& active_media_reference) {
return new Clip(name, media_reference, source_range, py_to_any_dictionary(metadata), active_media_reference);
}),
py::arg_v("name"_a = std::string()),
"media_reference"_a = nullptr,
"source_range"_a = nullopt,
py::arg_v("metadata"_a = py::none()))
.def_property("media_reference", &Clip::media_reference, &Clip::set_media_reference);
py::arg_v("metadata"_a = py::none()),
"active_media_reference"_a = std::string(Clip::MediaRepresentation::default_media))
.def_property("media_reference", &Clip::media_reference, &Clip::set_media_reference)
.def_property("media_references", &Clip::media_references, &Clip::set_media_references)
.def_property("active_media_reference", &Clip::active_media_reference, &Clip::set_active_media_reference);

py::class_<Clip::MediaRepresentation>(clip_class, "MediaRepresentation")
.def_property_readonly_static("DEFAULT_MEDIA",[](py::object /* self */) {
return Clip::MediaRepresentation::default_media; })
.def_property_readonly_static("HIGH_RESOLUTION_MEDIA", [](py::object /* self */) {
return Clip::MediaRepresentation::high_resolution_media; })
.def_property_readonly_static("PROXY_RESOLUTION_MEDIA", [](py::object /* self */) {
return Clip::MediaRepresentation::proxy_resolution_media; });

using CompositionIterator = ContainerIterator<Composition, Composable*>;
py::class_<CompositionIterator>(m, "CompositionIterator")
Expand Down
1 change: 1 addition & 0 deletions src/py-opentimelineio/opentimelineio/schema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
TrackKind = Track.Kind
TransitionTypes = Transition.Type
NeighborGapPolicy = Track.NeighborGapPolicy
ClipMediaRepresentation = Clip.MediaRepresentation

from . schemadef import (
SchemaDef
Expand Down
12 changes: 10 additions & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ foreach(test ${tests_opentime})
add_executable(${test} utils.h utils.cpp ${test}.cpp)
target_link_libraries(${test} opentime)
set_target_properties(${test} PROPERTIES FOLDER tests)
add_test(${test} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}${CMAKE_EXECUTABLE_SUFFIX})
add_test(NAME ${test}
COMMAND ${test}
# Set the pwd to the source directory so we can load the samples
# like the python tests do
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sample_data)
rogernelson marked this conversation as resolved.
Show resolved Hide resolved
endforeach()

list(APPEND tests_opentimelineio test_clip)
foreach(test ${tests_opentimelineio})
add_executable(${test} utils.h utils.cpp ${test}.cpp)
target_link_libraries(${test} opentimelineio)
set_target_properties(${test} PROPERTIES FOLDER tests)
add_test(${test} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${test}${CMAKE_EXECUTABLE_SUFFIX})
add_test(NAME ${test}
COMMAND ${test}
# Set the pwd to the source directory so we can load the samples
# like the python tests do
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/sample_data)
endforeach()
9 changes: 6 additions & 3 deletions tests/baselines/empty_clip.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
{
"OTIO_SCHEMA" : "Clip.1",
"OTIO_SCHEMA" : "Clip.2",
"metadata" : {},
"name" : "test_clip",
"source_range" : null,
"markers" : [],
"enabled" : true,
"effects" : [],
"media_reference" : {
"FROM_TEST_FILE" : "empty_missingreference.json"
"active_media_reference": "DEFAULT_MEDIA",
"media_references" : {
"DEFAULT_MEDIA" : {
"FROM_TEST_FILE" : "empty_missingreference.json"
}
}
}
Loading