Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin' into 1635_fix_flatten_issue
Browse files Browse the repository at this point in the history
  • Loading branch information
rameshm committed Dec 6, 2024
2 parents 3510ade + 68d891a commit 44243bf
Show file tree
Hide file tree
Showing 18 changed files with 235 additions and 94 deletions.
14 changes: 14 additions & 0 deletions ADOPTERS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# OpenTimelineIO Adopters

Below is a partial list of organizations and projects that are using OpenTimelineIO. If you would like to be added to this list, please submit a pull request to this file.

| Name | Description |
|------|------------------------------------------------------------------------------------------------------------------------------------------------|
| [Adobe Premiere Pro](https://www.adobe.com/products/premiere.html) | [Timeline import/export](https://community.adobe.com/t5/premiere-pro-beta-discussions/new-in-beta-otio-import-and-export/td-p/14937493) (beta) |
| [AVID Media Composer](https://www.avid.com/media-composer) | Timeline export (preview) |
| [Black Magic Design DaVinci Resolve](https://www.blackmagicdesign.com/products/davinciresolve/) | Timeline import/export |
| [CineSync](https://www.backlight.co/product/cinesync) | Timeline viewing | |
| [ColorFront Transkoder](https://colorfront.com/index.php?page=SOFTWARE&spage=Transkoder) | Timeline import |
| [Nuke Studio](https://www.foundry.com/products/nuke) | Timeline import/export with full timeline round-tripping |
| [Hiero](https://www.foundry.com/products/nuke-family/hiero) | Timeline import/export |
| [OpenRV](https://github.com/AcademySoftwareFoundation/OpenRV) | Timeline import and viewing |
1 change: 1 addition & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exclude readthedocs-conda.yml
exclude .codecov.yml
exclude .gitlab-ci.yml
exclude *.pdf
exclude ADOPTERS.md
exclude CODE_OF_CONDUCT.md
exclude CONTRIBUTING.md
exclude CONTRIBUTORS.md
Expand Down
3 changes: 3 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
r'cxx/cxx'
]

# For some reason this URL gives 403 Forbidden when running in github actions
linkcheck_ignore = [r'https://opensource.org/licenses/MIT']

# -- Options for MySt-Parser -----------------------------------------------------------
# https://myst-parser.readthedocs.io/en/latest/sphinx/reference.html

Expand Down
4 changes: 2 additions & 2 deletions src/opentime/errorStatus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ ErrorStatus::outcome_to_string(Outcome o)
case OK:
return std::string();
case INVALID_TIMECODE_RATE:
return "invalid timecode rate";
return "SMPTE timecode does not support this rate";
case INVALID_TIMECODE_STRING:
return "string is not a valid timecode string";
return "string is not a SMPTE timecode string";
case TIMECODE_RATE_MISMATCH:
return "timecode specifies a frame higher than its rate";
case INVALID_TIME_STRING:
Expand Down
83 changes: 42 additions & 41 deletions src/opentime/rationalTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,75 +13,68 @@ namespace opentime { namespace OPENTIME_VERSION {

RationalTime RationalTime::_invalid_time{ 0, RationalTime::_invalid_rate };

static constexpr std::array<double, 4> dropframe_timecode_rates{ {
// 23.976,
// 23.98,
// 23.97,
// 24000.0/1001.0,
29.97,
static constexpr std::array<double, 2> dropframe_timecode_rates{ {
30000.0 / 1001.0,
59.94,
60000.0 / 1001.0,
} };

// See the official source of these numbers here:
// ST 12-1:2014 - SMPTE Standard - Time and Control Code
// https://ieeexplore.ieee.org/document/7291029
//
static constexpr std::array<double, 11> smpte_timecode_rates{
{ 1.0,
12.0,
24000.0 / 1001.0,
{ 24000.0 / 1001.0,
24.0,
25.0,
30000.0 / 1001.0,
30.0,
48000.0 / 1001.0,
48.0,
50.0,
60000.0 / 1001.0,
60.0 }
};

static constexpr std::array<double, 16> valid_timecode_rates{
{ 1.0,
12.0,
23.97,
23.976,
23.98,
24000.0 / 1001.0,
24.0,
25.0,
29.97,
30000.0 / 1001.0,
30.0,
48.0,
50.0,
59.94,
60000.0 / 1001.0,
60.0 }
60.0
}
};

// deprecated in favor of `is_smpte_timecode_rate`
bool
RationalTime::is_valid_timecode_rate(double fps)
{
auto b = valid_timecode_rates.begin(), e = valid_timecode_rates.end();
return is_smpte_timecode_rate(fps);
}

bool
RationalTime::is_smpte_timecode_rate(double fps)
{
auto b = smpte_timecode_rates.begin(), e = smpte_timecode_rates.end();
return std::find(b, e, fps) != e;
}

// deprecated in favor of `is_smpte_timecode_rate`
double
RationalTime::nearest_valid_timecode_rate(double rate)
{
return nearest_smpte_timecode_rate(rate);
}

double
RationalTime::nearest_smpte_timecode_rate(double rate)
{
double nearest_rate = 0;
double min_diff = std::numeric_limits<double>::max();
for (auto valid_rate: smpte_timecode_rates)
for (auto smpte_rate: smpte_timecode_rates)
{
if (valid_rate == rate)
if (smpte_rate == rate)
{
return rate;
}
auto diff = std::abs(rate - valid_rate);
auto diff = std::abs(rate - smpte_rate);
if (diff >= min_diff)
{
continue;
}
min_diff = diff;
nearest_rate = valid_rate;
nearest_rate = smpte_rate;
}
return nearest_rate;
}
Expand Down Expand Up @@ -200,7 +193,7 @@ RationalTime::from_timecode(
double rate,
ErrorStatus* error_status)
{
if (!RationalTime::is_valid_timecode_rate(rate))
if (!RationalTime::is_smpte_timecode_rate(rate))
{
if (error_status)
{
Expand Down Expand Up @@ -331,7 +324,7 @@ RationalTime::from_time_string(
double rate,
ErrorStatus* error_status)
{
if (!RationalTime::is_valid_timecode_rate(rate))
if (!RationalTime::is_smpte_timecode_rate(rate))
{
set_error(
time_string,
Expand Down Expand Up @@ -460,7 +453,12 @@ RationalTime::to_timecode(
return std::string();
}

if (!is_valid_timecode_rate(rate))
// It is common practice to use truncated or rounded values
// like 29.97 instead of exact SMPTE rates like 30000/1001
// so as a convenience we will snap the rate to the nearest
// SMPTE rate if it is close enough.
double nearest_smpte_rate = nearest_smpte_timecode_rate(rate);
if (abs(nearest_smpte_rate - rate) > 0.1)
{
if (error_status)
{
Expand All @@ -469,6 +467,9 @@ RationalTime::to_timecode(
return std::string();
}

// Let's assume this is the rate instead of the given rate.
rate = nearest_smpte_rate;

bool rate_is_dropframe = is_dropframe_rate(rate);
if (drop_frame == IsDropFrameRate::ForceYes and not rate_is_dropframe)
{
Expand Down Expand Up @@ -504,11 +505,11 @@ RationalTime::to_timecode(
}
else
{
if ((rate == 29.97) or (rate == 30000 / 1001.0))
if (rate == 30000 / 1001.0)
{
dropframes = 2;
}
else if (rate == 59.94)
else if (rate == 60000 / 1001.0)
{
dropframes = 4;
}
Expand Down Expand Up @@ -582,7 +583,7 @@ RationalTime::to_nearest_timecode(
{
*error_status = ErrorStatus();

double nearest_rate = nearest_valid_timecode_rate(rate);
double nearest_rate = nearest_smpte_timecode_rate(rate);

return to_timecode(nearest_rate, drop_frame, error_status);
}
Expand Down
13 changes: 10 additions & 3 deletions src/opentime/rationalTime.h
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,20 @@ class RationalTime
start_time._rate };
}

/// @brief Returns true if the rate is valid for use with timecode.
/// @brief Returns true is the rate is supported by SMPTE timecode.
[[deprecated("Use is_smpte_timecode_rate() instead")]]
static bool is_valid_timecode_rate(double rate);

/// @brief Returns the first valid timecode rate that has the least
/// difference from rate.
/// @brief Returns true is the rate is supported by SMPTE timecode.
static bool is_smpte_timecode_rate(double rate);

/// @brief Returns the SMPTE timecode rate nearest to the given rate.
[[deprecated("Use nearest_smpte_timecode_rate() instead")]]
static double nearest_valid_timecode_rate(double rate);

/// @brief Returns the SMPTE timecode rate nearest to the given rate.
static double nearest_smpte_timecode_rate(double rate);

/// @brief Convert a frame number and rate into a time.
static constexpr RationalTime
from_frames(double frame, double rate) noexcept
Expand Down
4 changes: 3 additions & 1 deletion src/opentimelineio/clip.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ Clip::Clip(
MediaReference* media_reference,
std::optional<TimeRange> const& source_range,
AnyDictionary const& metadata,
std::vector<Effect*> const& effects,
std::vector<Marker*> const& markers,
std::string const& active_media_reference_key)
: Parent{ name, source_range, metadata }
: Parent{ name, source_range, metadata, effects, markers }
, _active_media_reference_key(active_media_reference_key)
{
set_media_reference(media_reference);
Expand Down
2 changes: 2 additions & 0 deletions src/opentimelineio/clip.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class Clip : public Item
MediaReference* media_reference = nullptr,
std::optional<TimeRange> const& source_range = std::nullopt,
AnyDictionary const& metadata = AnyDictionary(),
std::vector<Effect*> const& effects = std::vector<Effect*>(),
std::vector<Marker*> const& markers = std::vector<Marker*>(),
std::string const& active_media_reference_key = default_media_key);

void set_media_reference(MediaReference* media_reference);
Expand Down
18 changes: 13 additions & 5 deletions src/py-opentimelineio/opentime-bindings/opentime_rationalTime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,22 @@ Compute the duration of samples from first to last (including last). This is not
For example, the duration of a clip from frame 10 to frame 15 is 6 frames. Result will be in the rate of start_time.
)docstring")
.def_static("is_valid_timecode_rate", &RationalTime::is_valid_timecode_rate, "rate"_a, "Returns true if the rate is valid for use with timecode.")
.def_static("is_valid_timecode_rate", &RationalTime::is_valid_timecode_rate, "rate"_a,
"Deprecated. Please use `is_smpte_timecode_rate` instead. This function will be removed in a future release.")
.def_static("is_smpte_timecode_rate", &RationalTime::is_smpte_timecode_rate, "rate"_a,
"Returns true if the rate is valid for use with SMPTE timecode.")
.def_static("nearest_valid_timecode_rate", &RationalTime::nearest_valid_timecode_rate, "rate"_a,
"Returns the first valid timecode rate that has the least difference from the given value.")
.def_static("from_frames", &RationalTime::from_frames, "frame"_a, "rate"_a, "Turn a frame number and rate into a :class:`~RationalTime` object.")
"Deprecated. Please use `nearest_smpte_timecode_rate` instead. This function will be removed in a future release.")
.def_static("nearest_smpte_timecode_rate", &RationalTime::nearest_smpte_timecode_rate, "rate"_a,
"Returns the first SMPTE timecode rate that has the least difference from the given value.")
.def_static("from_frames", &RationalTime::from_frames, "frame"_a, "rate"_a,
"Turn a frame number and rate into a :class:`~RationalTime` object.")
.def_static("from_seconds", static_cast<RationalTime (*)(double, double)> (&RationalTime::from_seconds), "seconds"_a, "rate"_a)
.def_static("from_seconds", static_cast<RationalTime (*)(double)> (&RationalTime::from_seconds), "seconds"_a)
.def("to_frames", (int (RationalTime::*)() const) &RationalTime::to_frames, "Returns the frame number based on the current rate.")
.def("to_frames", (int (RationalTime::*)(double) const) &RationalTime::to_frames, "rate"_a, "Returns the frame number based on the given rate.")
.def("to_frames", (int (RationalTime::*)() const) &RationalTime::to_frames,
"Returns the frame number based on the current rate.")
.def("to_frames", (int (RationalTime::*)(double) const) &RationalTime::to_frames, "rate"_a,
"Returns the frame number based on the given rate.")
.def("to_seconds", &RationalTime::to_seconds)
.def("to_timecode", [](RationalTime rt, double rate, std::optional<bool> drop_frame) {
return rt.to_timecode(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "opentimelineio/typeRegistry.h"
#include "opentimelineio/stackAlgorithm.h"

#include <ImathBox.h>

namespace py = pybind11;
using namespace pybind11::literals;

Expand Down Expand Up @@ -270,6 +272,8 @@ PYBIND11_MODULE(_otio, m) {
.def(py::init([](RationalTime rt) { return new PyAny(rt); }))
.def(py::init([](TimeRange tr) { return new PyAny(tr); }))
.def(py::init([](TimeTransform tt) { return new PyAny(tt); }))
.def(py::init([](IMATH_NAMESPACE::V2d v2d) { return new PyAny(v2d); }))
.def(py::init([](IMATH_NAMESPACE::Box2d box2d) { return new PyAny(box2d); }))
.def(py::init([](AnyVectorProxy* p) { return new PyAny(p->fetch_any_vector()); }))
.def(py::init([](AnyDictionaryProxy* p) { return new PyAny(p->fetch_any_dictionary()); }))
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,13 +425,20 @@ Contains a :class:`.MediaReference` and a trim on that media reference.
)docstring")
.def(py::init([](std::string name, MediaReference* media_reference,
std::optional<TimeRange> source_range, py::object metadata,
std::optional<std::vector<Effect*>> effects,
std::optional<std::vector<Marker*>> markers,
const std::string& active_media_reference) {
return new Clip(name, media_reference, source_range, py_to_any_dictionary(metadata), active_media_reference);
return new Clip(name, media_reference, source_range, py_to_any_dictionary(metadata),
vector_or_default<Effect>(effects),
vector_or_default<Marker>(markers),
active_media_reference);
}),
py::arg_v("name"_a = std::string()),
"media_reference"_a = nullptr,
"source_range"_a = std::nullopt,
py::arg_v("metadata"_a = py::none()),
"effects"_a = py::none(),
"markers"_a = py::none(),
"active_media_reference"_a = std::string(Clip::default_media_key))
.def_property_readonly_static("DEFAULT_MEDIA_KEY",[](py::object /* self */) {
return Clip::default_media_key;
Expand Down
4 changes: 4 additions & 0 deletions src/py-opentimelineio/opentimelineio-bindings/otio_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
#include "opentimelineio/safely_typed_any.h"
#include "opentimelineio/stringUtils.h"

#include <ImathBox.h>

#include <map>
#include <cstring>

Expand Down Expand Up @@ -60,6 +62,8 @@ void _build_any_to_py_dispatch_table() {
t[&typeid(RationalTime)] = [](std::any const& a, bool) { return py::cast(safely_cast_rational_time_any(a)); };
t[&typeid(TimeRange)] = [](std::any const& a, bool) { return py::cast(safely_cast_time_range_any(a)); };
t[&typeid(TimeTransform)] = [](std::any const& a, bool) { return py::cast(safely_cast_time_transform_any(a)); };
t[&typeid(IMATH_NAMESPACE::V2d)] = [](std::any const& a, bool) { return py::cast(safely_cast_point_any(a)); };
t[&typeid(IMATH_NAMESPACE::Box2d)] = [](std::any const& a, bool) { return py::cast(safely_cast_box_any(a)); };
t[&typeid(SerializableObject::Retainer<>)] = [](std::any const& a, bool) {
SerializableObject* so = safely_cast_retainer_any(a);
return py::cast(managing_ptr<SerializableObject>(so)); };
Expand Down
12 changes: 9 additions & 3 deletions src/py-opentimelineio/opentimelineio/schema/clip.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

@add_method(_otio.Clip)
def __str__(self):
return 'Clip("{}", {}, {}, {})'.format(
return 'Clip("{}", {}, {}, {}, {}, {})'.format(
self.name,
self.media_reference,
self.source_range,
self.metadata
self.metadata,
self.effects,
self.markers
)


Expand All @@ -22,12 +24,16 @@ def __repr__(self):
'name={}, '
'media_reference={}, '
'source_range={}, '
'metadata={}'
'metadata={}, '
'effects={}, '
'markers={}'
')'.format(
repr(self.name),
repr(self.media_reference),
repr(self.source_range),
repr(self.metadata),
repr(self.effects),
repr(self.markers)
)
)

Expand Down
Loading

0 comments on commit 44243bf

Please sign in to comment.