Files
xiaoxia-saas/packages/shared/ffmpeg_utils.py
T
CI Bot (saas-backend-agent) e34598aab0
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 1s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 51s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 55s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m31s
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
perf(gpu): mezzanine relay直传跳过公网OSS,CPU preset veryfast,修复CI staging SSH目标
## GPU mezzanine relay直传(核心优化)
- 新增 relay /mezzanine/{key} 接口(PUT/GET/HEAD/DELETE),复用token鉴权
- gpu_encoder 默认走 relay PUT 上传 mezzanine(Tailscale/Docker内网,~1s完成)
- relay上传失败自动fallback到公网OSS,兼容未配Tailscale的环境
- P4000 从 relay_base_url(:8092) GET mezzanine,跳过OSS下载
- 节省公网OSS上传+下载18-20s固定延迟,短视频(1min)GPU路径终于比CPU快
- 新增 GPU_ENCODE_MEZZANINE_TRANSPORT 配置(relay|oss),默认relay

## CPU编码preset
- FFMPEG_ENCODE_PRESET 默认 fast→veryfast,60s720p CPU编码32s→20s(~38%提速)

## CI staging部署修复
- STAGING_SSH_HOST 默认值恢复为 116.62.226.203(真实staging机,DNS指向的机器)
- STAGING_SSH_PORT 默认值 22222→22
- scripts/ci_staging_healthcheck.sh 同步更新
- 注:47.98.113.167 是prod机,之前误设为staging目标导致部署打偏
2026-09-27 14:12:38 +08:00

88 lines
3.7 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""FFmpeg 共享工具 — packages/shared 层.
仅包含与业务无关的底层原语:FFmpeg/FFprobe 二进制路径、run_ffmpeg 执行器。
业务相关的滤镜构建、视频探测等留在 apps/worker/video_processing/ffmpeg_utils.py。
application 层和 worker 层都可以引用本模块,避免跨层依赖。
"""
from __future__ import annotations
import logging
import os
import shutil
import subprocess # nosec B404
logger = logging.getLogger(__name__)
# ── 常量 ──────────────────────────────────────────────────────────────────────
FFMPEG_BIN: str = shutil.which("ffmpeg") or "ffmpeg"
FFPROBE_BIN: str = shutil.which("ffprobe") or "ffprobe"
# FFmpeg 执行默认超时(秒),防止 FFmpeg hang 住导致进程永久阻塞
# 默认 30 分钟,足够处理大部分短视频渲染;超长视频可单独传参覆盖
DEFAULT_FFMPEG_TIMEOUT = 1800
# ── 编码参数(集中配置,支持环境变量覆盖)────────────────────────────────────
# preset 默认 veryfast,相比 fast 再提速 ~40%(4 核 Xeon 60s 720p: 32s→20s),CRF=23 画质可接受
# 可通过环境变量 FFMPEG_ENCODE_PRESET 覆盖(如 fast/medium 追求质量,ultrafast 追求极致速度)
FFMPEG_ENCODE_PRESET: str = os.environ.get("FFMPEG_ENCODE_PRESET", "veryfast")
# CRF 保持 23(libx264 默认质量),可通过 FFMPEG_ENCODE_CRF 覆盖
FFMPEG_ENCODE_CRF: str = os.environ.get("FFMPEG_ENCODE_CRF", "23")
# 编码线程数:0 = 自动检测 CPU 核心数,充分利用多核
FFMPEG_ENCODE_THREADS: str = os.environ.get("FFMPEG_ENCODE_THREADS", "0")
# ── FFmpeg 执行 ───────────────────────────────────────────────────────────────
def run_ffmpeg(
command: list[str],
*,
capture_output: bool = True,
timeout: int | None = DEFAULT_FFMPEG_TIMEOUT,
) -> tuple[str, str]:
"""执行 FFmpeg 命令(统一入口)。
Args:
command: 完整的 ffmpeg 命令列表(含 "ffmpeg" 本身)
capture_output: 是否捕获 stdout/stderr
timeout: 超时时间(秒),默认 1800s(30分钟);None 表示不设超时(不推荐)
Returns:
(stdout, stderr) 元组
Raises:
subprocess.CalledProcessError: 命令执行失败时抛出,
异常信息包含完整 stderr 以便排查。
subprocess.TimeoutExpired: 超时未完成时抛出,FFmpeg 进程会被 kill。
"""
try:
result = subprocess.run( # nosec B603
command,
check=True,
stdout=subprocess.PIPE if capture_output else None,
stderr=subprocess.PIPE if capture_output else None,
text=True,
timeout=timeout,
)
return (result.stdout or "", result.stderr or "")
except subprocess.TimeoutExpired:
logger.error(
"FFmpeg 命令超时 (%ds): command=%s",
timeout or -1,
" ".join(str(c) for c in command[:20]),
)
raise
except subprocess.CalledProcessError as e:
# 把完整 stderr 打到日志,方便排查 exit code 183 等问题
stderr_text = (e.stderr or "").strip()
logger.error(
"FFmpeg 命令失败: exit_code=%d command=%s\nstderr:\n%s",
e.returncode,
" ".join(str(c) for c in command[:20]), # 截断过长的命令
stderr_text[:5000], # 截断过长的 stderr
)
raise