From d7704005b6b6e74ce2ebcfa657f1a566324d1d19 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Thu, 18 Sep 2025 16:47:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(entity/ffmpeg.py):=20=E6=B7=BB=E5=8A=A0gri?= =?UTF-8?q?d4=E6=95=88=E6=9E=9C=E6=94=AF=E6=8C=81=E5=9C=A8`ffmpeg.py`?= =?UTF-8?q?=E4=B8=AD=E5=A2=9E=E5=8A=A0=E4=BA=86=E5=AF=B9`grid4`=E6=95=88?= =?UTF-8?q?=E6=9E=9C=E7=9A=84=E6=94=AF=E6=8C=81=E3=80=82=E8=AF=A5=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=85=81=E8=AE=B8=E7=94=A8=E6=88=B7=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E5=8F=82=E6=95=B0=E6=9D=A5=E5=88=9B=E5=BB=BA?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=9B=9B=E5=AE=AB=E6=A0=BC=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E5=B8=83=E5=B1=80=EF=BC=8C=E6=AF=8F=E4=B8=AA=E6=A0=BC=E5=AD=90?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=B8=8D=E5=90=8C=E7=9A=84=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E7=89=87=E6=AE=B5=EF=BC=8C=E5=B9=B6=E4=B8=94=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=BB=B6=E8=BF=9F=E6=97=B6=E9=97=B4=E4=BB=A5?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=9B=B4=E4=B8=B0=E5=AF=8C=E7=9A=84=E8=A7=86?= =?UTF-8?q?=E8=A7=89=E6=95=88=E6=9E=9C=E3=80=82=E5=85=B7=E4=BD=93=E6=94=B9?= =?UTF-8?q?=E5=8A=A8=E5=8C=85=E6=8B=AC=EF=BC=9A=20-=20=E8=A7=A3=E6=9E=90`g?= =?UTF-8?q?rid4`=E6=95=88=E6=9E=9C=E7=9A=84=E5=8F=82=E6=95=B0=EF=BC=8C?= =?UTF-8?q?=E5=A6=82=E6=9E=9C=E6=9C=AA=E6=8F=90=E4=BE=9B=E5=88=99=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E4=B8=BA1=E3=80=82=20-=20=E6=A0=B9=E6=8D=AE=E6=8F=90?= =?UTF-8?q?=E4=BE=9B=E7=9A=84=E6=88=96=E9=BB=98=E8=AE=A4=E7=9A=84=E5=88=86?= =?UTF-8?q?=E8=BE=A8=E7=8E=87=E5=88=86=E5=89=B2=E8=A7=86=E9=A2=91=E6=B5=81?= =?UTF-8?q?=E4=B8=BA=E5=9B=9B=E4=B8=AA=E9=83=A8=E5=88=86=E3=80=82=20-?= =?UTF-8?q?=E4=B8=BA=E6=AF=8F=E4=B8=AA=E5=88=86=E5=89=B2=E5=90=8E=E7=9A=84?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E6=B5=81=E5=BA=94=E7=94=A8=E7=BC=A9=E6=94=BE?= =?UTF-8?q?=E5=92=8C=E6=97=B6=E9=97=B4=E5=BB=B6=E8=BF=9F=E5=A4=84=E7=90=86?= =?UTF-8?q?=E3=80=82=20-=20=E5=88=9B=E5=BB=BA=E9=BB=91=E8=89=B2=E8=83=8C?= =?UTF-8?q?=E6=99=AF=E5=B9=B6=E4=BD=BF=E7=94=A8overlay=E6=BB=A4=E9=95=9C?= =?UTF-8?q?=E5=B0=86=E5=A4=84=E7=90=86=E5=90=8E=E7=9A=84=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=B5=81=E6=94=BE=E7=BD=AE=E4=BA=8E=E6=AD=A3=E7=A1=AE=E7=9A=84?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E4=B8=8A=EF=BC=8C=E5=BD=A2=E6=88=90=E6=9C=80?= =?UTF-8?q?=E7=BB=88=E7=9A=84=E5=9B=9B=E5=AE=AB=E6=A0=BC=E5=B8=83=E5=B1=80?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- entity/ffmpeg.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/entity/ffmpeg.py b/entity/ffmpeg.py index 38ced9a..660fc95 100644 --- a/entity/ffmpeg.py +++ b/entity/ffmpeg.py @@ -353,6 +353,42 @@ class FfmpegTask(object): effect_index += 1 filter_args.append(f"{video_output_str}trim=end={show_seconds}[v_eff{effect_index}]") video_output_str = f"[v_eff{effect_index}]" + elif effect.startswith("grid4:"): + param = effect.split(":", 2)[1] + if param == '': + param = "1" + delay_seconds = float(param) + effect_index += 1 + + # 获取分辨率,如果没有设置则使用默认值 + if self.resolution: + width, height = self.resolution.split('x') + else: + width, height = "1920", "1080" # 默认分辨率 + + # 计算四宫格中每个象限的大小 + grid_width = int(width) // 2 + grid_height = int(height) // 2 + + # 分割视频流为4份 + filter_args.append(f"{video_output_str}split=4[grid_v1_{effect_index}][grid_v2_{effect_index}][grid_v3_{effect_index}][grid_v4_{effect_index}]") + + # 创建黑色背景 + filter_args.append(f"color=black:size={width}x{height}:duration=1[bg_{effect_index}]") + + # 缩放每个视频流到绝对尺寸 + filter_args.append(f"[grid_v1_{effect_index}]scale={grid_width}:{grid_height}[v1_scaled_{effect_index}]") + filter_args.append(f"[grid_v2_{effect_index}]scale={grid_width}:{grid_height},tpad=start_duration={delay_seconds}[v2_scaled_{effect_index}]") + filter_args.append(f"[grid_v3_{effect_index}]scale={grid_width}:{grid_height},tpad=start_duration={delay_seconds*2}[v3_scaled_{effect_index}]") + filter_args.append(f"[grid_v4_{effect_index}]scale={grid_width}:{grid_height},tpad=start_duration={delay_seconds*3}[v4_scaled_{effect_index}]") + + # 使用overlay将四个视频流叠加到四个位置 + filter_args.append(f"[bg_{effect_index}][v1_scaled_{effect_index}]overlay=0:0:shortest=1[grid_step1_{effect_index}]") + filter_args.append(f"[grid_step1_{effect_index}][v2_scaled_{effect_index}]overlay=w/2:0:shortest=1[grid_step2_{effect_index}]") + filter_args.append(f"[grid_step2_{effect_index}][v3_scaled_{effect_index}]overlay=0:h/2:shortest=1[grid_step3_{effect_index}]") + filter_args.append(f"[grid_step3_{effect_index}][v4_scaled_{effect_index}]overlay=w/2:h/2:shortest=1[v_eff{effect_index}]") + + video_output_str = f"[v_eff{effect_index}]" ... if self.resolution: filter_args.append(f"{video_output_str}scale={self.resolution.replace('x', ':')}[v]")