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

AY-6975 Consolidate retimes computation. #1087

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 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
79 changes: 56 additions & 23 deletions client/ayon_core/pipeline/editorial.py
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,14 @@ def remap_range_on_file_sequence(otio_clip, otio_range):
rate=available_range_rate,
).to_frames()

# e.g.:
# duration = 10 frames at 24fps
# if frame_in = 1001 then
# frame_out = 1010
offset_duration = max(0, otio_range.duration.to_frames() - 1)

frame_out = otio.opentime.RationalTime.from_frames(
frame_in + otio_range.duration.to_frames() - 1,
frame_in + offset_duration,
rate=available_range_rate,
).to_frames()

Expand Down Expand Up @@ -369,10 +375,6 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
tw_node.update(metadata)
tw_node["lookup"] = list(lookup)

# get first and last frame offsets
offset_in += lookup[0]
offset_out += lookup[-1]

# add to timewarp nodes
time_warp_nodes.append(tw_node)

Expand All @@ -397,15 +399,13 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
src_in = conformed_source_range.start_time
src_duration = conformed_source_range.duration

offset_in = otio.opentime.RationalTime(offset_in, rate=src_in.rate)
offset_duration = otio.opentime.RationalTime(
offset_out,
rate=src_duration.rate
retimed_duration = otio.opentime.RationalTime(
src_duration.value * abs(time_scalar),
src_duration.rate
)

trim_range = otio.opentime.TimeRange(
start_time=src_in + offset_in,
duration=src_duration + offset_duration
start_time=src_in,
duration=retimed_duration,
)

# preserve discrete frame numbers
Expand All @@ -419,17 +419,50 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
else:
# compute retimed range
media_in_trimmed = conformed_source_range.start_time.value + offset_in
media_out_trimmed = media_in_trimmed + (
(
conformed_source_range.duration.value
* abs(time_scalar)
+ offset_out
) - 1
)

offset_duration = conformed_source_range.duration.value * abs(time_scalar)
offset_duration -= 1 # duration 1 frame -> freeze frame -> end = start + 0
offset_duration = max(0, offset_duration) # negative duration = frozen frame
media_out_trimmed = media_in_trimmed + offset_duration
robin-ynput marked this conversation as resolved.
Show resolved Hide resolved

media_in = available_range.start_time.value
media_out = available_range.end_time_inclusive().value

if time_warp_nodes:
# Naive approach: Resolve consecutive timewarp(s) on range,
# then check if plate range has to be extended beyond source range.
frame_range = [media_in_trimmed]
in_frame = media_in_trimmed + time_scalar
while in_frame <= media_out_trimmed:
frame_range.append(in_frame)
in_frame += time_scalar
Copy link
Member

@iLLiCiTiT iLLiCiTiT Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If time_scalar is int then this can be simplified to:

Suggested change
frame_range = [media_in_trimmed]
in_frame = media_in_trimmed + time_scalar
while in_frame <= media_out_trimmed:
frame_range.append(in_frame)
in_frame += time_scalar
frame_range = list(range(
media_in_trimmed,
media_out_trimmed + 1,
time_scalar
))

If is float then this is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

time_scalar might be float (unfortunately 😞)


for tw_idx, tw in enumerate(time_warp_nodes):
for idx, frame_number in enumerate(frame_range):
# First timewarp, apply on media range
if tw_idx == 0:
frame_range[idx] = round(frame_number + tw["lookup"][idx] * time_scalar)
# Consecutive timewarp, apply on the previous result
else:
new_idx = round(idx + tw["lookup"][idx])

if not 0 <= new_idx < len(frame_range):
# TODO: implementing this would need to actually have
# retiming engine resolve process within AYON, resolving wraps
# as curves, then projecting those into the previous media_range.
raise NotImplementedError(
"Unsupported consecutive timewarps (out of computed range)"
)

frame_range[idx] = frame_range[new_idx]
robin-ynput marked this conversation as resolved.
Show resolved Hide resolved

# adjust range if needed
media_in_trimmed = min(media_in_trimmed, min(frame_range))
media_in_trimmed = max(media_in_trimmed, media_in)

media_out_trimmed = max(media_out_trimmed, max(frame_range))
media_out_trimmed = min(media_out_trimmed, media_out)

# adjust available handles if needed
if (media_in_trimmed - media_in) < handle_start:
handle_start = max(0, media_in_trimmed - media_in)
Expand All @@ -448,16 +481,16 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end):
"retime": True,
"speed": time_scalar,
"timewarps": time_warp_nodes,
"handleStart": int(handle_start),
"handleEnd": int(handle_end)
"handleStart": round(handle_start),
"handleEnd": round(handle_end)
}
}

returning_dict = {
"mediaIn": media_in_trimmed,
"mediaOut": media_out_trimmed,
"handleStart": int(handle_start),
"handleEnd": int(handle_end),
"handleStart": round(handle_start),
"handleEnd": round(handle_end),
"speed": time_scalar
}

Expand Down
29 changes: 23 additions & 6 deletions client/ayon_core/plugins/publish/collect_otio_subset_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
instance -> otioReviewClips
"""
import os
import math

import clique
import pyblish.api
Expand Down Expand Up @@ -69,9 +70,17 @@ def process(self, instance):
self.log.debug(
">> retimed_attributes: {}".format(retimed_attributes))

# break down into variables
media_in = int(retimed_attributes["mediaIn"])
media_out = int(retimed_attributes["mediaOut"])
# break down into variables as rounded frame numbers
#
# 0 1 2 3 4
# |-------------|---------------|--------------|-------------|
# |_______________media range_______________|
# 0.6 3.2
#
# As rounded frames, media_in = 0 and media_out = 4
media_in = math.floor(retimed_attributes["mediaIn"])
media_out = math.ceil(retimed_attributes["mediaOut"])

handle_start = int(retimed_attributes["handleStart"])
handle_end = int(retimed_attributes["handleEnd"])

Expand Down Expand Up @@ -174,9 +183,17 @@ def process(self, instance):
path, trimmed_media_range_h, metadata)
self.staging_dir, collection = collection_data

self.log.debug(collection)
repre = self._create_representation(
frame_start, frame_end, collection=collection)
if len(collection.indexes) > 1:
self.log.debug(collection)
repre = self._create_representation(
frame_start, frame_end, collection=collection)
else:
filename, = tuple(collection)
robin-ynput marked this conversation as resolved.
Show resolved Hide resolved
self.log.debug(filename)

# TODO: discuss this, it erases frame number.
repre = self._create_representation(
frame_start, frame_end, file=filename)

if (
not instance.data.get("otioReviewClips")
Expand Down
3 changes: 1 addition & 2 deletions client/ayon_core/plugins/publish/extract_otio_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ def process(self, instance):
self.actual_fps = available_range.duration.rate
start = src_range.start_time.rescaled_to(self.actual_fps)
duration = src_range.duration.rescaled_to(self.actual_fps)
src_frame_start = src_range.start_time.to_frames()

# Temporary.
# Some AYON custom OTIO exporter were implemented with
Expand All @@ -157,7 +156,7 @@ def process(self, instance):
if (
is_clip_from_media_sequence(r_otio_cl)
and available_range_start_frame == media_ref.start_frame
and src_frame_start < media_ref.start_frame
and start.to_frames() < media_ref.start_frame
):
available_range = otio.opentime.TimeRange(
otio.opentime.RationalTime(0, rate=self.actual_fps),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
{
"OTIO_SCHEMA": "Clip.2",
"metadata": {},
"name": "sh010",
"source_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 23.976024627685547,
"value": 11.0
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 23.976024627685547,
"value": 909986.0387191772
}
},
"effects": [
{
"OTIO_SCHEMA": "LinearTimeWarp.1",
"metadata": {},
"name": "Speed",
"effect_name": "LinearTimeWarp",
"time_scalar": 2.0
}
],
"markers": [
{
"OTIO_SCHEMA": "Marker.2",
"metadata": {
"applieswhole": "1",
"hiero_source_type": "TrackItem",
"json_metadata": "{\"hiero_sub_products\": {\"io.ayon.creators.hiero.shot\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"shot\", \"productName\": \"shotMain\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.shot\", \"variant\": \"main\", \"folderPath\": \"/shots/hiero_retime_2x/sh010\", \"task\": null, \"clip_index\": \"37BA620A-6580-A543-ADF3-5A7133F41BB6\", \"hierarchy\": \"shots/hiero_retime_2x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_retime_2x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_retime_2x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_retime_2x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"c60086c3-9ec3-448a-9bc5-6aa9f6af0fd5\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_retime_2x/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"8cdde735-d5a7-4f95-9cff-ded20ff21135\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceIn\": 176.0, \"sourceOut\": 196.0, \"fps\": \"from_selection\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}, \"io.ayon.creators.hiero.plate\": {\"id\": \"pyblish.avalon.instance\", \"productType\": \"plate\", \"productName\": \"plateVideo_1\", \"active\": true, \"creator_identifier\": \"io.ayon.creators.hiero.plate\", \"variant\": \"Video_1\", \"folderPath\": \"/shots/hiero_retime_2x/sh010\", \"task\": null, \"clip_index\": \"37BA620A-6580-A543-ADF3-5A7133F41BB6\", \"hierarchy\": \"shots/hiero_retime_2x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_retime_2x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"Video 1\", \"sourceResolution\": false, \"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"parents\": [{\"entity_type\": \"folder\", \"folder_type\": \"folder\", \"entity_name\": \"shots\"}, {\"entity_type\": \"sequence\", \"folder_type\": \"sequence\", \"entity_name\": \"hiero_retime_2x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_retime_2x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"c60086c3-9ec3-448a-9bc5-6aa9f6af0fd5\", \"reviewTrack\": \"Video 1\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"8cdde735-d5a7-4f95-9cff-ded20ff21135\", \"label\": \"/shots/hiero_retime_2x/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"064a92fc-5704-4316-8cc9-780e430ae2e5\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_retime_2x/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"Video 1\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"37BA620A-6580-A543-ADF3-5A7133F41BB6\"}",
"label": "AYONdata_3c3f54af",
"note": "AYON data container"
},
"name": "AYONdata_3c3f54af",
"color": "RED",
"marked_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 23.976024627685547,
"value": 0.0
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 23.976024627685547,
"value": 0.0
}
}
}
],
"enabled": true,
"media_references": {
"DEFAULT_MEDIA": {
"OTIO_SCHEMA": "ImageSequenceReference.1",
"metadata": {
"ayon.source.colorspace": "ACES - ACES2065-1",
"ayon.source.height": 720,
"ayon.source.pixelAspect": 1.0,
"ayon.source.width": 1280,
"clip.properties.blendfunc": "0",
"clip.properties.colourspacename": "default",
"clip.properties.domainroot": "",
"clip.properties.enabled": "1",
"clip.properties.expanded": "1",
"clip.properties.opacity": "1",
"clip.properties.valuesource": "",
"foundry.source.audio": "",
"foundry.source.bitmapsize": "0",
"foundry.source.bitsperchannel": "0",
"foundry.source.channelformat": "integer",
"foundry.source.colourtransform": "ACES - ACES2065-1",
"foundry.source.duration": "301",
"foundry.source.filename": "output.%07d.exr 948674-948974",
"foundry.source.filesize": "",
"foundry.source.fragments": "301",
"foundry.source.framerate": "25",
"foundry.source.fullpath": "",
"foundry.source.height": "720",
"foundry.source.layers": "colour",
"foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.%07d.exr 948674-948974",
"foundry.source.pixelAspect": "1",
"foundry.source.pixelAspectRatio": "",
"foundry.source.pixelformat": "RGBA (Float16) Open Color IO space: 11",
"foundry.source.reelID": "",
"foundry.source.resolution": "",
"foundry.source.samplerate": "Invalid",
"foundry.source.shortfilename": "output.%07d.exr 948674-948974",
"foundry.source.shot": "",
"foundry.source.shotDate": "",
"foundry.source.startTC": "",
"foundry.source.starttime": "948674",
"foundry.source.timecode": "948674",
"foundry.source.umid": "28c4702f-5af7-4980-52c9-6eb875968890",
"foundry.source.umidOriginator": "foundry.source.umid",
"foundry.source.width": "1280",
"foundry.timeline.autodiskcachemode": "Manual",
"foundry.timeline.colorSpace": "ACES - ACES2065-1",
"foundry.timeline.duration": "301",
"foundry.timeline.framerate": "25",
"foundry.timeline.outputformat": "",
"foundry.timeline.poster": "0",
"foundry.timeline.posterLayer": "colour",
"foundry.timeline.readParams": "AAAAAQAAAAAAAAAFAAAACmNvbG9yc3BhY2UAAAAFaW50MzIAAAAAAAAAC2VkZ2VfcGl4ZWxzAAAABWludDMyAAAAAAAAABFpZ25vcmVfcGFydF9uYW1lcwAAAARib29sAAAAAAhub3ByZWZpeAAAAARib29sAAAAAB5vZmZzZXRfbmVnYXRpdmVfZGlzcGxheV93aW5kb3cAAAAEYm9vbAE=",
"foundry.timeline.samplerate": "Invalid",
"isSequence": true,
"media.exr.channels": "B:{1 0 1 1},G:{1 0 1 1},R:{1 0 1 1}",
"media.exr.compression": "2",
"media.exr.compressionName": "Zip (1 scanline)",
"media.exr.dataWindow": "1,1,1278,718",
"media.exr.displayWindow": "0,0,1279,719",
"media.exr.lineOrder": "0",
"media.exr.pixelAspectRatio": "1",
"media.exr.screenWindowCenter": "0,0",
"media.exr.screenWindowWidth": "1",
"media.exr.type": "scanlineimage",
"media.exr.version": "1",
"media.input.bitsperchannel": "16-bit half float",
"media.input.ctime": "2025-01-13 14:26:25",
"media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange/output.0948674.exr",
"media.input.filereader": "exr",
"media.input.filesize": "214941",
"media.input.frame": "1",
"media.input.height": "720",
"media.input.mtime": "2025-01-13 14:26:25",
"media.input.width": "1280",
"media.nuke.full_layer_names": "0",
"media.nuke.node_hash": "b13e3153b31d8f14",
"media.nuke.version": "15.0v5",
"padding": 7
},
"name": "",
"available_range": {
"OTIO_SCHEMA": "TimeRange.1",
"duration": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 25.0,
"value": 301.0
},
"start_time": {
"OTIO_SCHEMA": "RationalTime.1",
"rate": 25.0,
"value": 948674.0
}
},
"available_image_bounds": null,
"target_url_base": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/img_sequence/exr_long_frameRange\\",
"name_prefix": "output.",
"name_suffix": ".exr",
"start_frame": 948674,
"frame_step": 1,
"rate": 25.0,
"frame_zero_padding": 7,
"missing_frame_policy": "error"
}
},
"active_media_reference_key": "DEFAULT_MEDIA"
}
Loading
Loading