Skip to content

Commit 44243bf

Browse files
author
rameshm
committed
Merge remote-tracking branch 'origin' into 1635_fix_flatten_issue
2 parents 3510ade + 68d891a commit 44243bf

18 files changed

+235
-94
lines changed

ADOPTERS.md

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# OpenTimelineIO Adopters
2+
3+
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.
4+
5+
| Name | Description |
6+
|------|------------------------------------------------------------------------------------------------------------------------------------------------|
7+
| [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) |
8+
| [AVID Media Composer](https://www.avid.com/media-composer) | Timeline export (preview) |
9+
| [Black Magic Design DaVinci Resolve](https://www.blackmagicdesign.com/products/davinciresolve/) | Timeline import/export |
10+
| [CineSync](https://www.backlight.co/product/cinesync) | Timeline viewing | |
11+
| [ColorFront Transkoder](https://colorfront.com/index.php?page=SOFTWARE&spage=Transkoder) | Timeline import |
12+
| [Nuke Studio](https://www.foundry.com/products/nuke) | Timeline import/export with full timeline round-tripping |
13+
| [Hiero](https://www.foundry.com/products/nuke-family/hiero) | Timeline import/export |
14+
| [OpenRV](https://github.com/AcademySoftwareFoundation/OpenRV) | Timeline import and viewing |

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ exclude readthedocs-conda.yml
1212
exclude .codecov.yml
1313
exclude .gitlab-ci.yml
1414
exclude *.pdf
15+
exclude ADOPTERS.md
1516
exclude CODE_OF_CONDUCT.md
1617
exclude CONTRIBUTING.md
1718
exclude CONTRIBUTORS.md

docs/conf.py

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@
100100
r'cxx/cxx'
101101
]
102102

103+
# For some reason this URL gives 403 Forbidden when running in github actions
104+
linkcheck_ignore = [r'https://opensource.org/licenses/MIT']
105+
103106
# -- Options for MySt-Parser -----------------------------------------------------------
104107
# https://myst-parser.readthedocs.io/en/latest/sphinx/reference.html
105108

src/opentime/errorStatus.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ ErrorStatus::outcome_to_string(Outcome o)
1313
case OK:
1414
return std::string();
1515
case INVALID_TIMECODE_RATE:
16-
return "invalid timecode rate";
16+
return "SMPTE timecode does not support this rate";
1717
case INVALID_TIMECODE_STRING:
18-
return "string is not a valid timecode string";
18+
return "string is not a SMPTE timecode string";
1919
case TIMECODE_RATE_MISMATCH:
2020
return "timecode specifies a frame higher than its rate";
2121
case INVALID_TIME_STRING:

src/opentime/rationalTime.cpp

+42-41
Original file line numberDiff line numberDiff line change
@@ -13,75 +13,68 @@ namespace opentime { namespace OPENTIME_VERSION {
1313

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

16-
static constexpr std::array<double, 4> dropframe_timecode_rates{ {
17-
// 23.976,
18-
// 23.98,
19-
// 23.97,
20-
// 24000.0/1001.0,
21-
29.97,
16+
static constexpr std::array<double, 2> dropframe_timecode_rates{ {
2217
30000.0 / 1001.0,
23-
59.94,
2418
60000.0 / 1001.0,
2519
} };
2620

21+
// See the official source of these numbers here:
22+
// ST 12-1:2014 - SMPTE Standard - Time and Control Code
23+
// https://ieeexplore.ieee.org/document/7291029
24+
//
2725
static constexpr std::array<double, 11> smpte_timecode_rates{
28-
{ 1.0,
29-
12.0,
30-
24000.0 / 1001.0,
26+
{ 24000.0 / 1001.0,
3127
24.0,
3228
25.0,
3329
30000.0 / 1001.0,
3430
30.0,
31+
48000.0 / 1001.0,
3532
48.0,
3633
50.0,
3734
60000.0 / 1001.0,
38-
60.0 }
39-
};
40-
41-
static constexpr std::array<double, 16> valid_timecode_rates{
42-
{ 1.0,
43-
12.0,
44-
23.97,
45-
23.976,
46-
23.98,
47-
24000.0 / 1001.0,
48-
24.0,
49-
25.0,
50-
29.97,
51-
30000.0 / 1001.0,
52-
30.0,
53-
48.0,
54-
50.0,
55-
59.94,
56-
60000.0 / 1001.0,
57-
60.0 }
35+
60.0
36+
}
5837
};
5938

39+
// deprecated in favor of `is_smpte_timecode_rate`
6040
bool
6141
RationalTime::is_valid_timecode_rate(double fps)
6242
{
63-
auto b = valid_timecode_rates.begin(), e = valid_timecode_rates.end();
43+
return is_smpte_timecode_rate(fps);
44+
}
45+
46+
bool
47+
RationalTime::is_smpte_timecode_rate(double fps)
48+
{
49+
auto b = smpte_timecode_rates.begin(), e = smpte_timecode_rates.end();
6450
return std::find(b, e, fps) != e;
6551
}
6652

53+
// deprecated in favor of `is_smpte_timecode_rate`
6754
double
6855
RationalTime::nearest_valid_timecode_rate(double rate)
56+
{
57+
return nearest_smpte_timecode_rate(rate);
58+
}
59+
60+
double
61+
RationalTime::nearest_smpte_timecode_rate(double rate)
6962
{
7063
double nearest_rate = 0;
7164
double min_diff = std::numeric_limits<double>::max();
72-
for (auto valid_rate: smpte_timecode_rates)
65+
for (auto smpte_rate: smpte_timecode_rates)
7366
{
74-
if (valid_rate == rate)
67+
if (smpte_rate == rate)
7568
{
7669
return rate;
7770
}
78-
auto diff = std::abs(rate - valid_rate);
71+
auto diff = std::abs(rate - smpte_rate);
7972
if (diff >= min_diff)
8073
{
8174
continue;
8275
}
8376
min_diff = diff;
84-
nearest_rate = valid_rate;
77+
nearest_rate = smpte_rate;
8578
}
8679
return nearest_rate;
8780
}
@@ -200,7 +193,7 @@ RationalTime::from_timecode(
200193
double rate,
201194
ErrorStatus* error_status)
202195
{
203-
if (!RationalTime::is_valid_timecode_rate(rate))
196+
if (!RationalTime::is_smpte_timecode_rate(rate))
204197
{
205198
if (error_status)
206199
{
@@ -331,7 +324,7 @@ RationalTime::from_time_string(
331324
double rate,
332325
ErrorStatus* error_status)
333326
{
334-
if (!RationalTime::is_valid_timecode_rate(rate))
327+
if (!RationalTime::is_smpte_timecode_rate(rate))
335328
{
336329
set_error(
337330
time_string,
@@ -460,7 +453,12 @@ RationalTime::to_timecode(
460453
return std::string();
461454
}
462455

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

470+
// Let's assume this is the rate instead of the given rate.
471+
rate = nearest_smpte_rate;
472+
472473
bool rate_is_dropframe = is_dropframe_rate(rate);
473474
if (drop_frame == IsDropFrameRate::ForceYes and not rate_is_dropframe)
474475
{
@@ -504,11 +505,11 @@ RationalTime::to_timecode(
504505
}
505506
else
506507
{
507-
if ((rate == 29.97) or (rate == 30000 / 1001.0))
508+
if (rate == 30000 / 1001.0)
508509
{
509510
dropframes = 2;
510511
}
511-
else if (rate == 59.94)
512+
else if (rate == 60000 / 1001.0)
512513
{
513514
dropframes = 4;
514515
}
@@ -582,7 +583,7 @@ RationalTime::to_nearest_timecode(
582583
{
583584
*error_status = ErrorStatus();
584585

585-
double nearest_rate = nearest_valid_timecode_rate(rate);
586+
double nearest_rate = nearest_smpte_timecode_rate(rate);
586587

587588
return to_timecode(nearest_rate, drop_frame, error_status);
588589
}

src/opentime/rationalTime.h

+10-3
Original file line numberDiff line numberDiff line change
@@ -171,13 +171,20 @@ class RationalTime
171171
start_time._rate };
172172
}
173173

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

177-
/// @brief Returns the first valid timecode rate that has the least
178-
/// difference from rate.
178+
/// @brief Returns true is the rate is supported by SMPTE timecode.
179+
static bool is_smpte_timecode_rate(double rate);
180+
181+
/// @brief Returns the SMPTE timecode rate nearest to the given rate.
182+
[[deprecated("Use nearest_smpte_timecode_rate() instead")]]
179183
static double nearest_valid_timecode_rate(double rate);
180184

185+
/// @brief Returns the SMPTE timecode rate nearest to the given rate.
186+
static double nearest_smpte_timecode_rate(double rate);
187+
181188
/// @brief Convert a frame number and rate into a time.
182189
static constexpr RationalTime
183190
from_frames(double frame, double rate) noexcept

src/opentimelineio/clip.cpp

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ Clip::Clip(
1313
MediaReference* media_reference,
1414
std::optional<TimeRange> const& source_range,
1515
AnyDictionary const& metadata,
16+
std::vector<Effect*> const& effects,
17+
std::vector<Marker*> const& markers,
1618
std::string const& active_media_reference_key)
17-
: Parent{ name, source_range, metadata }
19+
: Parent{ name, source_range, metadata, effects, markers }
1820
, _active_media_reference_key(active_media_reference_key)
1921
{
2022
set_media_reference(media_reference);

src/opentimelineio/clip.h

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class Clip : public Item
2727
MediaReference* media_reference = nullptr,
2828
std::optional<TimeRange> const& source_range = std::nullopt,
2929
AnyDictionary const& metadata = AnyDictionary(),
30+
std::vector<Effect*> const& effects = std::vector<Effect*>(),
31+
std::vector<Marker*> const& markers = std::vector<Marker*>(),
3032
std::string const& active_media_reference_key = default_media_key);
3133

3234
void set_media_reference(MediaReference* media_reference);

src/py-opentimelineio/opentime-bindings/opentime_rationalTime.cpp

+13-5
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,22 @@ Compute the duration of samples from first to last (including last). This is not
102102
103103
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.
104104
)docstring")
105-
.def_static("is_valid_timecode_rate", &RationalTime::is_valid_timecode_rate, "rate"_a, "Returns true if the rate is valid for use with timecode.")
105+
.def_static("is_valid_timecode_rate", &RationalTime::is_valid_timecode_rate, "rate"_a,
106+
"Deprecated. Please use `is_smpte_timecode_rate` instead. This function will be removed in a future release.")
107+
.def_static("is_smpte_timecode_rate", &RationalTime::is_smpte_timecode_rate, "rate"_a,
108+
"Returns true if the rate is valid for use with SMPTE timecode.")
106109
.def_static("nearest_valid_timecode_rate", &RationalTime::nearest_valid_timecode_rate, "rate"_a,
107-
"Returns the first valid timecode rate that has the least difference from the given value.")
108-
.def_static("from_frames", &RationalTime::from_frames, "frame"_a, "rate"_a, "Turn a frame number and rate into a :class:`~RationalTime` object.")
110+
"Deprecated. Please use `nearest_smpte_timecode_rate` instead. This function will be removed in a future release.")
111+
.def_static("nearest_smpte_timecode_rate", &RationalTime::nearest_smpte_timecode_rate, "rate"_a,
112+
"Returns the first SMPTE timecode rate that has the least difference from the given value.")
113+
.def_static("from_frames", &RationalTime::from_frames, "frame"_a, "rate"_a,
114+
"Turn a frame number and rate into a :class:`~RationalTime` object.")
109115
.def_static("from_seconds", static_cast<RationalTime (*)(double, double)> (&RationalTime::from_seconds), "seconds"_a, "rate"_a)
110116
.def_static("from_seconds", static_cast<RationalTime (*)(double)> (&RationalTime::from_seconds), "seconds"_a)
111-
.def("to_frames", (int (RationalTime::*)() const) &RationalTime::to_frames, "Returns the frame number based on the current rate.")
112-
.def("to_frames", (int (RationalTime::*)(double) const) &RationalTime::to_frames, "rate"_a, "Returns the frame number based on the given rate.")
117+
.def("to_frames", (int (RationalTime::*)() const) &RationalTime::to_frames,
118+
"Returns the frame number based on the current rate.")
119+
.def("to_frames", (int (RationalTime::*)(double) const) &RationalTime::to_frames, "rate"_a,
120+
"Returns the frame number based on the given rate.")
113121
.def("to_seconds", &RationalTime::to_seconds)
114122
.def("to_timecode", [](RationalTime rt, double rate, std::optional<bool> drop_frame) {
115123
return rt.to_timecode(

src/py-opentimelineio/opentimelineio-bindings/otio_bindings.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#include "opentimelineio/typeRegistry.h"
1515
#include "opentimelineio/stackAlgorithm.h"
1616

17+
#include <ImathBox.h>
18+
1719
namespace py = pybind11;
1820
using namespace pybind11::literals;
1921

@@ -270,6 +272,8 @@ PYBIND11_MODULE(_otio, m) {
270272
.def(py::init([](RationalTime rt) { return new PyAny(rt); }))
271273
.def(py::init([](TimeRange tr) { return new PyAny(tr); }))
272274
.def(py::init([](TimeTransform tt) { return new PyAny(tt); }))
275+
.def(py::init([](IMATH_NAMESPACE::V2d v2d) { return new PyAny(v2d); }))
276+
.def(py::init([](IMATH_NAMESPACE::Box2d box2d) { return new PyAny(box2d); }))
273277
.def(py::init([](AnyVectorProxy* p) { return new PyAny(p->fetch_any_vector()); }))
274278
.def(py::init([](AnyDictionaryProxy* p) { return new PyAny(p->fetch_any_dictionary()); }))
275279
;

src/py-opentimelineio/opentimelineio-bindings/otio_serializableObjects.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -425,13 +425,20 @@ Contains a :class:`.MediaReference` and a trim on that media reference.
425425
)docstring")
426426
.def(py::init([](std::string name, MediaReference* media_reference,
427427
std::optional<TimeRange> source_range, py::object metadata,
428+
std::optional<std::vector<Effect*>> effects,
429+
std::optional<std::vector<Marker*>> markers,
428430
const std::string& active_media_reference) {
429-
return new Clip(name, media_reference, source_range, py_to_any_dictionary(metadata), active_media_reference);
431+
return new Clip(name, media_reference, source_range, py_to_any_dictionary(metadata),
432+
vector_or_default<Effect>(effects),
433+
vector_or_default<Marker>(markers),
434+
active_media_reference);
430435
}),
431436
py::arg_v("name"_a = std::string()),
432437
"media_reference"_a = nullptr,
433438
"source_range"_a = std::nullopt,
434439
py::arg_v("metadata"_a = py::none()),
440+
"effects"_a = py::none(),
441+
"markers"_a = py::none(),
435442
"active_media_reference"_a = std::string(Clip::default_media_key))
436443
.def_property_readonly_static("DEFAULT_MEDIA_KEY",[](py::object /* self */) {
437444
return Clip::default_media_key;

src/py-opentimelineio/opentimelineio-bindings/otio_utils.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
#include "opentimelineio/safely_typed_any.h"
1212
#include "opentimelineio/stringUtils.h"
1313

14+
#include <ImathBox.h>
15+
1416
#include <map>
1517
#include <cstring>
1618

@@ -60,6 +62,8 @@ void _build_any_to_py_dispatch_table() {
6062
t[&typeid(RationalTime)] = [](std::any const& a, bool) { return py::cast(safely_cast_rational_time_any(a)); };
6163
t[&typeid(TimeRange)] = [](std::any const& a, bool) { return py::cast(safely_cast_time_range_any(a)); };
6264
t[&typeid(TimeTransform)] = [](std::any const& a, bool) { return py::cast(safely_cast_time_transform_any(a)); };
65+
t[&typeid(IMATH_NAMESPACE::V2d)] = [](std::any const& a, bool) { return py::cast(safely_cast_point_any(a)); };
66+
t[&typeid(IMATH_NAMESPACE::Box2d)] = [](std::any const& a, bool) { return py::cast(safely_cast_box_any(a)); };
6367
t[&typeid(SerializableObject::Retainer<>)] = [](std::any const& a, bool) {
6468
SerializableObject* so = safely_cast_retainer_any(a);
6569
return py::cast(managing_ptr<SerializableObject>(so)); };

src/py-opentimelineio/opentimelineio/schema/clip.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77

88
@add_method(_otio.Clip)
99
def __str__(self):
10-
return 'Clip("{}", {}, {}, {})'.format(
10+
return 'Clip("{}", {}, {}, {}, {}, {})'.format(
1111
self.name,
1212
self.media_reference,
1313
self.source_range,
14-
self.metadata
14+
self.metadata,
15+
self.effects,
16+
self.markers
1517
)
1618

1719

@@ -22,12 +24,16 @@ def __repr__(self):
2224
'name={}, '
2325
'media_reference={}, '
2426
'source_range={}, '
25-
'metadata={}'
27+
'metadata={}, '
28+
'effects={}, '
29+
'markers={}'
2630
')'.format(
2731
repr(self.name),
2832
repr(self.media_reference),
2933
repr(self.source_range),
3034
repr(self.metadata),
35+
repr(self.effects),
36+
repr(self.markers)
3137
)
3238
)
3339

0 commit comments

Comments
 (0)