You've already forked FrameTour-RenderWorker
feat(video): 支持视频格式叠加层渲染
- 添加对 .mov 视频格式叠加层的支持 - 实现视频叠加层结束后自动消失的功能 - 修改参数传递方式从 has_overlay 改为 overlay_file - 添加 is_video_overlay 参数区分图片和视频叠加层 - 优化 overlay 滤镜参数根据文件类型动态设置 - 更新函数签名和文档注释以支持新的叠加层功能
This commit is contained in:
@@ -123,9 +123,13 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
overlay_file = None
|
overlay_file = None
|
||||||
if render_spec.overlay_url:
|
if render_spec.overlay_url:
|
||||||
# 根据 URL 后缀确定文件扩展名
|
# 根据 URL 后缀确定文件扩展名
|
||||||
ext = '.png'
|
url_lower = render_spec.overlay_url.lower()
|
||||||
if render_spec.overlay_url.lower().endswith('.jpg') or render_spec.overlay_url.lower().endswith('.jpeg'):
|
if url_lower.endswith('.jpg') or url_lower.endswith('.jpeg'):
|
||||||
ext = '.jpg'
|
ext = '.jpg'
|
||||||
|
elif url_lower.endswith('.mov'):
|
||||||
|
ext = '.mov'
|
||||||
|
else:
|
||||||
|
ext = '.png' # 默认
|
||||||
overlay_file = os.path.join(work_dir, f'overlay{ext}')
|
overlay_file = os.path.join(work_dir, f'overlay{ext}')
|
||||||
if not self.download_file(render_spec.overlay_url, overlay_file):
|
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")
|
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,
|
render_spec=render_spec,
|
||||||
output_spec=output_spec,
|
output_spec=output_spec,
|
||||||
lut_file=lut_file,
|
lut_file=lut_file,
|
||||||
has_overlay=overlay_file is not None,
|
overlay_file=overlay_file,
|
||||||
overlap_head_ms=overlap_head_ms,
|
overlap_head_ms=overlap_head_ms,
|
||||||
overlap_tail_ms=overlap_tail_ms
|
overlap_tail_ms=overlap_tail_ms
|
||||||
)
|
)
|
||||||
@@ -376,7 +380,7 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
render_spec: RenderSpec,
|
render_spec: RenderSpec,
|
||||||
output_spec: OutputSpec,
|
output_spec: OutputSpec,
|
||||||
lut_file: Optional[str] = None,
|
lut_file: Optional[str] = None,
|
||||||
has_overlay: bool = False,
|
overlay_file: Optional[str] = None,
|
||||||
overlap_head_ms: int = 0,
|
overlap_head_ms: int = 0,
|
||||||
overlap_tail_ms: int = 0
|
overlap_tail_ms: int = 0
|
||||||
) -> str:
|
) -> str:
|
||||||
@@ -387,7 +391,7 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
render_spec: 渲染规格
|
render_spec: 渲染规格
|
||||||
output_spec: 输出规格
|
output_spec: 输出规格
|
||||||
lut_file: LUT 文件路径
|
lut_file: LUT 文件路径
|
||||||
has_overlay: 是否有叠加层
|
overlay_file: 叠加层文件路径(支持图片 png/jpg 和视频 mov)
|
||||||
overlap_head_ms: 头部 overlap 时长(毫秒)
|
overlap_head_ms: 头部 overlap 时长(毫秒)
|
||||||
overlap_tail_ms: 尾部 overlap 时长(毫秒)
|
overlap_tail_ms: 尾部 overlap 时长(毫秒)
|
||||||
|
|
||||||
@@ -399,6 +403,10 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
height = output_spec.height
|
height = output_spec.height
|
||||||
fps = output_spec.fps
|
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
|
||||||
effects = render_spec.get_effects()
|
effects = render_spec.get_effects()
|
||||||
has_camera_shot = any(e.effect_type == 'cameraShot' for e in effects)
|
has_camera_shot = any(e.effect_type == 'cameraShot' for e in effects)
|
||||||
@@ -459,6 +467,7 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
effects=effects,
|
effects=effects,
|
||||||
fps=fps,
|
fps=fps,
|
||||||
has_overlay=has_overlay,
|
has_overlay=has_overlay,
|
||||||
|
is_video_overlay=is_video_overlay,
|
||||||
overlap_head_ms=overlap_head_ms,
|
overlap_head_ms=overlap_head_ms,
|
||||||
overlap_tail_ms=overlap_tail_ms,
|
overlap_tail_ms=overlap_tail_ms,
|
||||||
use_hwdownload=bool(hwaccel_prefix)
|
use_hwdownload=bool(hwaccel_prefix)
|
||||||
@@ -483,7 +492,10 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
if has_overlay:
|
if has_overlay:
|
||||||
# 使用 filter_complex 格式
|
# 使用 filter_complex 格式
|
||||||
base_filters = ','.join(filters) if filters else 'copy'
|
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:
|
else:
|
||||||
return ','.join(filters) if filters else ''
|
return ','.join(filters) if filters else ''
|
||||||
|
|
||||||
@@ -493,6 +505,7 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
effects: List[Effect],
|
effects: List[Effect],
|
||||||
fps: int,
|
fps: int,
|
||||||
has_overlay: bool = False,
|
has_overlay: bool = False,
|
||||||
|
is_video_overlay: bool = False,
|
||||||
overlap_head_ms: int = 0,
|
overlap_head_ms: int = 0,
|
||||||
overlap_tail_ms: int = 0,
|
overlap_tail_ms: int = 0,
|
||||||
use_hwdownload: bool = False
|
use_hwdownload: bool = False
|
||||||
@@ -507,6 +520,7 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
effects: 特效列表
|
effects: 特效列表
|
||||||
fps: 帧率
|
fps: 帧率
|
||||||
has_overlay: 是否有叠加层
|
has_overlay: 是否有叠加层
|
||||||
|
is_video_overlay: 叠加层是否为视频格式(如 .mov)
|
||||||
overlap_head_ms: 头部 overlap 时长
|
overlap_head_ms: 头部 overlap 时长
|
||||||
overlap_tail_ms: 尾部 overlap 时长
|
overlap_tail_ms: 尾部 overlap 时长
|
||||||
use_hwdownload: 是否使用了硬件加速解码(已在 base_filters 中包含 hwdownload)
|
use_hwdownload: 是否使用了硬件加速解码(已在 base_filters 中包含 hwdownload)
|
||||||
@@ -587,7 +601,10 @@ class RenderSegmentVideoHandler(BaseHandler):
|
|||||||
# 最终输出
|
# 最终输出
|
||||||
if has_overlay:
|
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:
|
else:
|
||||||
# 移除最后一个标签,直接输出
|
# 移除最后一个标签,直接输出
|
||||||
# 将最后一个滤镜的输出标签替换为空(直接输出)
|
# 将最后一个滤镜的输出标签替换为空(直接输出)
|
||||||
|
|||||||
Reference in New Issue
Block a user