Files
xiaoxia-saas/tests/unit/test_ffmpeg_encoding_optimization.py
T
saas-backend-agent d13ee7991b
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Has been cancelled
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
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 / Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Lint (pull_request) Blocked by required conditions
CI/CD Pipeline / Frontend Unit Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / PR Build Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Check push changed paths (pull_request) Waiting to run
CI/CD Pipeline / Build Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging E2E Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Staging API Integration Tests (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production API Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Web Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Build Production Worker Image (pull_request) Blocked by required conditions
CI/CD Pipeline / Deploy Production (pull_request) Blocked by required conditions
CI/CD Pipeline / Production Browser E2E (pull_request) Blocked by required conditions
CI/CD Pipeline / ACR Image Cleanup (pull_request) Blocked by required conditions
CI/CD Pipeline / Canary Release to Production (pull_request) Blocked by required conditions
CI/CD Pipeline / CI Gate (pull_request) Blocked by required conditions
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Blocked by required conditions
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
test(tests/unit/test_ffmpeg_encoding_optimization.py): adapt to veryfast preset + gpu mezzanine relay refactor
2026-09-27 14:31:14 +08:00

349 lines
13 KiB
Python
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.
"""Tests for FFmpeg encoding optimization (#1758).
验证:
1. 集中编码常量正确定义,支持环境变量覆盖
2. 所有渲染路径(_execute_ffmpeg / _render_pass_through / normalize_video / processor)
使用统一的编码参数
3. preset 从 medium → veryfast(#2063 优化 fast→veryfast,再提速 38%)
4. threads=0 自动检测 CPU 核心数
"""
from __future__ import annotations
import importlib
import os
import sys
from unittest.mock import MagicMock, patch
import pytest
# ── 常量定义与默认值 ─────────────────────────────────────────────────────────
class TestEncodingConstants:
"""测试集中编码常量的定义与默认值."""
def test_default_preset_is_fast(self):
"""默认 preset 应为 fast(非 medium),确保速度提升."""
from shared.ffmpeg_utils import FFMPEG_ENCODE_PRESET
assert FFMPEG_ENCODE_PRESET == "veryfast"
def test_default_crf_is_23(self):
"""默认 CRF 保持 23,画质不变."""
from shared.ffmpeg_utils import FFMPEG_ENCODE_CRF
assert FFMPEG_ENCODE_CRF == "23"
def test_default_threads_is_auto(self):
"""默认线程数为 0(自动检测 CPU 核心数)."""
from shared.ffmpeg_utils import FFMPEG_ENCODE_THREADS
assert FFMPEG_ENCODE_THREADS == "0"
def test_preset_is_not_medium(self):
"""确保 preset 不再是 medium(验证改动生效)."""
from shared.ffmpeg_utils import FFMPEG_ENCODE_PRESET
assert FFMPEG_ENCODE_PRESET != "medium"
# ── 环境变量覆盖 ─────────────────────────────────────────────────────────────
class TestEnvironmentVariableOverride:
"""测试环境变量覆盖编码参数."""
def _reload_module(self):
"""重新加载模块以读取环境变量."""
import shared.ffmpeg_utils
return importlib.reload(shared.ffmpeg_utils)
def test_override_preset_via_env(self, monkeypatch):
"""通过环境变量覆盖 preset."""
monkeypatch.setenv("FFMPEG_ENCODE_PRESET", "ultrafast")
mod = self._reload_module()
assert mod.FFMPEG_ENCODE_PRESET == "ultrafast"
# 恢复
monkeypatch.delenv("FFMPEG_ENCODE_PRESET", raising=False)
self._reload_module()
def test_override_crf_via_env(self, monkeypatch):
"""通过环境变量覆盖 CRF."""
monkeypatch.setenv("FFMPEG_ENCODE_CRF", "18")
mod = self._reload_module()
assert mod.FFMPEG_ENCODE_CRF == "18"
monkeypatch.delenv("FFMPEG_ENCODE_CRF", raising=False)
self._reload_module()
def test_override_threads_via_env(self, monkeypatch):
"""通过环境变量覆盖线程数."""
monkeypatch.setenv("FFMPEG_ENCODE_THREADS", "4")
mod = self._reload_module()
assert mod.FFMPEG_ENCODE_THREADS == "4"
monkeypatch.delenv("FFMPEG_ENCODE_THREADS", raising=False)
self._reload_module()
# ── Worker 层 re-export ──────────────────────────────────────────────────────
class TestWorkerReExport:
"""测试 worker 层 ffmpeg_utils 正确 re-export 编码常量."""
def test_worker_reexports_preset(self):
"""worker ffmpeg_utils 应 re-export FFMPEG_ENCODE_PRESET."""
from video_processing.ffmpeg_utils import FFMPEG_ENCODE_PRESET
assert FFMPEG_ENCODE_PRESET == "veryfast"
def test_worker_reexports_crf(self):
"""worker ffmpeg_utils 应 re-export FFMPEG_ENCODE_CRF."""
from video_processing.ffmpeg_utils import FFMPEG_ENCODE_CRF
assert FFMPEG_ENCODE_CRF == "23"
def test_worker_reexports_threads(self):
"""worker ffmpeg_utils 应 re-export FFMPEG_ENCODE_THREADS."""
from video_processing.ffmpeg_utils import FFMPEG_ENCODE_THREADS
assert FFMPEG_ENCODE_THREADS == "0"
# ── _execute_ffmpeg 编码参数 ─────────────────────────────────────────────────
class TestExecuteFfmpegEncoding:
"""测试 _execute_ffmpeg 方法使用正确的编码参数."""
def _get_execute_command(self):
"""提取 _execute_ffmpeg 构建的 FFmpeg 命令."""
from video_processing.unified_render_service import UnifiedRenderService
service = UnifiedRenderService.__new__(UnifiedRenderService)
service.plan = MagicMock()
service.plan.id = "test-plan"
captured_cmd = []
def mock_run_ffmpeg(cmd, **kwargs):
captured_cmd.extend(cmd)
with patch(
"video_processing.unified_render_service.run_ffmpeg",
side_effect=mock_run_ffmpeg,
):
service._execute_ffmpeg(
"test_filter_complex",
["-i", "input.mp4"],
__import__("pathlib").Path("/tmp/output.mp4"),
)
return captured_cmd
def test_execute_uses_veryfast_preset(self):
"""_execute_ffmpeg 应使用 veryfast preset."""
cmd = self._get_execute_command()
idx = cmd.index("-preset")
assert cmd[idx + 1] == "veryfast"
def test_execute_uses_crf_23(self):
"""_execute_ffmpeg 应使用 CRF 23."""
cmd = self._get_execute_command()
idx = cmd.index("-crf")
assert cmd[idx + 1] == "23"
def test_execute_uses_auto_threads(self):
"""_execute_ffmpeg 应启用多线程."""
cmd = self._get_execute_command()
idx = cmd.index("-threads")
assert cmd[idx + 1] == "0"
def test_execute_no_medium_preset(self):
"""_execute_ffmpeg 不应再使用 medium preset."""
cmd = self._get_execute_command()
assert "medium" not in cmd
# ── _render_pass_through 编码参数 ────────────────────────────────────────────
class TestRenderPassThroughEncoding:
"""测试 _render_pass_through 方法使用正确的编码参数."""
def test_passthrough_command_contains_veryfast_preset(self):
"""_render_pass_through 命令应包含 veryfast preset."""
# 通过源码检查确认参数已替换
import inspect
from video_processing.unified_render_service import UnifiedRenderService
source = inspect.getsource(UnifiedRenderService._render_pass_through)
assert "FFMPEG_ENCODE_PRESET" in source
assert "FFMPEG_ENCODE_THREADS" in source
assert '"medium"' not in source
def test_passthrough_command_contains_threads(self):
"""_render_pass_through 命令应包含 threads 参数."""
import inspect
from video_processing.unified_render_service import UnifiedRenderService
source = inspect.getsource(UnifiedRenderService._render_pass_through)
assert "FFMPEG_ENCODE_THREADS" in source
# ── normalize_video 编码参数 ─────────────────────────────────────────────────
class TestNormalizeVideoEncoding:
"""测试 normalize_video 使用正确的编码参数."""
def test_normalize_uses_veryfast_preset(self):
"""normalize_video 应使用 veryfast preset."""
import inspect
from video_processing.ffmpeg_utils import normalize_video
source = inspect.getsource(normalize_video)
assert "FFMPEG_ENCODE_PRESET" in source
assert '"medium"' not in source
def test_normalize_uses_configurable_crf(self):
"""normalize_video CRF 应使用可配置常量."""
import inspect
from video_processing.ffmpeg_utils import normalize_video
source = inspect.getsource(normalize_video)
assert "FFMPEG_ENCODE_CRF" in source
def test_normalize_uses_threads(self):
"""normalize_video 应启用多线程."""
import inspect
from video_processing.ffmpeg_utils import normalize_video
source = inspect.getsource(normalize_video)
assert "FFMPEG_ENCODE_THREADS" in source
# ── random_edge_crop 编码参数 ────────────────────────────────────────────────
class TestRandomEdgeCropEncoding:
"""测试 random_edge_crop 函数使用统一编码参数."""
def test_crop_uses_unified_preset(self):
"""random_edge_crop 应使用统一 preset 常量(非硬编码 fast)."""
import inspect
from video_processing.ffmpeg_utils import random_edge_crop
source = inspect.getsource(random_edge_crop)
assert "FFMPEG_ENCODE_PRESET" in source
def test_crop_uses_unified_crf(self):
"""random_edge_crop 应使用统一 CRF 常量(非硬编码 18)."""
import inspect
from video_processing.ffmpeg_utils import random_edge_crop
source = inspect.getsource(random_edge_crop)
assert "FFMPEG_ENCODE_CRF" in source
assert '"18"' not in source
# ── processor.py 编码参数 ────────────────────────────────────────────────────
class TestProcessorEncoding:
"""测试 VideoProcessor 使用统一编码参数."""
def test_processor_uses_unified_preset(self):
"""VideoProcessor.concatenate_videos 应使用统一 preset."""
import inspect
from video_processing.processor import VideoProcessor
source = inspect.getsource(VideoProcessor.concatenate_videos)
assert "FFMPEG_ENCODE_PRESET" in source
assert 'preset="medium"' not in source
def test_processor_uses_unified_crf(self):
"""VideoProcessor.concatenate_videos 应使用统一 CRF."""
import inspect
from video_processing.processor import VideoProcessor
source = inspect.getsource(VideoProcessor.concatenate_videos)
assert "FFMPEG_ENCODE_CRF" in source
assert "crf=23" not in source
# ── 编码参数一致性验证 ────────────────────────────────────────────────────────
class TestEncodingConsistency:
"""验证所有渲染路径使用同一套编码参数."""
def test_all_paths_share_same_preset_source(self):
"""所有路径的 preset 均来自 FFMPEG_ENCODE_PRESET."""
import inspect
from video_processing.ffmpeg_utils import normalize_video, random_edge_crop
from video_processing.processor import VideoProcessor
from video_processing.unified_render_service import UnifiedRenderService
sources = [
inspect.getsource(UnifiedRenderService._execute_ffmpeg),
inspect.getsource(UnifiedRenderService._render_pass_through),
inspect.getsource(normalize_video),
inspect.getsource(random_edge_crop),
inspect.getsource(VideoProcessor.concatenate_videos),
]
for i, src in enumerate(sources):
assert "FFMPEG_ENCODE_PRESET" in src, f"渲染路径 #{i} 未使用 FFMPEG_ENCODE_PRESET"
def test_no_hardcoded_medium_preset_anywhere(self):
"""所有渲染代码中不应再有硬编码的 medium preset."""
import inspect
from video_processing.ffmpeg_utils import normalize_video
from video_processing.unified_render_service import UnifiedRenderService
for fn in [
UnifiedRenderService._execute_ffmpeg,
UnifiedRenderService._render_pass_through,
normalize_video,
]:
src = inspect.getsource(fn)
assert '"medium"' not in src, f"{fn.__name__} 仍有硬编码 medium preset"
def test_no_hardcoded_crf_23_anywhere(self):
"""所有渲染代码中不应再有硬编码的 CRF 23."""
import inspect
from video_processing.ffmpeg_utils import normalize_video
from video_processing.unified_render_service import UnifiedRenderService
for fn in [
UnifiedRenderService._execute_ffmpeg,
UnifiedRenderService._render_pass_through,
normalize_video,
]:
src = inspect.getsource(fn)
# 检查没有硬编码的 "23" 作为 CRF 值
# 注意:行中可能有其他 "23",只检查紧跟 -crf 之后的值
lines = src.split("\n")
for j, line in enumerate(lines):
if '"-crf"' in line or "'-crf'" in line:
# 下一个非空行应该是常量引用而非硬编码数字
for k in range(j + 1, min(j + 3, len(lines))):
stripped = lines[k].strip().strip(",").strip('"').strip("'")
if stripped:
assert stripped != "23", f"{fn.__name__} 仍有硬编码 CRF 23"
break