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 all commits
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_key*
- *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_key*:
- *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
130 changes: 119 additions & 11 deletions src/opentimelineio/clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@

namespace opentimelineio { namespace OPENTIMELINEIO_VERSION {

char constexpr Clip::default_media_key[];

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_key)
: Parent{ name, source_range, metadata }
, _active_media_reference_key(active_media_reference_key)
{
set_media_reference(media_reference);
}
Expand All @@ -19,33 +23,136 @@ Clip::~Clip()
MediaReference*
Clip::media_reference() const noexcept
{
return _media_reference;
auto active = _media_references.find(_active_media_reference_key);
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;
}

template <typename MediaRefMap>
bool
Clip::check_for_valid_media_reference_key(
std::string const& caller,
std::string const& key,
MediaRefMap const& media_references,
ErrorStatus* error_status)
{
auto empty_key = media_references.find("");
if (empty_key != media_references.end())
{
if (error_status)
{
*error_status = ErrorStatus(
ErrorStatus::MEDIA_REFERENCES_CONTAIN_EMPTY_KEY,
caller +
" failed because the media references contain an empty string key",
this);
}
return false;
}

auto found = media_references.find(key);
meshula marked this conversation as resolved.
Show resolved Hide resolved
if (found == media_references.end())
{
if (error_status)
{
*error_status = ErrorStatus(
ErrorStatus::MEDIA_REFERENCES_DO_NOT_CONTAIN_ACTIVE_KEY,
caller +
" failed because the media references do not contain the active key",
this);
}
return false;
}
meshula marked this conversation as resolved.
Show resolved Hide resolved
return true;
meshula marked this conversation as resolved.
Show resolved Hide resolved
}

void
Clip::set_media_references(
MediaReferences const& media_references,
std::string const& new_active_key,
ErrorStatus* error_status) noexcept
{
if (!check_for_valid_media_reference_key(
meshula marked this conversation as resolved.
Show resolved Hide resolved
"set_media_references",
new_active_key,
media_references,
error_status))
{
return;
meshula marked this conversation as resolved.
Show resolved Hide resolved
}

_media_references.clear();
for (auto const& m: media_references)
{
_media_references[m.first] = m.second ? m.second : new MissingReference;
meshula marked this conversation as resolved.
Show resolved Hide resolved
}

_active_media_reference_key = new_active_key;
}

std::string
Clip::active_media_reference_key() const noexcept
{
return _active_media_reference_key;
}

void
Clip::set_active_media_reference_key(
std::string const& new_active_key, ErrorStatus* error_status) noexcept
{
if (!check_for_valid_media_reference_key(
"set_active_media_reference_key",
new_active_key,
_media_references,
error_status))
{
return;
meshula marked this conversation as resolved.
Show resolved Hide resolved
}
_active_media_reference_key = new_active_key;
}

void
Clip::set_media_reference(MediaReference* media_reference)
{
_media_reference = media_reference ? media_reference : new MissingReference;
_media_references[_active_media_reference_key] =
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_key", &_active_media_reference_key) &&
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_key", _active_media_reference_key);
}

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 +164,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 +176,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 +192,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 +201,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
40 changes: 32 additions & 8 deletions src/opentimelineio/clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,39 @@ namespace opentimelineio { namespace OPENTIMELINEIO_VERSION {
class Clip : public Item
{
public:
static char constexpr default_media_key[] = "DEFAULT_MEDIA";

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

using Parent = Item;

Clip(
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);
std::string const& name = std::string(),
MediaReference* media_reference = nullptr,
optional<TimeRange> const& source_range = nullopt,
AnyDictionary const& metadata = AnyDictionary(),
std::string const& active_media_reference_key = default_media_key);

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,
std::string const& new_active_key,
ErrorStatus* error_status = nullptr) noexcept;

std::string active_media_reference_key() const noexcept;
void set_active_media_reference_key(
std::string const& new_active_key,
ErrorStatus* error_status = nullptr) noexcept;

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

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

private:
Retainer<MediaReference> _media_reference;
template <typename MediaRefMap>
bool check_for_valid_media_reference_key(
std::string const& caller,
std::string const& key,
MediaRefMap const& media_references,
ErrorStatus* error_status);

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

}} // namespace opentimelineio::OPENTIMELINEIO_VERSION
4 changes: 4 additions & 0 deletions src/opentimelineio/errorStatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ ErrorStatus::outcome_to_string(Outcome o)
return "cannot trim transition";
case CANNOT_COMPUTE_BOUNDS:
return "cannot compute image bounds";
case MEDIA_REFERENCES_DO_NOT_CONTAIN_ACTIVE_KEY:
return "active key not found in media references";
case MEDIA_REFERENCES_CONTAIN_EMPTY_KEY:
return "the media referencess cannot contain an empty key";
default:
return "unknown/illegal ErrorStatus::Outcome code";
};
Expand Down
4 changes: 3 additions & 1 deletion src/opentimelineio/errorStatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ struct ErrorStatus
OBJECT_WITHOUT_DURATION,
CANNOT_TRIM_TRANSITION,
OBJECT_CYCLE,
CANNOT_COMPUTE_BOUNDS
CANNOT_COMPUTE_BOUNDS,
MEDIA_REFERENCES_DO_NOT_CONTAIN_ACTIVE_KEY,
MEDIA_REFERENCES_CONTAIN_EMPTY_KEY
};

ErrorStatus()
Expand Down
20 changes: 20 additions & 0 deletions src/opentimelineio/typeRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,26 @@ 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::default_media_key, media_ref } };

(*d)["active_media_reference_key"] =
std::string(Clip::default_media_key);

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

bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ ErrorStatusHandler::~ErrorStatusHandler() noexcept(false) {
throw _CannotComputeAvailableRangeException(full_details());
case ErrorStatus::OBJECT_CYCLE:
throw py::value_error("Detected SerializableObject cycle while copying/serializing: " + details());
case ErrorStatus::MEDIA_REFERENCES_DO_NOT_CONTAIN_ACTIVE_KEY:
throw py::value_error("The media references do not contain the active key");
case ErrorStatus::MEDIA_REFERENCES_CONTAIN_EMPTY_KEY:
throw py::value_error("The media references contain an empty key");
default:
throw py::value_error(full_details());
}
Expand Down
Loading