You've already forked FrameTour-RenderWorker
244 lines
8.6 KiB
Python
244 lines
8.6 KiB
Python
"""测试FFmpeg命令构建器"""
|
|
import pytest
|
|
from unittest.mock import Mock, patch
|
|
|
|
from entity.ffmpeg_command_builder import FFmpegCommandBuilder
|
|
from entity.render_task import RenderTask
|
|
from config.settings import Config
|
|
from tests.utils.test_helpers import MockRenderTask, FFmpegValidator
|
|
|
|
|
|
class TestFFmpegCommandBuilder:
|
|
"""测试FFmpeg命令构建器"""
|
|
|
|
@pytest.fixture
|
|
def mock_config(self):
|
|
"""模拟配置"""
|
|
return Config(
|
|
encoder_args="-c:v h264",
|
|
video_args="-preset fast",
|
|
template_dir="tests/test_data/templates",
|
|
api_endpoint="http://test.local",
|
|
access_key="test_key"
|
|
)
|
|
|
|
@pytest.fixture
|
|
def simple_task(self):
|
|
"""简单的渲染任务"""
|
|
task = MockRenderTask()
|
|
task.input_files = ["input1.mp4", "input2.mp4"]
|
|
task.output_path = "output.mp4"
|
|
task.effects = []
|
|
return task
|
|
|
|
@pytest.fixture
|
|
def task_with_effects(self):
|
|
"""带特效的渲染任务"""
|
|
task = MockRenderTask()
|
|
task.input_files = ["input.mp4"]
|
|
task.output_path = "output.mp4"
|
|
task.effects = ["zoom:0,2.0,3.0", "ospeed:1.5"]
|
|
task.ext_data = {
|
|
"posJson": '{"ltX": 100, "ltY": 100, "rbX": 200, "rbY": 200, "imgWidth": 300, "imgHeight": 300}'
|
|
}
|
|
return task
|
|
|
|
def test_init(self, simple_task, mock_config):
|
|
"""测试初始化"""
|
|
builder = FFmpegCommandBuilder(simple_task, mock_config)
|
|
assert builder.task == simple_task
|
|
assert builder.config == mock_config
|
|
|
|
def test_build_copy_command(self, simple_task, mock_config):
|
|
"""测试构建复制命令"""
|
|
builder = FFmpegCommandBuilder(simple_task, mock_config)
|
|
command = builder._build_copy_command()
|
|
|
|
# 验证命令结构
|
|
validation = FFmpegValidator.validate_ffmpeg_command(command)
|
|
assert validation["valid"], f"Invalid command: {validation['errors']}"
|
|
assert validation["has_input"]
|
|
assert validation["has_output"]
|
|
|
|
# 验证具体内容
|
|
assert command[0] == "ffmpeg"
|
|
assert "-i" in command
|
|
assert "input1.mp4" in command
|
|
assert "output.mp4" in command
|
|
assert "-c" in command and "copy" in command
|
|
|
|
def test_build_concat_command_multiple_files(self, mock_config):
|
|
"""测试构建多文件拼接命令"""
|
|
task = MockRenderTask()
|
|
task.input_files = ["file1.mp4", "file2.mp4", "file3.mp4"]
|
|
task.output_path = "concat_output.mp4"
|
|
|
|
builder = FFmpegCommandBuilder(task, mock_config)
|
|
command = builder._build_concat_command()
|
|
|
|
validation = FFmpegValidator.validate_ffmpeg_command(command)
|
|
assert validation["valid"]
|
|
assert validation["has_filter"]
|
|
|
|
# 验证拼接相关参数
|
|
assert "concat=n=3:v=1:a=1" in " ".join(command)
|
|
assert all(f"file{i}.mp4" in command for i in range(1, 4))
|
|
|
|
def test_build_encode_command_with_effects(self, task_with_effects, mock_config):
|
|
"""测试构建带特效的编码命令"""
|
|
builder = FFmpegCommandBuilder(task_with_effects, mock_config)
|
|
command = builder._build_encode_command()
|
|
|
|
validation = FFmpegValidator.validate_ffmpeg_command(command)
|
|
assert validation["valid"]
|
|
assert validation["has_filter"]
|
|
|
|
command_str = " ".join(command)
|
|
# 应该包含特效相关的滤镜
|
|
assert "-filter_complex" in command
|
|
# 应该包含编码参数
|
|
assert "-c:v" in command and "h264" in command
|
|
|
|
def test_add_effects_single_effect(self, mock_config):
|
|
"""测试添加单个特效"""
|
|
task = MockRenderTask()
|
|
task.effects = ["zoom:0,2.0,3.0"]
|
|
task.ext_data = {"posJson": "{}"}
|
|
|
|
builder = FFmpegCommandBuilder(task, mock_config)
|
|
filter_args = []
|
|
|
|
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
|
|
|
|
# 验证结果
|
|
assert len(filter_args) > 0 # 应该有滤镜被添加
|
|
assert result_input == "[v_eff1]" # 输出流应该更新
|
|
assert result_index == 2 # 索引应该递增
|
|
|
|
def test_add_effects_multiple_effects(self, mock_config):
|
|
"""测试添加多个特效"""
|
|
task = MockRenderTask()
|
|
task.effects = ["zoom:0,2.0,3.0", "ospeed:1.5"]
|
|
task.ext_data = {"posJson": "{}"}
|
|
|
|
builder = FFmpegCommandBuilder(task, mock_config)
|
|
filter_args = []
|
|
|
|
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
|
|
|
|
# 验证特效链
|
|
assert len(filter_args) >= 2 # 应该有两个特效的滤镜
|
|
assert result_input == "[v_eff2]" # 最终输出流
|
|
assert result_index == 3 # 索引应该递增两次
|
|
|
|
def test_add_effects_invalid_effect(self, mock_config):
|
|
"""测试添加无效特效"""
|
|
task = MockRenderTask()
|
|
task.effects = ["invalid_effect:params"]
|
|
|
|
builder = FFmpegCommandBuilder(task, mock_config)
|
|
filter_args = []
|
|
|
|
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
|
|
|
|
# 无效特效应该被忽略
|
|
assert result_input == "[0:v]" # 输入流不变
|
|
assert result_index == 1 # 索引不变
|
|
|
|
def test_add_effects_no_effects(self, mock_config):
|
|
"""测试无特效情况"""
|
|
task = MockRenderTask()
|
|
task.effects = []
|
|
|
|
builder = FFmpegCommandBuilder(task, mock_config)
|
|
filter_args = []
|
|
|
|
result_input, result_index = builder._add_effects(filter_args, "[0:v]", 1)
|
|
|
|
assert result_input == "[0:v]" # 输入流不变
|
|
assert result_index == 1 # 索引不变
|
|
assert len(filter_args) == 0 # 无滤镜添加
|
|
|
|
def test_build_command_copy_mode(self, simple_task, mock_config):
|
|
"""测试构建复制模式命令"""
|
|
simple_task.input_files = ["single_file.mp4"]
|
|
|
|
builder = FFmpegCommandBuilder(simple_task, mock_config)
|
|
command = builder.build_command()
|
|
|
|
validation = FFmpegValidator.validate_ffmpeg_command(command)
|
|
assert validation["valid"]
|
|
|
|
# 应该是复制模式
|
|
command_str = " ".join(command)
|
|
assert "-c copy" in command_str
|
|
|
|
def test_build_command_concat_mode(self, mock_config):
|
|
"""测试构建拼接模式命令"""
|
|
task = MockRenderTask()
|
|
task.input_files = ["file1.mp4", "file2.mp4"]
|
|
task.effects = []
|
|
|
|
builder = FFmpegCommandBuilder(task, mock_config)
|
|
command = builder.build_command()
|
|
|
|
validation = FFmpegValidator.validate_ffmpeg_command(command)
|
|
assert validation["valid"]
|
|
assert validation["has_filter"]
|
|
|
|
# 应该包含拼接滤镜
|
|
command_str = " ".join(command)
|
|
assert "concat=" in command_str
|
|
|
|
def test_build_command_encode_mode(self, task_with_effects, mock_config):
|
|
"""测试构建编码模式命令"""
|
|
builder = FFmpegCommandBuilder(task_with_effects, mock_config)
|
|
command = builder.build_command()
|
|
|
|
validation = FFmpegValidator.validate_ffmpeg_command(command)
|
|
assert validation["valid"]
|
|
assert validation["has_filter"]
|
|
|
|
# 应该包含编码参数和特效滤镜
|
|
command_str = " ".join(command)
|
|
assert "-c:v h264" in command_str
|
|
|
|
@patch('entity.effects.registry.get_processor')
|
|
def test_effect_processor_integration(self, mock_get_processor, mock_config):
|
|
"""测试与特效处理器的集成"""
|
|
# 模拟特效处理器
|
|
mock_processor = Mock()
|
|
mock_processor.frame_rate = 25
|
|
mock_processor.generate_filter_args.return_value = (
|
|
["[0:v]zoompan=z=2.0:x=iw/2:y=ih/2:d=1[v_eff1]"],
|
|
"[v_eff1]"
|
|
)
|
|
mock_get_processor.return_value = mock_processor
|
|
|
|
task = MockRenderTask()
|
|
task.effects = ["zoom:0,2.0,3.0"]
|
|
task.frame_rate = 30
|
|
|
|
builder = FFmpegCommandBuilder(task, mock_config)
|
|
filter_args = []
|
|
|
|
builder._add_effects(filter_args, "[0:v]", 1)
|
|
|
|
# 验证处理器被正确调用
|
|
mock_processor.generate_filter_args.assert_called_once_with("[0:v]", 1)
|
|
assert mock_processor.frame_rate == 30 # 帧率应该被设置
|
|
|
|
def test_error_handling_missing_input(self, mock_config):
|
|
"""测试缺少输入文件的错误处理"""
|
|
task = MockRenderTask()
|
|
task.input_files = []
|
|
|
|
builder = FFmpegCommandBuilder(task, mock_config)
|
|
|
|
# 构建命令时应该处理错误情况
|
|
# 具体的错误处理依赖于实现
|
|
command = builder.build_command()
|
|
|
|
# 验证至少返回了基本的ffmpeg命令结构
|
|
assert isinstance(command, list)
|
|
assert len(command) > 0 |