diff --git a/biz/ffmpeg.py b/biz/ffmpeg.py index b58c86a..ba2e130 100644 --- a/biz/ffmpeg.py +++ b/biz/ffmpeg.py @@ -28,6 +28,8 @@ def parse_ffmpeg_task(task_info, template_info): sub_ffmpeg_task = FfmpegTask(source) sub_ffmpeg_task.annexb = True sub_ffmpeg_task.frame_rate = template_info.get("frame_rate", 25) + for effect in part.get('effects', []): + sub_ffmpeg_task.add_effect(effect) for lut in part.get('filters', []): sub_ffmpeg_task.add_lut(os.path.join(template_info.get("local_path"), lut)) for audio in part.get('audios', []): @@ -43,6 +45,8 @@ def parse_ffmpeg_task(task_info, template_info): if overall.get('source', ''): source = parse_video(overall.get('source'), task_params, template_info) task.add_inputs(source) + for effect in overall.get('effects', []): + task.add_effect(effect) for lut in overall.get('filters', []): task.add_lut(os.path.join(template_info.get("local_path"), lut)) for audio in overall.get('audios', []): diff --git a/entity/ffmpeg.py b/entity/ffmpeg.py index 1328348..7bdf91a 100644 --- a/entity/ffmpeg.py +++ b/entity/ffmpeg.py @@ -1,5 +1,6 @@ import time import uuid +from typing import Any ENCODER_ARGS = ("-c:v", "h264_qsv", "-global_quality", "28", "-look_ahead", "1",) PROFILE_LEVEL_ARGS = ("-profile:v", "high", "-level:v", "4") @@ -7,6 +8,8 @@ PROFILE_LEVEL_ARGS = ("-profile:v", "high", "-level:v", "4") class FfmpegTask(object): + effects: list[str] + def __init__(self, input_file, task_type='copy', output_file=''): self.annexb = False if type(input_file) is str: @@ -29,6 +32,7 @@ class FfmpegTask(object): self.luts = [] self.audios = [] self.overlays = [] + self.effects = [] def __repr__(self): _str = f'FfmpegTask(input_file={self.input_file}, task_type={self.task_type}' @@ -40,6 +44,8 @@ class FfmpegTask(object): _str += f', overlays={self.overlays}' if self.annexb: _str += f', annexb={self.annexb}' + if self.effects: + _str += f', effects={self.effects}' if self.mute: _str += f', mute={self.mute}' return _str + ')' @@ -83,6 +89,10 @@ class FfmpegTask(object): self.luts.extend(luts) self.correct_task_type() + def add_effect(self, *effects): + self.effects.extend(effects) + self.correct_task_type() + def get_output_file(self): if self.task_type == 'copy': return self.input_file[0] @@ -105,6 +115,8 @@ class FfmpegTask(object): return False if len(self.subtitles) > 0: return False + if len(self.effects) > 0: + return False if self.speed != 1: return False if self.zoom_cut is not None: @@ -120,6 +132,8 @@ class FfmpegTask(object): return False if len(self.subtitles) > 0: return False + if len(self.effects) > 0: + return False if self.speed != 1: return False if len(self.audios) > 1: @@ -141,7 +155,7 @@ class FfmpegTask(object): if self.task_type == 'encode': input_args = [] filter_args = [] - output_args = ["-profile", "high", "-level", "4","-shortest", *ENCODER_ARGS] + output_args = [*PROFILE_LEVEL_ARGS,"-shortest", *ENCODER_ARGS] if self.annexb: output_args.append("-bsf:v") output_args.append("h264_mp4toannexb") @@ -162,6 +176,29 @@ class FfmpegTask(object): _f_x = pos_json.get('ltX', 0) _x = f'{float(_f_x/_v_w) :.5f}*iw' filter_args.append(f"[{video_output_str}]crop=x={_x}:y=0:w=ih*ih/iw:h=ih[{video_output_str}]") + for effect in self.effects: + if effect.startswith("cameraShot:"): + param = effect.split(":", 2)[1] + if param == '': + param = "3,1" + _split = param.split(",") + start = 3 + duration = 1 + if len(_split) >= 2: + start = int(_split[0]) + duration = int(_split[1]) + elif len(_split) == 1: + start = int(_split[0]) + _start_out_str = "[eff_s]" + _end_out_str = "[eff_e]" + filter_args.append(f"{video_output_str}fps=fps={self.frame_rate},split{_start_out_str}{_end_out_str}") + filter_args.append(f"{_start_out_str}trim=start={0}:end={start+duration},freezeframes=first={start*self.frame_rate}:replace={start*self.frame_rate}{_start_out_str}") + filter_args.append(f"{_end_out_str}trim=start={start}{_end_out_str}") + video_output_str = "[v_eff]" + filter_args.append(f"{_start_out_str}{_end_out_str}concat=n=2:v=1:a=0{video_output_str}") + elif effect.startswith("zoom:"): + ... + ... for lut in self.luts: filter_args.append(f"[{video_output_str}]lut3d=file={lut}[{video_output_str}]") for overlay in self.overlays: diff --git a/util/api.py b/util/api.py index f264490..8f2550b 100644 --- a/util/api.py +++ b/util/api.py @@ -105,6 +105,9 @@ def get_template_info(template_id): _only_if = template_info.get('onlyIf', '') if _only_if: _template['only_if'] = _only_if + _effects = template_info.get('effects', '') + if _effects: + _template['effects'] = _effects.split("|") return _template # outer template definition