Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
Merge pull request #3612 from pypeclub/feature/OP-3703_fps-ftrack-met…
Browse files Browse the repository at this point in the history
…adata-for-cineSyncPlay

Ftrack: Add more metadata to ftrack components
  • Loading branch information
iLLiCiTiT authored Aug 3, 2022
2 parents 6aedfce + 59463a3 commit e19f0ba
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 33 deletions.
2 changes: 2 additions & 0 deletions openpype/lib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
get_ffmpeg_codec_args,
get_ffmpeg_format_args,
convert_ffprobe_fps_value,
convert_ffprobe_fps_to_float,
)
from .avalon_context import (
CURRENT_DOC_SCHEMAS,
Expand Down Expand Up @@ -287,6 +288,7 @@
"get_ffmpeg_codec_args",
"get_ffmpeg_format_args",
"convert_ffprobe_fps_value",
"convert_ffprobe_fps_to_float",

"CURRENT_DOC_SCHEMAS",
"PROJECT_NAME_ALLOWED_SYMBOLS",
Expand Down
37 changes: 37 additions & 0 deletions openpype/lib/transcoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,3 +938,40 @@ def convert_ffprobe_fps_value(str_value):
fps = int(fps)

return str(fps)


def convert_ffprobe_fps_to_float(value):
"""Convert string value of frame rate to float.
Copy of 'convert_ffprobe_fps_value' which raises exceptions on invalid
value, does not convert value to string and does not return "Unknown"
string.
Args:
value (str): Value to be converted.
Returns:
Float: Converted frame rate in float. If divisor in value is '0' then
'0.0' is returned.
Raises:
ValueError: Passed value is invalid for conversion.
"""

if not value:
raise ValueError("Got empty value.")

items = value.split("/")
if len(items) == 1:
return float(items[0])

if len(items) > 2:
raise ValueError((
"FPS expression contains multiple dividers \"{}\"."
).format(value))

dividend = float(items.pop(0))
divisor = float(items.pop(0))
if divisor == 0.0:
return 0.0
return dividend / divisor
43 changes: 40 additions & 3 deletions openpype/modules/ftrack/plugins/publish/integrate_ftrack_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def process(self, instance):

asset_versions_data_by_id = {}
used_asset_versions = []

# Iterate over components and publish
for data in component_list:
self.log.debug("data: {}".format(data))
Expand Down Expand Up @@ -116,9 +117,6 @@ def process(self, instance):
asset_version_status_ids_by_name
)

# Component
self.create_component(session, asset_version_entity, data)

# Store asset version and components items that were
version_id = asset_version_entity["id"]
if version_id not in asset_versions_data_by_id:
Expand All @@ -135,6 +133,8 @@ def process(self, instance):
if asset_version_entity not in used_asset_versions:
used_asset_versions.append(asset_version_entity)

self._create_components(session, asset_versions_data_by_id)

instance.data["ftrackIntegratedAssetVersionsData"] = (
asset_versions_data_by_id
)
Expand Down Expand Up @@ -623,3 +623,40 @@ def create_component(self, session, asset_version_entity, data):
session.rollback()
session._configure_locations()
six.reraise(tp, value, tb)

def _create_components(self, session, asset_versions_data_by_id):
for item in asset_versions_data_by_id.values():
asset_version_entity = item["asset_version"]
component_items = item["component_items"]

component_entities = session.query(
(
"select id, name from Component where version_id is \"{}\""
).format(asset_version_entity["id"])
).all()

existing_component_names = {
component["name"]
for component in component_entities
}

contain_review = "ftrackreview-mp4" in existing_component_names
thumbnail_component_item = None
for component_item in component_items:
component_data = component_item.get("component_data") or {}
component_name = component_data.get("name")
if component_name == "ftrackreview-mp4":
contain_review = True
elif component_name == "ftrackreview-image":
thumbnail_component_item = component_item

if contain_review and thumbnail_component_item:
thumbnail_component_item["component_data"]["name"] = (
"thumbnail"
)

# Component
for component_item in component_items:
self.create_component(
session, asset_version_entity, component_item
)
159 changes: 129 additions & 30 deletions openpype/modules/ftrack/plugins/publish/integrate_ftrack_instances.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import copy
import pyblish.api

from openpype.lib import get_ffprobe_streams
from openpype.lib.transcoding import (
get_ffprobe_streams,
convert_ffprobe_fps_to_float,
)
from openpype.lib.profiles_filtering import filter_profiles


Expand Down Expand Up @@ -79,11 +82,6 @@ def process(self, instance):
).format(family))
return

# Prepare FPS
instance_fps = instance.data.get("fps")
if instance_fps is None:
instance_fps = instance.context.data["fps"]

status_name = self._get_asset_version_status_name(instance)

# Base of component item data
Expand Down Expand Up @@ -168,10 +166,7 @@ def process(self, instance):
# Add item to component list
component_list.append(thumbnail_item)

if (
not review_representations
and first_thumbnail_component is not None
):
if first_thumbnail_component is not None:
width = first_thumbnail_component_repre.get("width")
height = first_thumbnail_component_repre.get("height")
if not width or not height:
Expand Down Expand Up @@ -253,20 +248,9 @@ def process(self, instance):
first_thumbnail_component[
"asset_data"]["name"] = extended_asset_name

frame_start = repre.get("frameStartFtrack")
frame_end = repre.get("frameEndFtrack")
if frame_start is None or frame_end is None:
frame_start = instance.data["frameStart"]
frame_end = instance.data["frameEnd"]

# Frame end of uploaded video file should be duration in frames
# - frame start is always 0
# - frame end is duration in frames
duration = frame_end - frame_start + 1

fps = repre.get("fps")
if fps is None:
fps = instance_fps
component_meta = self._prepare_component_metadata(
instance, repre, repre_path, True
)

# Change location
review_item["component_path"] = repre_path
Expand All @@ -275,11 +259,7 @@ def process(self, instance):
# Default component name is "main".
"name": "ftrackreview-mp4",
"metadata": {
"ftr_meta": json.dumps({
"frameIn": 0,
"frameOut": int(duration),
"frameRate": float(fps)
})
"ftr_meta": json.dumps(component_meta)
}
}

Expand Down Expand Up @@ -322,6 +302,13 @@ def process(self, instance):
component_data = copy_src_item["component_data"]
component_name = component_data["name"]
component_data["name"] = component_name + "_src"
component_meta = self._prepare_component_metadata(
instance, repre, copy_src_item["component_path"], False
)
if component_meta:
component_data["metadata"] = {
"ftr_meta": json.dumps(component_meta)
}
component_list.append(copy_src_item)

# Add others representations as component
Expand All @@ -339,9 +326,17 @@ def process(self, instance):
):
other_item["asset_data"]["name"] = extended_asset_name

other_item["component_data"] = {
component_meta = self._prepare_component_metadata(
instance, repre, published_path, False
)
component_data = {
"name": repre["name"]
}
if component_meta:
component_data["metadata"] = {
"ftr_meta": json.dumps(component_meta)
}
other_item["component_data"] = component_data
other_item["component_location_name"] = unmanaged_location_name
other_item["component_path"] = published_path
component_list.append(other_item)
Expand Down Expand Up @@ -424,3 +419,107 @@ def _get_asset_version_status_name(self, instance):
return None

return matching_profile["status"] or None

def _prepare_component_metadata(
self, instance, repre, component_path, is_review
):
extension = os.path.splitext(component_path)[-1]
streams = []
try:
streams = get_ffprobe_streams(component_path)
except Exception:
self.log.debug((
"Failed to retrieve information about intput {}"
).format(component_path))

# Find video streams
video_streams = [
stream
for stream in streams
if stream["codec_type"] == "video"
]
# Skip if there are not video streams
# - exr is special case which can have issues with reading through
# ffmpegh but we want to set fps for it
if not video_streams and extension not in [".exr"]:
return {}

stream_width = None
stream_height = None
stream_fps = None
frame_out = None
for video_stream in video_streams:
tmp_width = video_stream.get("width")
tmp_height = video_stream.get("height")
if tmp_width and tmp_height:
stream_width = tmp_width
stream_height = tmp_height

input_framerate = video_stream.get("r_frame_rate")
duration = video_stream.get("duration")
if input_framerate is None or duration is None:
continue
try:
stream_fps = convert_ffprobe_fps_to_float(
input_framerate
)
except ValueError:
self.log.warning((
"Could not convert ffprobe fps to float \"{}\""
).format(input_framerate))
continue

stream_width = tmp_width
stream_height = tmp_height

self.log.debug("FPS from stream is {} and duration is {}".format(
input_framerate, duration
))
frame_out = float(duration) * stream_fps
break

# Prepare FPS
instance_fps = instance.data.get("fps")
if instance_fps is None:
instance_fps = instance.context.data["fps"]

if not is_review:
output = {}
fps = stream_fps or instance_fps
if fps:
output["frameRate"] = fps

if stream_width and stream_height:
output["width"] = int(stream_width)
output["height"] = int(stream_height)
return output

frame_start = repre.get("frameStartFtrack")
frame_end = repre.get("frameEndFtrack")
if frame_start is None or frame_end is None:
frame_start = instance.data["frameStart"]
frame_end = instance.data["frameEnd"]

fps = None
repre_fps = repre.get("fps")
if repre_fps is not None:
repre_fps = float(repre_fps)

fps = stream_fps or repre_fps or instance_fps

# Frame end of uploaded video file should be duration in frames
# - frame start is always 0
# - frame end is duration in frames
if not frame_out:
frame_out = frame_end - frame_start + 1

# Ftrack documentation says that it is required to have
# 'width' and 'height' in review component. But with those values
# review video does not play.
component_meta = {
"frameIn": 0,
"frameOut": frame_out,
"frameRate": float(fps)
}

return component_meta
1 change: 1 addition & 0 deletions openpype/plugins/publish/extract_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ def _render_output_definitions(
os.unlink(f)

new_repre.update({
"fps": temp_data["fps"],
"name": "{}_{}".format(output_name, output_ext),
"outputName": output_name,
"outputDef": output_def,
Expand Down

0 comments on commit e19f0ba

Please sign in to comment.