Skip to content

Commit 641dda7

Browse files
committed
Initial implementation of ImageSequenceReference (#602)
Added ImageSequenceReference MediaReference subclass schema.
1 parent 39aae41 commit 641dda7

File tree

10 files changed

+1115
-4
lines changed

10 files changed

+1115
-4
lines changed

docs/tutorials/otio-serialized-schema-only-fields.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,21 @@ parameters:
179179
- *name*
180180
- *parameters*
181181

182+
### ImageSequenceReference.1
183+
184+
parameters:
185+
- *available_range*
186+
- *frame_step*
187+
- *frame_zero_padding*
188+
- *metadata*
189+
- *missing_frame_policy*
190+
- *name*
191+
- *name_prefix*
192+
- *name_suffix*
193+
- *rate*
194+
- *start_frame*
195+
- *target_url_base*
196+
182197
### LinearTimeWarp.1
183198

184199
parameters:

docs/tutorials/otio-serialized-schema.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,29 @@ parameters:
354354
- *name*:
355355
- *parameters*:
356356

357+
### ImageSequenceReference.1
358+
359+
*full module path*: `opentimelineio.schema.ImageSequenceReference`
360+
361+
*documentation*:
362+
363+
```
364+
None
365+
```
366+
367+
parameters:
368+
- *available_range*:
369+
- *frame_step*:
370+
- *frame_zero_padding*:
371+
- *metadata*:
372+
- *missing_frame_policy*:
373+
- *name*:
374+
- *name_prefix*:
375+
- *name_suffix*:
376+
- *rate*:
377+
- *start_frame*:
378+
- *target_url_base*:
379+
357380
### LinearTimeWarp.1
358381

359382
*full module path*: `opentimelineio.schema.LinearTimeWarp`

src/opentimelineio/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ set(OPENTIMELINEIO_HEADER_FILES
1212
freezeFrame.h
1313
gap.h
1414
generatorReference.h
15+
imageSequenceReference.h
1516
item.h
1617
linearTimeWarp.h
1718
marker.h
@@ -47,6 +48,7 @@ add_library(opentimelineio SHARED
4748
freezeFrame.cpp
4849
gap.cpp
4950
generatorReference.cpp
51+
imageSequenceReference.cpp
5052
item.cpp
5153
linearTimeWarp.cpp
5254
marker.cpp
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
#include "opentimelineio/imageSequenceReference.h"
2+
3+
namespace opentimelineio { namespace OPENTIMELINEIO_VERSION {
4+
5+
ImageSequenceReference::ImageSequenceReference(std::string const& target_url_base,
6+
std::string const& name_prefix,
7+
std::string const& name_suffix,
8+
int start_frame,
9+
int frame_step,
10+
double const rate,
11+
int frame_zero_padding,
12+
MissingFramePolicy const missing_frame_policy,
13+
optional<TimeRange> const& available_range,
14+
AnyDictionary const& metadata)
15+
: Parent(std::string(), available_range, metadata),
16+
_target_url_base(target_url_base),
17+
_name_prefix(name_prefix),
18+
_name_suffix(name_suffix),
19+
_start_frame {start_frame},
20+
_frame_step {frame_step},
21+
_rate {rate},
22+
_frame_zero_padding {frame_zero_padding},
23+
_missing_frame_policy {missing_frame_policy} {
24+
}
25+
26+
ImageSequenceReference::~ImageSequenceReference() {
27+
}
28+
29+
RationalTime
30+
ImageSequenceReference::frame_duration() const {
31+
return RationalTime((double)_frame_step, _rate);
32+
}
33+
34+
int ImageSequenceReference::end_frame() const {
35+
if (!this->available_range().has_value()) {
36+
return _start_frame;
37+
}
38+
39+
int num_frames = this->available_range().value().duration().to_frames(_rate);
40+
41+
// Subtract 1 for inclusive frame ranges
42+
return (_start_frame + num_frames - 1);
43+
}
44+
45+
int ImageSequenceReference::number_of_images_in_sequence() const {
46+
if (!this->available_range().has_value()) {
47+
return 0;
48+
}
49+
50+
double playback_rate = (_rate / (double)_frame_step);
51+
int num_frames = this->available_range().value().duration().to_frames(playback_rate);
52+
return num_frames;
53+
}
54+
55+
int ImageSequenceReference::frame_for_time(RationalTime const time, ErrorStatus* error_status) const {
56+
if (!this->available_range().has_value() || !this->available_range().value().contains(time)) {
57+
*error_status = ErrorStatus(ErrorStatus::INVALID_TIME_RANGE);
58+
return 0;
59+
}
60+
61+
RationalTime start = this->available_range().value().start_time();
62+
RationalTime duration_from_start = (time - start);
63+
int frame_offset = duration_from_start.to_frames(_rate);
64+
65+
*error_status = ErrorStatus(ErrorStatus::OK);
66+
67+
return (_start_frame + frame_offset);
68+
}
69+
70+
std::string
71+
ImageSequenceReference::target_url_for_image_number(int const image_number, ErrorStatus* error_status) const {
72+
if (image_number >= this->number_of_images_in_sequence()) {
73+
*error_status = ErrorStatus(ErrorStatus::ILLEGAL_INDEX);
74+
return std::string();
75+
}
76+
const int file_image_num = _start_frame + (image_number * _frame_step);
77+
const bool is_negative = (file_image_num < 0);
78+
79+
std::string image_num_string = std::to_string(abs(file_image_num));
80+
81+
std::string zero_pad = std::string();
82+
if (image_num_string.length() < _frame_zero_padding) {
83+
zero_pad = std::string(_frame_zero_padding - image_num_string.length(), '0');
84+
}
85+
86+
std::string sign = std::string();
87+
if (is_negative) {
88+
sign = "-";
89+
}
90+
91+
// If the base does not include a trailing slash, add it
92+
std::string path_sep = std::string();
93+
if (_target_url_base.compare(_target_url_base.length() - 1, 1, "/") != 0) {
94+
path_sep = "/";
95+
}
96+
97+
std::string out_string = _target_url_base + path_sep + _name_prefix + sign + zero_pad + image_num_string + _name_suffix;
98+
*error_status = ErrorStatus(ErrorStatus::OK);
99+
return out_string;
100+
}
101+
102+
RationalTime
103+
ImageSequenceReference::presentation_time_for_image_number(int const image_number, ErrorStatus* error_status) const {
104+
if (image_number >= this->number_of_images_in_sequence()) {
105+
*error_status = ErrorStatus(ErrorStatus::ILLEGAL_INDEX);
106+
return RationalTime();
107+
}
108+
109+
auto first_frame_time = this->available_range().value().start_time();
110+
auto time_multiplier = TimeTransform(first_frame_time, image_number, -1);
111+
return time_multiplier.applied_to(frame_duration());
112+
}
113+
114+
bool ImageSequenceReference::read_from(Reader& reader) {
115+
auto result = reader.read("target_url_base", &_target_url_base) &&
116+
reader.read("name_prefix", &_name_prefix) &&
117+
reader.read("name_suffix", &_name_suffix) &&
118+
reader.read("start_frame", &_start_frame) &&
119+
reader.read("frame_step", &_frame_step) &&
120+
reader.read("rate", &_rate) &&
121+
reader.read("frame_zero_padding", &_frame_zero_padding);
122+
123+
std::string missing_frame_policy_value;
124+
result && reader.read("missing_frame_policy", &missing_frame_policy_value);
125+
if (!result) {
126+
return result;
127+
}
128+
129+
if (missing_frame_policy_value == "error") {
130+
_missing_frame_policy = MissingFramePolicy::error;
131+
}
132+
else if (missing_frame_policy_value == "black") {
133+
_missing_frame_policy = MissingFramePolicy::black;
134+
}
135+
else if (missing_frame_policy_value == "hold") {
136+
_missing_frame_policy = MissingFramePolicy::hold;
137+
}
138+
else {
139+
// Unrecognized value
140+
ErrorStatus error_status = ErrorStatus(ErrorStatus::JSON_PARSE_ERROR,
141+
"Unknown missing_frame_policy: " + missing_frame_policy_value);
142+
reader.error(error_status);
143+
return false;
144+
}
145+
146+
return result && Parent::read_from(reader);
147+
}
148+
149+
void ImageSequenceReference::write_to(Writer& writer) const {
150+
Parent::write_to(writer);
151+
writer.write("target_url_base", _target_url_base);
152+
writer.write("name_prefix", _name_prefix);
153+
writer.write("name_suffix", _name_suffix);
154+
writer.write("start_frame", _start_frame);
155+
writer.write("frame_step", _frame_step);
156+
writer.write("rate", _rate);
157+
writer.write("frame_zero_padding", _frame_zero_padding);
158+
159+
std::string missing_frame_policy_value;
160+
switch (_missing_frame_policy)
161+
{
162+
case MissingFramePolicy::error:
163+
missing_frame_policy_value = "error";
164+
break;
165+
case MissingFramePolicy::black:
166+
missing_frame_policy_value = "black";
167+
break;
168+
case MissingFramePolicy::hold:
169+
missing_frame_policy_value = "hold";
170+
break;
171+
}
172+
writer.write("missing_frame_policy", missing_frame_policy_value);
173+
}
174+
} }
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#pragma once
2+
3+
#include "opentimelineio/version.h"
4+
#include "opentimelineio/mediaReference.h"
5+
6+
namespace opentimelineio { namespace OPENTIMELINEIO_VERSION {
7+
8+
class ImageSequenceReference final : public MediaReference {
9+
public:
10+
enum MissingFramePolicy {
11+
error = 0,
12+
hold = 1,
13+
black = 2
14+
};
15+
16+
struct Schema {
17+
static auto constexpr name = "ImageSequenceReference";
18+
static int constexpr version = 1;
19+
};
20+
21+
using Parent = MediaReference;
22+
23+
ImageSequenceReference(std::string const& target_url_base = std::string(),
24+
std::string const& name_prefix = std::string(),
25+
std::string const& name_suffix = std::string(),
26+
int start_frame = 1,
27+
int frame_step = 1,
28+
double const rate = 1,
29+
int frame_zero_padding = 0,
30+
MissingFramePolicy const missing_frame_policy = MissingFramePolicy::error,
31+
optional<TimeRange> const& available_range = nullopt,
32+
AnyDictionary const& metadata = AnyDictionary());
33+
34+
std::string const& target_url_base() const {
35+
return _target_url_base;
36+
}
37+
38+
void set_target_url_base(std::string const& target_url_base) {
39+
_target_url_base = target_url_base;
40+
}
41+
42+
std::string const& name_prefix() const {
43+
return _name_prefix;
44+
}
45+
46+
void set_name_prefix(std::string const& target_url_base) {
47+
_name_prefix = target_url_base;
48+
}
49+
50+
std::string const& name_suffix() const {
51+
return _name_suffix;
52+
}
53+
54+
void set_name_suffix(std::string const& target_url_base) {
55+
_name_suffix = target_url_base;
56+
}
57+
58+
int start_frame() const {
59+
return _start_frame;
60+
}
61+
62+
void set_start_frame(int const start_frame) {
63+
_start_frame = start_frame;
64+
}
65+
66+
int frame_step() const {
67+
return _frame_step;
68+
}
69+
70+
void set_frame_step(int const frame_step) {
71+
_frame_step = frame_step;
72+
}
73+
74+
double const& rate() const {
75+
return _rate;
76+
}
77+
78+
void set_rate(double const rate) {
79+
_rate = rate;
80+
}
81+
82+
int frame_zero_padding() const {
83+
return _frame_zero_padding;
84+
}
85+
86+
void set_frame_zero_padding(int const frame_zero_padding) {
87+
_frame_zero_padding = frame_zero_padding;
88+
}
89+
90+
void set_missing_frame_policy(MissingFramePolicy const missing_frame_policy) {
91+
_missing_frame_policy = missing_frame_policy;
92+
}
93+
94+
MissingFramePolicy missing_frame_policy() const {
95+
return _missing_frame_policy;
96+
}
97+
98+
int end_frame() const;
99+
int number_of_images_in_sequence() const;
100+
int frame_for_time(RationalTime const time, ErrorStatus* error_status) const;
101+
102+
std::string
103+
target_url_for_image_number(int const image_number, ErrorStatus* error_status) const;
104+
105+
RationalTime
106+
presentation_time_for_image_number(int const image_number, ErrorStatus* error_status) const;
107+
108+
protected:
109+
virtual ~ImageSequenceReference();
110+
111+
virtual bool read_from(Reader&);
112+
virtual void write_to(Writer&) const;
113+
114+
private:
115+
std::string _target_url_base;
116+
std::string _name_prefix;
117+
std::string _name_suffix;
118+
int _start_frame;
119+
int _frame_step;
120+
double _rate;
121+
int _frame_zero_padding;
122+
MissingFramePolicy _missing_frame_policy;
123+
124+
RationalTime frame_duration() const;
125+
};
126+
127+
} }

src/opentimelineio/typeRegistry.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "opentimelineio/freezeFrame.h"
1010
#include "opentimelineio/gap.h"
1111
#include "opentimelineio/generatorReference.h"
12+
#include "opentimelineio/imageSequenceReference.h"
1213
#include "opentimelineio/item.h"
1314
#include "opentimelineio/linearTimeWarp.h"
1415
#include "opentimelineio/marker.h"
@@ -55,6 +56,7 @@ TypeRegistry::TypeRegistry() {
5556
register_type_from_existing_type("Filler", 1, "Gap", nullptr);
5657

5758
register_type<GeneratorReference>();
59+
register_type<ImageSequenceReference>();
5860
register_type<Item>();
5961
register_type<LinearTimeWarp>();
6062
register_type<Marker>();

0 commit comments

Comments
 (0)