From 0a7a0dac891c8ecfb9077070dc8c6b1208cc1a0a Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Wed, 21 Jan 2026 15:24:58 +0800 Subject: [PATCH] =?UTF-8?q?feat(video):=20=E6=94=AF=E6=8C=81=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=A0=BC=E5=BC=8F=E5=8F=A0=E5=8A=A0=E5=B1=82=E6=B8=B2?= =?UTF-8?q?=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加对 .mov 视频格式叠加层的支持 - 实现视频叠加层结束后自动消失的功能 - 修改参数传递方式从 has_overlay 改为 overlay_file - 添加 is_video_overlay 参数区分图片和视频叠加层 - 优化 overlay 滤镜参数根据文件类型动态设置 - 更新函数签名和文档注释以支持新的叠加层功能 --- handlers/render_video.py | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/handlers/render_video.py b/handlers/render_video.py index 9f9982c..b4f0622 100644 --- a/handlers/render_video.py +++ b/handlers/render_video.py @@ -123,9 +123,13 @@ class RenderSegmentVideoHandler(BaseHandler): overlay_file = None if render_spec.overlay_url: # 根据 URL 后缀确定文件扩展名 - ext = '.png' - if render_spec.overlay_url.lower().endswith('.jpg') or render_spec.overlay_url.lower().endswith('.jpeg'): + url_lower = render_spec.overlay_url.lower() + if url_lower.endswith('.jpg') or url_lower.endswith('.jpeg'): ext = '.jpg' + elif url_lower.endswith('.mov'): + ext = '.mov' + else: + ext = '.png' # 默认 overlay_file = os.path.join(work_dir, f'overlay{ext}') if not self.download_file(render_spec.overlay_url, overlay_file): logger.warning(f"[task:{task.task_id}] Failed to download overlay, continuing without it") @@ -332,7 +336,7 @@ class RenderSegmentVideoHandler(BaseHandler): render_spec=render_spec, output_spec=output_spec, lut_file=lut_file, - has_overlay=overlay_file is not None, + overlay_file=overlay_file, overlap_head_ms=overlap_head_ms, overlap_tail_ms=overlap_tail_ms ) @@ -376,7 +380,7 @@ class RenderSegmentVideoHandler(BaseHandler): render_spec: RenderSpec, output_spec: OutputSpec, lut_file: Optional[str] = None, - has_overlay: bool = False, + overlay_file: Optional[str] = None, overlap_head_ms: int = 0, overlap_tail_ms: int = 0 ) -> str: @@ -387,7 +391,7 @@ class RenderSegmentVideoHandler(BaseHandler): render_spec: 渲染规格 output_spec: 输出规格 lut_file: LUT 文件路径 - has_overlay: 是否有叠加层 + overlay_file: 叠加层文件路径(支持图片 png/jpg 和视频 mov) overlap_head_ms: 头部 overlap 时长(毫秒) overlap_tail_ms: 尾部 overlap 时长(毫秒) @@ -399,6 +403,10 @@ class RenderSegmentVideoHandler(BaseHandler): height = output_spec.height fps = output_spec.fps + # 判断 overlay 类型 + has_overlay = overlay_file is not None + is_video_overlay = has_overlay and overlay_file.lower().endswith('.mov') + # 解析 effects effects = render_spec.get_effects() has_camera_shot = any(e.effect_type == 'cameraShot' for e in effects) @@ -459,6 +467,7 @@ class RenderSegmentVideoHandler(BaseHandler): effects=effects, fps=fps, has_overlay=has_overlay, + is_video_overlay=is_video_overlay, overlap_head_ms=overlap_head_ms, overlap_tail_ms=overlap_tail_ms, use_hwdownload=bool(hwaccel_prefix) @@ -483,7 +492,10 @@ class RenderSegmentVideoHandler(BaseHandler): if has_overlay: # 使用 filter_complex 格式 base_filters = ','.join(filters) if filters else 'copy' - return f"[0:v]{base_filters}[base];[base][1:v]overlay=0:0" + # 视频 overlay 使用 eof_action=pass(结束后消失),图片 overlay 使用默认行为(保持显示) + overlay_params = 'eof_action=pass' if is_video_overlay else '' + overlay_filter = f"overlay=0:0:{overlay_params}" if overlay_params else 'overlay=0:0' + return f"[0:v]{base_filters}[base];[base][1:v]{overlay_filter}" else: return ','.join(filters) if filters else '' @@ -493,6 +505,7 @@ class RenderSegmentVideoHandler(BaseHandler): effects: List[Effect], fps: int, has_overlay: bool = False, + is_video_overlay: bool = False, overlap_head_ms: int = 0, overlap_tail_ms: int = 0, use_hwdownload: bool = False @@ -507,6 +520,7 @@ class RenderSegmentVideoHandler(BaseHandler): effects: 特效列表 fps: 帧率 has_overlay: 是否有叠加层 + is_video_overlay: 叠加层是否为视频格式(如 .mov) overlap_head_ms: 头部 overlap 时长 overlap_tail_ms: 尾部 overlap 时长 use_hwdownload: 是否使用了硬件加速解码(已在 base_filters 中包含 hwdownload) @@ -587,7 +601,10 @@ class RenderSegmentVideoHandler(BaseHandler): # 最终输出 if has_overlay: # 叠加层处理 - filter_parts.append(f"{current_output}[1:v]overlay=0:0") + # 视频 overlay 使用 eof_action=pass(结束后消失),图片 overlay 使用默认行为(保持显示) + overlay_params = 'eof_action=pass' if is_video_overlay else '' + overlay_filter = f"overlay=0:0:{overlay_params}" if overlay_params else 'overlay=0:0' + filter_parts.append(f"{current_output}[1:v]{overlay_filter}") else: # 移除最后一个标签,直接输出 # 将最后一个滤镜的输出标签替换为空(直接输出)