From 57130b9924bc4f2c3cb9a6126abce38d94e08046 Mon Sep 17 00:00:00 2001 From: Cyprien CAILLOT Date: Wed, 11 Sep 2024 12:15:40 +0200 Subject: [PATCH] Bug: Fix aspect ratio on all review --- openpype/lib/__init__.py | 2 + openpype/lib/transcoding.py | 52 ++++++++++++++++- openpype/plugins/publish/extract_review.py | 28 ++++++---- .../plugins/publish/extract_review_slate.py | 56 ++----------------- 4 files changed, 76 insertions(+), 62 deletions(-) diff --git a/openpype/lib/__init__.py b/openpype/lib/__init__.py index b3b12ac2507..3a7a4e46cd8 100644 --- a/openpype/lib/__init__.py +++ b/openpype/lib/__init__.py @@ -107,6 +107,7 @@ convert_input_paths_for_ffmpeg, get_ffprobe_data, get_ffprobe_streams, + get_video_metadata, get_ffmpeg_codec_args, get_ffmpeg_format_args, convert_ffprobe_fps_value, @@ -229,6 +230,7 @@ "convert_input_paths_for_ffmpeg", "get_ffprobe_data", "get_ffprobe_streams", + "get_video_metadata", "get_ffmpeg_codec_args", "get_ffmpeg_format_args", "convert_ffprobe_fps_value", diff --git a/openpype/lib/transcoding.py b/openpype/lib/transcoding.py index 1cfe9ac14bc..f9bfb4cdc95 100644 --- a/openpype/lib/transcoding.py +++ b/openpype/lib/transcoding.py @@ -834,6 +834,55 @@ def get_ffprobe_streams(path_to_file, logger=None): return get_ffprobe_data(path_to_file, logger)["streams"] +def get_video_metadata(streams, log=None): + input_timecode = "" + input_width = None + input_height = None + input_frame_rate = None + input_pixel_aspect = None + for stream in streams: + if stream.get("codec_type") != "video": + continue + if log: + log.debug("FFprobe Video: {}".format(stream)) + + if "width" not in stream or "height" not in stream: + continue + width = int(stream["width"]) + height = int(stream["height"]) + if not width or not height: + continue + + # Make sure that width and height are captured even if frame rate + # is not available + input_width = width + input_height = height + + input_pixel_aspect = stream.get("sample_aspect_ratio") + if input_pixel_aspect is not None: + try: + input_pixel_aspect = float( + eval(str(input_pixel_aspect).replace(':', '/'))) + except Exception: + if log: + log.debug("__Converting pixel aspect to float failed: {}".format( + input_pixel_aspect)) + + tags = stream.get("tags") or {} + input_timecode = tags.get("timecode") or "" + + input_frame_rate = stream.get("r_frame_rate") + if input_frame_rate is not None: + break + return ( + input_width, + input_height, + input_timecode, + input_frame_rate, + input_pixel_aspect + ) + + def get_ffmpeg_format_args(ffprobe_data, source_ffmpeg_cmd=None): """Copy format from input metadata for output. @@ -1048,9 +1097,10 @@ def convert_ffprobe_fps_value(str_value): items = str_value.split("/") if len(items) == 1: fps = float(items[0]) - elif len(items) == 2: fps = float(items[0]) / float(items[1]) + else: + return "Unknown" # Check if fps is integer or float number if int(fps) == fps: diff --git a/openpype/plugins/publish/extract_review.py b/openpype/plugins/publish/extract_review.py index 7eef0e66688..f45c9b772f2 100644 --- a/openpype/plugins/publish/extract_review.py +++ b/openpype/plugins/publish/extract_review.py @@ -20,6 +20,7 @@ from openpype.lib.transcoding import ( IMAGE_EXTENSIONS, get_ffprobe_streams, + get_video_metadata, should_convert_for_ffmpeg, get_review_layer_name, convert_input_paths_for_ffmpeg, @@ -1223,7 +1224,7 @@ def get_letterbox_filters( return output def rescaling_filters(self, temp_data, output_def, new_repre): - """Prepare vieo filters based on tags in new representation. + """Prepare video filters based on tags in new representation. It is possible to add letterboxes to output video or rescale to different resolution. @@ -1262,18 +1263,25 @@ def rescaling_filters(self, temp_data, output_def, new_repre): # Try to find first stream with defined 'width' and 'height' # - this is to avoid order of streams where audio can be as first # - there may be a better way (checking `codec_type`?) - input_width = None - input_height = None + + # Get video metadata + ( + input_width, + input_height, + input_timecode, + input_frame_rate, + input_pixel_aspect + ) = get_video_metadata(streams, self.log) + output_width = None output_height = None - for stream in streams: - if "width" in stream and "height" in stream: - input_width = int(stream["width"]) - input_height = int(stream["height"]) - break # Get instance data pixel_aspect = temp_data["pixel_aspect"] + if input_pixel_aspect: + # Use in priority the aspect ratio retrieved from video metadata + pixel_aspect = input_pixel_aspect + if reformat_in_baking: self.log.debug(( "Using resolution from input. It is already " @@ -1283,13 +1291,13 @@ def rescaling_filters(self, temp_data, output_def, new_repre): output_width = input_width output_height = input_height - # Raise exception of any stream didn't define input resolution + # Raise an exception if a stream didn't define input resolution if input_width is None: raise AssertionError(( "FFprobe couldn't read resolution from input file: \"{}\"" ).format(full_input_path_single_file)) - # NOTE Setting only one of `width` or `heigth` is not allowed + # NOTE Setting only one of `width` or `height` is not allowed # - settings value can't have None but has value of 0 output_width = output_def.get("width") or output_width or None output_height = output_def.get("height") or output_height or None diff --git a/openpype/plugins/publish/extract_review_slate.py b/openpype/plugins/publish/extract_review_slate.py index 9c557d1c386..7dda71a2863 100644 --- a/openpype/plugins/publish/extract_review_slate.py +++ b/openpype/plugins/publish/extract_review_slate.py @@ -11,6 +11,7 @@ get_ffmpeg_tool_args, get_ffprobe_data, get_ffprobe_streams, + get_video_metadata, get_ffmpeg_codec_args, get_ffmpeg_format_args, ) @@ -88,11 +89,11 @@ def process(self, instance): input_timecode, input_frame_rate, input_pixel_aspect - ) = self._get_video_metadata(streams) + ) = get_video_metadata(streams, self.log) if input_pixel_aspect: pixel_aspect = input_pixel_aspect - # Raise exception of any stream didn't define input resolution + # Raise an exception if a stream didn't define input resolution if input_width is None: raise KnownPublishError( "FFprobe couldn't read resolution from input file: \"{}\"" @@ -423,60 +424,13 @@ def _get_slates_resolution(self, slate_path): slate_height = int(slate_stream["height"]) break - # Raise exception of any stream didn't define input resolution + # Raise an exception if a stream didn't define input resolution if slate_width is None: raise AssertionError(( "FFprobe couldn't read resolution from input file: \"{}\"" ).format(slate_path)) - return (slate_width, slate_height) - - def _get_video_metadata(self, streams): - input_timecode = "" - input_width = None - input_height = None - input_frame_rate = None - input_pixel_aspect = None - for stream in streams: - if stream.get("codec_type") != "video": - continue - self.log.debug("FFprobe Video: {}".format(stream)) - - if "width" not in stream or "height" not in stream: - continue - width = int(stream["width"]) - height = int(stream["height"]) - if not width or not height: - continue - - # Make sure that width and height are captured even if frame rate - # is not available - input_width = width - input_height = height - - input_pixel_aspect = stream.get("sample_aspect_ratio") - if input_pixel_aspect is not None: - try: - input_pixel_aspect = float( - eval(str(input_pixel_aspect).replace(':', '/'))) - except Exception: - self.log.debug( - "__Converting pixel aspect to float failed: {}".format( - input_pixel_aspect)) - - tags = stream.get("tags") or {} - input_timecode = tags.get("timecode") or "" - - input_frame_rate = stream.get("r_frame_rate") - if input_frame_rate is not None: - break - return ( - input_width, - input_height, - input_timecode, - input_frame_rate, - input_pixel_aspect - ) + return slate_width, slate_height def _get_audio_metadata(self, streams): # Get audio metadata