diff --git a/client/ayon_core/pipeline/editorial.py b/client/ayon_core/pipeline/editorial.py index ed1cdf9974..7076b31ed9 100644 --- a/client/ayon_core/pipeline/editorial.py +++ b/client/ayon_core/pipeline/editorial.py @@ -406,7 +406,6 @@ def get_media_range_with_retimes(otio_clip, handle_start, handle_end): trim_range = otio.opentime.TimeRange( start_time=src_in, duration=retimed_duration, - ) # preserve discrete frame numbers @@ -479,16 +478,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 } diff --git a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py index ed7e8ac4f1..bfcf5a71bb 100644 --- a/client/ayon_core/plugins/publish/collect_otio_subset_resources.py +++ b/client/ayon_core/plugins/publish/collect_otio_subset_resources.py @@ -6,6 +6,7 @@ Provides: instance -> otioReviewClips """ import os +import math import clique import pyblish.api @@ -69,9 +70,17 @@ class CollectOtioSubsetResources( 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"]) diff --git a/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_0_7.json b/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_0_7.json new file mode 100644 index 0000000000..3ed27bcf8b --- /dev/null +++ b/tests/client/ayon_core/pipeline/editorial/resources/qt_reverse_speed_0_7.json @@ -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": 29.970030784606934 + } + }, + "effects": [ + { + "OTIO_SCHEMA": "LinearTimeWarp.1", + "metadata": {}, + "name": "Speed", + "effect_name": "LinearTimeWarp", + "time_scalar": -0.699999988079071 + } + ], + "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_qt_neg07x/sh010\", \"task\": null, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\", \"hierarchy\": \"shots/hiero_qt_neg07x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg07x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"clip_media\", \"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_qt_neg07x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg07x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"d7c96d32-6884-452f-9f8c-2383e20ca2db\", \"reviewTrack\": \"clip_media\", \"review\": true, \"folderName\": \"sh010\", \"label\": \"/shots/hiero_qt_neg07x/sh010 shotMain\", \"newHierarchyIntegration\": true, \"instance_id\": \"dae8823d-d664-4afd-9d9d-be20647ad756\", \"creator_attributes\": {\"workfileFrameStart\": 1001, \"handleStart\": 10, \"handleEnd\": 10, \"frameStart\": 1001, \"frameEnd\": 1012, \"clipIn\": 0, \"clipOut\": 10, \"clipDuration\": 11, \"sourceOut\": 30.0, \"fps\": \"from_selection\", \"sourceIn\": 0}, \"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_qt_neg07x/sh010\", \"task\": null, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\", \"hierarchy\": \"shots/hiero_qt_neg07x\", \"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg07x\", \"track\": \"Video_1\", \"shot\": \"sh010\", \"reviewableSource\": \"clip_media\", \"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_qt_neg07x\"}], \"hierarchyData\": {\"folder\": \"shots\", \"episode\": \"ep01\", \"sequence\": \"hiero_qt_neg07x\", \"track\": \"Video_1\"}, \"heroTrack\": true, \"uuid\": \"d7c96d32-6884-452f-9f8c-2383e20ca2db\", \"reviewTrack\": \"clip_media\", \"review\": true, \"folderName\": \"sh010\", \"parent_instance_id\": \"dae8823d-d664-4afd-9d9d-be20647ad756\", \"label\": \"/shots/hiero_qt_neg07x/sh010 plateVideo_1\", \"newHierarchyIntegration\": true, \"instance_id\": \"a1aa49c0-49a1-4499-a3ec-1ac35982d92b\", \"creator_attributes\": {\"parentInstance\": \"/shots/hiero_qt_neg07x/sh010 shotMain\", \"review\": true, \"reviewableSource\": \"clip_media\"}, \"publish_attributes\": {\"CollectSlackFamilies\": {\"additional_message\": \"\"}}}}, \"clip_index\": \"D812B65C-F1C7-DA48-9060-F932A50B2BB4\"}", + "label": "AYONdata_26480dbf", + "note": "AYON data container" + }, + "name": "AYONdata_26480dbf", + "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": "ExternalReference.1", + "metadata": { + "ayon.source.colorspace": "Gamma2.2", + "ayon.source.height": 1080, + "ayon.source.pixelAspect": 1.0, + "ayon.source.width": 1920, + "clip.properties.blendfunc": "0", + "clip.properties.colourspacename": "error", + "clip.properties.domainroot": "", + "clip.properties.enabled": "1", + "clip.properties.expanded": "1", + "clip.properties.opacity": "1", + "clip.properties.valuesource": "", + "clip.properties.ycbcrmatrix": "0", + "com.apple.quicktime.codec": "H.264", + "foundry.source.audio": "", + "foundry.source.bitmapsize": "0", + "foundry.source.bitsperchannel": "0", + "foundry.source.channelformat": "integer", + "foundry.source.colourtransform": "Gamma2.2", + "foundry.source.duration": "101", + "foundry.source.filename": "qt_no_tc_24fps.mov", + "foundry.source.filesize": "", + "foundry.source.fragments": "1", + "foundry.source.framerate": "24", + "foundry.source.fullpath": "", + "foundry.source.height": "1080", + "foundry.source.layers": "colour", + "foundry.source.path": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov", + "foundry.source.pixelAspect": "1", + "foundry.source.pixelAspectRatio": "", + "foundry.source.pixelformat": "RGBA (Float32) Open Color IO space: 114", + "foundry.source.reelID": "", + "foundry.source.resolution": "", + "foundry.source.samplerate": "Invalid", + "foundry.source.shoottime": "4294967295", + "foundry.source.shortfilename": "qt_no_tc_24fps.mov", + "foundry.source.shot": "", + "foundry.source.shotDate": "", + "foundry.source.startTC": "", + "foundry.source.starttime": "0", + "foundry.source.timecode": "0", + "foundry.source.type": "QuickTime H.264", + "foundry.source.umid": "16634e88-6450-4727-6c6e-501f4b31b637", + "foundry.source.umidOriginator": "foundry.source.umid", + "foundry.source.width": "1920", + "foundry.timeline.autodiskcachemode": "Manual", + "foundry.timeline.colorSpace": "Gamma2.2", + "foundry.timeline.duration": "101", + "foundry.timeline.framerate": "24", + "foundry.timeline.outputformat": "", + "foundry.timeline.poster": "0", + "foundry.timeline.posterLayer": "colour", + "foundry.timeline.samplerate": "Invalid", + "media.input.bitsperchannel": "8-bit fixed", + "media.input.ctime": "2024-09-25 17:16:12", + "media.input.filename": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov", + "media.input.filereader": "mov64", + "media.input.filesize": "14631252", + "media.input.frame": "1", + "media.input.frame_rate": "24", + "media.input.height": "1080", + "media.input.mtime": "2024-09-25 17:16:16", + "media.input.pixel_aspect": "1", + "media.input.timecode": "00:00:00:00", + "media.input.width": "1920", + "media.quicktime.codec_id": "avc1", + "media.quicktime.codec_name": "h264", + "media.quicktime.encoder": "H.264", + "media.quicktime.nclc_matrix": "BT709", + "media.quicktime.nclc_primaries": "ITU-R BT.709", + "media.quicktime.nclc_transfer_function": "ITU-R BT.709", + "media.quicktime.thefoundry.Application": "Nuke", + "media.quicktime.thefoundry.ApplicationVersion": "15.0v5", + "media.quicktime.thefoundry.Colorspace": "Gamma2.2", + "media.quicktime.thefoundry.Writer": "mov64", + "media.quicktime.thefoundry.YCbCrMatrix": "Rec 709", + "media.stream.pixel_format": "yuv420p", + "uk.co.thefoundry.Application": "Nuke", + "uk.co.thefoundry.ApplicationVersion": "15.0v5", + "uk.co.thefoundry.Colorspace": "Gamma2.2", + "uk.co.thefoundry.Writer": "mov64", + "uk.co.thefoundry.YCbCrMatrix": "Rec 709" + }, + "name": "", + "available_range": { + "OTIO_SCHEMA": "TimeRange.1", + "duration": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 101.0 + }, + "start_time": { + "OTIO_SCHEMA": "RationalTime.1", + "rate": 24.0, + "value": 0.0 + } + }, + "available_image_bounds": null, + "target_url": "C:/Users/robin/OneDrive/Bureau/dev_ayon/data/qt_no_tc_24fps.mov" + } + }, + "active_media_reference_key": "DEFAULT_MEDIA" +} \ No newline at end of file diff --git a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py index 45cba64558..9ff2a7fdb2 100644 --- a/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py +++ b/tests/client/ayon_core/pipeline/editorial/test_media_range_with_retimes.py @@ -146,6 +146,35 @@ def test_movie_reverse_speed_2x(): ) +def test_movie_reverse_speed_0_7x(): + """ + Movie clip (no timecode) + available files = 0-100 24fps + source_range = 29.97-40.97 23.976fps + speed = -0.7 + """ + expected_data = { + 'handleEnd': 7, + 'handleStart': 7, + 'mediaIn': 30.000000000000004, + 'mediaOut': 36.70769965924555, + 'speed': -0.699999988079071, + 'versionData': { + 'handleEnd': 7, + 'handleStart': 7, + 'retime': True, + 'speed': -0.699999988079071, + 'timewarps': [] + } + } + + _check_expected_retimed_values( + "qt_reverse_speed_0_7.json", + expected_data, + handle_start=10, + handle_end=10, + ) + def test_movie_frozen_frame(): """