From d4899b69dc90f52901720e66562ac07f4621d43e Mon Sep 17 00:00:00 2001 From: Jakub Jezek Date: Mon, 11 May 2020 19:27:58 +0200 Subject: [PATCH] feat(nks): adding empty black frames at handle start if missing --- .../publish/collect_clip_resolution.py | 21 +++++ .../nukestudio/publish/collect_clips.py | 2 +- .../publish/collect_hierarchy_context.py | 10 +-- .../nukestudio/publish/collect_plates.py | 10 +-- .../nukestudio/publish/collect_reviews.py | 12 ++- .../publish/extract_review_cutup_video.py | 87 ++++++++++++++++--- 6 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 pype/plugins/nukestudio/publish/collect_clip_resolution.py diff --git a/pype/plugins/nukestudio/publish/collect_clip_resolution.py b/pype/plugins/nukestudio/publish/collect_clip_resolution.py new file mode 100644 index 00000000000..b70f8f2f958 --- /dev/null +++ b/pype/plugins/nukestudio/publish/collect_clip_resolution.py @@ -0,0 +1,21 @@ +import pyblish.api + + +class CollectClipResolution(pyblish.api.InstancePlugin): + """Collect clip geometry resolution""" + + order = pyblish.api.CollectorOrder + 0.101 + label = "Collect Clip Resoluton" + hosts = ["nukestudio"] + + def process(self, instance): + sequence = instance.context.data['activeSequence'] + resolution_width = int(sequence.format().width()) + resolution_height = int(sequence.format().height()) + pixel_aspect = sequence.format().pixelAspect() + + instance.data.update({ + "resolutionWidth": resolution_width, + "resolutionHeight": resolution_height, + "pixelAspect": pixel_aspect + }) diff --git a/pype/plugins/nukestudio/publish/collect_clips.py b/pype/plugins/nukestudio/publish/collect_clips.py index 0e92193b14b..81ab9b40ddf 100644 --- a/pype/plugins/nukestudio/publish/collect_clips.py +++ b/pype/plugins/nukestudio/publish/collect_clips.py @@ -124,7 +124,7 @@ def process(self, context): "effects": effects, "sourceIn": int(item.sourceIn()), "sourceOut": int(item.sourceOut()), - "mediaDuration": source.duration(), + "mediaDuration": int(source.duration()), "clipIn": clip_in, "clipOut": clip_out, "clipDuration": ( diff --git a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py index a46baabe1b3..edf08dec6e1 100644 --- a/pype/plugins/nukestudio/publish/collect_hierarchy_context.py +++ b/pype/plugins/nukestudio/publish/collect_hierarchy_context.py @@ -39,9 +39,9 @@ def process(self, context): clip = instance.data["item"] asset = instance.data["asset"] sequence = context.data['activeSequence'] - width = int(sequence.format().width()) - height = int(sequence.format().height()) - pixel_aspect = sequence.format().pixelAspect() + resolution_width = instance.data["resolutionWidth"] + resolution_height = instance.data["resolutionHeight"] + pixel_aspect = instance.data["pixelAspect"] clip_in = instance.data["clipIn"] clip_out = instance.data["clipOut"] fps = context.data["fps"] @@ -187,8 +187,8 @@ def process(self, context): "asset": asset, "hierarchy": hierarchy, "parents": parents, - "resolutionWidth": width, - "resolutionHeight": height, + "resolutionWidth": resolution_width, + "resolutionHeight": resolution_height, "pixelAspect": pixel_aspect, "fps": fps, "tasks": instance.data["tasks"] diff --git a/pype/plugins/nukestudio/publish/collect_plates.py b/pype/plugins/nukestudio/publish/collect_plates.py index 4f21b02e3af..5e8c0ecedca 100644 --- a/pype/plugins/nukestudio/publish/collect_plates.py +++ b/pype/plugins/nukestudio/publish/collect_plates.py @@ -64,15 +64,15 @@ def process(self, instance): # adding SourceResolution if Tag was present if instance.data.get("sourceResolution") and instance.data.get("main"): item = instance.data["item"] - width = int(item.source().mediaSource().width()) - height = int(item.source().mediaSource().height()) + resolution_width = int(item.source().mediaSource().width()) + resolution_height = int(item.source().mediaSource().height()) pixel_aspect = int(item.source().mediaSource().pixelAspect()) self.log.info("Source Width and Height are: `{0} x {1} : {2}`".format( - width, height, pixel_aspect)) + resolution_width, resolution_height, pixel_aspect)) data.update({ - "width": width, - "height": height, + "resolutionWidth": resolution_width, + "resolutionHeight": resolution_height, "pixelAspect": pixel_aspect }) diff --git a/pype/plugins/nukestudio/publish/collect_reviews.py b/pype/plugins/nukestudio/publish/collect_reviews.py index 6919950b777..63b8b7d397d 100644 --- a/pype/plugins/nukestudio/publish/collect_reviews.py +++ b/pype/plugins/nukestudio/publish/collect_reviews.py @@ -110,7 +110,15 @@ def process(self, instance): representation.update({ "frameStart": instance.data.get("sourceInH"), "frameEnd": instance.data.get("sourceOutH"), - "tags": ["cut-up", "delete"] + "tags": ["_cut-bigger", "delete"] + }) + elif media_duration < clip_duration_h: + self.log.debug("Media duration higher: {}".format( + (media_duration - clip_duration_h))) + representation.update({ + "frameStart": instance.data.get("sourceInH"), + "frameEnd": instance.data.get("sourceOutH"), + "tags": ["_cut-smaller", "delete"] }) instance.data["representations"].append(representation) @@ -133,7 +141,7 @@ def create_thumbnail(self, instance): self.log.debug("__ thumb_path: {}".format(thumb_path)) thumb_frame = instance.data["sourceIn"] + ((instance.data["sourceOut"] - instance.data["sourceIn"])/2) - + self.log.debug("__ thumb_frame: {}".format(thumb_frame)) thumbnail = item.thumbnail(thumb_frame).save( thumb_path, format='png' diff --git a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py index 445a26a1841..1db6fbd13cb 100644 --- a/pype/plugins/nukestudio/publish/extract_review_cutup_video.py +++ b/pype/plugins/nukestudio/publish/extract_review_cutup_video.py @@ -6,8 +6,8 @@ class ExtractReviewCutUpVideo(pype.api.Extractor): """Cut up clips from long video file""" - order = api.ExtractorOrder - # order = api.CollectorOrder + 0.1023 + # order = api.ExtractorOrder + order = api.CollectorOrder + 0.1023 label = "Extract Review CutUp Video" hosts = ["nukestudio"] families = ["review"] @@ -22,7 +22,16 @@ def process(self, instance): # get representation and loop them representations = inst_data["representations"] + # resolution data + resolution_width = inst_data["resolutionWidth"] + resolution_height = inst_data["resolutionHeight"] + pixel_aspect = inst_data["pixelAspect"] + + # frame range data + media_duration = inst_data["mediaDuration"] + ffmpeg_path = pype.lib.get_ffmpeg_tool_path("ffmpeg") + ffprobe_path = pype.lib.get_ffmpeg_tool_path("ffprobe") # filter out mov and img sequences representations_new = representations[:] @@ -32,7 +41,9 @@ def process(self, instance): tags = repre.get("tags", []) - if "cut-up" not in tags: + if not next( + (t for t in tags + if t in ["_cut-bigger", "_cut-smaller"]), None): continue self.log.debug("__ repre: {}".format(repre)) @@ -49,21 +60,77 @@ def process(self, instance): full_input_path = os.path.join( staging_dir, file) + full_output_dir = os.path.join( + staging_dir, "cuts") + + os.path.isdir(full_output_dir) or os.makedirs(full_output_dir) + full_output_path = os.path.join( - staging_dir, new_file_name) + full_output_dir, new_file_name) self.log.debug("__ full_input_path: {}".format(full_input_path)) self.log.debug("__ full_output_path: {}".format(full_output_path)) - input_args.append("-y") - input_args.append("-i {}".format(full_input_path)) + # check if audio stream is in input video file + ffprob_cmd = ( + "{ffprobe_path} -i {full_input_path} -show_streams " + "-select_streams a -loglevel error" + ).format(**locals()) + self.log.debug("ffprob_cmd: {}".format(ffprob_cmd)) + audio_check_output = pype.api.subprocess(ffprob_cmd) + self.log.debug("audio_check_output: {}".format(audio_check_output)) + # translate frame to sec start_sec = float(frame_start) / fps + duration_sec = float(frame_end - frame_start + 1) / fps + + input_args.append("-y") + + if start_sec < 0: + audio_empty = "" + audio_output = "" + audio_layer = "" + v_inp_idx = 0 + black_duration = abs(start_sec) + start_sec = 0 + duration_sec = float(frame_end - ( + frame_start + (black_duration * fps)) + 1) / fps + + if audio_check_output: + # adding input for empty audio + input_args.append("-f lavfi -i anullsrc") + audio_empty = ( + "[0]atrim=duration={black_duration}[ga0];" + ).format(**locals()) + audio_output = ":a=1" + audio_layer = "[ga0]" + v_inp_idx = 1 + + # adding input for video black frame + input_args.append(( + "-f lavfi -i \"color=c=black:" + "s={resolution_width}x{resolution_height}:r={fps}\"" + ).format(**locals())) + + # concutting black frame togather + output_args.append(( + "-filter_complex \"" + "{audio_empty}" + "[{v_inp_idx}]trim=duration={black_duration}[gv0];" + "[gv0]{audio_layer}[1:v]" + "concat=n=2:v=1{audio_output}\"" + ).format(**locals())) + input_args.append("-ss {:0.2f}".format(start_sec)) + input_args.append("-t {:0.2f}".format(duration_sec)) + input_args.append("-i {}".format(full_input_path)) - output_args.append("-c copy") - duration_sec = float(frame_end - frame_start + 1) / fps - output_args.append("-t {:0.2f}".format(duration_sec)) + # check if not missing frames at the end + self.log.debug("media_duration: {}".format(media_duration)) + self.log.debug("frame_end: {}".format(frame_end)) + + # make sure it is having no frame to frame comprassion + output_args.append("-intra") # output filename output_args.append(full_output_path) @@ -90,7 +157,7 @@ def process(self, instance): "step": 1, "fps": fps, "name": "cut_up_preview", - "tags": ["cut-up", "review", "delete"] + self.tags_addition, + "tags": ["review", "delete"] + self.tags_addition, "ext": ext, "anatomy_template": "publish" }