Skip to content
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
6 changes: 5 additions & 1 deletion Sources/objc/include/opentimelineio.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,11 @@ bool media_reference_is_missing_reference(CxxRetainer* self);
bool media_reference_available_range(CxxRetainer* self, CxxTimeRange*);
void media_reference_set_available_range(CxxRetainer* self, CxxTimeRange);
void media_reference_clear_available_range(CxxRetainer* self);


bool media_reference_available_image_bounds(CxxRetainer* self, CGRect* );
void media_reference_set_available_image_bounds(CxxRetainer* self, CGRect image_bounds);
void media_reference_clear_available_image_bounds(CxxRetainer* self);

// MARK: - Timeline
void* timeline_get_tracks(CxxRetainer* self);
void timeline_set_tracks(CxxRetainer* self, CxxRetainer* stack);
Expand Down
33 changes: 33 additions & 0 deletions Sources/objc/opentimelineio.mm
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,39 @@ void media_reference_clear_available_range(CxxRetainer* self) {
SO_cast<otio::MediaReference>(self)->set_available_range(std::nullopt);
}


// If true, value of passed in rect is set. If false, there was no media reference bounds
bool media_reference_available_image_bounds(CxxRetainer* self, CGRect* rect) {
std::optional<IMATH_NAMESPACE::Box2d> iBox2D = SO_cast<otio::MediaReference>(self)->available_image_bounds();

if (iBox2D) {
rect->origin.x = iBox2D->min.x;
rect->origin.y = iBox2D->min.y;
rect->size.width = iBox2D->max.x - iBox2D->min.x;
rect->size.height = iBox2D->max.y - iBox2D->min.y;

return true;
}

return false;
}

void media_reference_set_available_image_bounds(CxxRetainer* self, CGRect image_bounds) {
std::optional<IMATH_NAMESPACE::Box2d> iBox2D = std::optional<IMATH_NAMESPACE::Box2d>();

iBox2D->min.x = image_bounds.origin.x;
iBox2D->min.y = image_bounds.origin.y;
iBox2D->max.x = image_bounds.size.width + image_bounds.origin.x;
iBox2D->max.y = image_bounds.size.height + image_bounds.origin.y;

SO_cast<otio::MediaReference>(self)->set_available_image_bounds(iBox2D);
}

void media_reference_clear_available_image_bounds(CxxRetainer* self) {
SO_cast<otio::MediaReference>(self)->set_available_image_bounds(std::nullopt);
}


// MARK: - Timeline

void* timeline_get_tracks(CxxRetainer* self) {
Expand Down
17 changes: 17 additions & 0 deletions Sources/swift/MediaReference.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,21 @@ public class MediaReference : SerializableObjectWithMetadata {
public var isMissingReference: Bool {
get { return media_reference_is_missing_reference(self) }
}

public var availableImageBounds: CGRect?
{
get {
var rect = CGRect(origin: CGPoint.init(x: 0, y: 0), size: CGSize(width: 0, height: 0))
return media_reference_available_image_bounds(self, &rect) ? rect : nil
}
set {
if let newValue {
media_reference_set_available_image_bounds(self, newValue)
}
else {
media_reference_clear_available_image_bounds(self)
}
}

}
}
29 changes: 29 additions & 0 deletions Tests/OpenTimelineIOTests/testTimeline.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,33 @@ final class testTimeline: XCTestCase {
XCTFail("Cannot read OTIO file `\(timelineInputPath)`: \(error)")
}
}

func testTimelineClipAvailableBounds() {
let inputName = "data/clip_example.otio"

guard let timelineInputPath = Bundle.module.path(forResource: inputName, ofType: "") else {
XCTFail("Missing test data `\(inputName)`")
return
}

do {
let otio = try SerializableObject.fromJSON(filename: timelineInputPath)

guard let timeline = otio as? Timeline else {
XCTFail("Could not create Timeline object from \(timelineInputPath)")
return
}

if let firstClip = timeline.videoTracks.first!.children[1] as? Clip,
let mediaReference = firstClip.mediaReference,
let availableBounds = mediaReference.availableImageBounds
{
XCTAssertEqual(availableBounds, CGRect(origin: .zero, size: CGSize(width: 16, height: 9)))
}

} catch let error {
XCTFail("Cannot read OTIO file `\(timelineInputPath)`: \(error)")
}
}

}
139 changes: 139 additions & 0 deletions Tests/data/clip_example.otio
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
{
"OTIO_SCHEMA": "Timeline.1",
"metadata": {},
"name": "transition_test",
"tracks": {
"OTIO_SCHEMA": "Stack.1",
"children": [
{
"OTIO_SCHEMA": "Track.1",
"children": [
{
"OTIO_SCHEMA": "Gap.1",
"effects": [],
"markers": [],
"enabled": true,
"metadata": {},
"name": "Gap A",
"source_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 8
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 0
}
}
},
{
"OTIO_SCHEMA": "Clip.1",
"effects": [],
"markers": [],
"enabled": true,
"media_reference": {
"OTIO_SCHEMA": "MissingReference.1",
"available_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 8
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 0
}
},
"metadata": {},
"available_image_bounds": {
"OTIO_SCHEMA": "Box2d.1",
"min": {
"OTIO_SCHEMA":"V2d.1",
"x": 0.0,
"y": 0.0
},
"max": {
"OTIO_SCHEMA":"V2d.1",
"x": 16.0,
"y": 9.0
}
},
"name": null
},
"metadata": {},
"name": "Clip-001",
"source_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 3
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 3
}
}
},
{
"OTIO_SCHEMA": "Transition.1",
"metadata": {},
"name": "Dissolve",
"transition_type": "SMPTE_Dissolve",
"parameters": {},
"in_offset": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 2
},
"out_offset": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 1
}
},
{
"OTIO_SCHEMA": "Gap.1",
"effects": [],
"markers": [],
"enabled": true,
"metadata": {},
"name": "Gap B",
"source_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 8
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 24,
"value": 0
}
}
}
],
"effects": [],
"kind": "Video",
"markers": [],
"enabled": true,
"metadata": {},
"name": "Track-001",
"source_range": null
}
],
"effects": [],
"markers": [],
"enabled": true,
"metadata": {},
"name": "tracks",
"source_range": null
}
}