fix(worker): render_plan 保留视频原声并尊重每clip音量
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 30s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m34s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m39s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m18s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 39s
AI Code Review / AI Code Review (pull_request) Failing after 2m14s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 32s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m35s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m17s
CI/CD Pipeline / Validate - Code Quality (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
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 / ACR Image Cleanup (pull_request) Failing after 567h28m26s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 567h28m32s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 567h29m13s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 567h30m47s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 567h30m49s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 567h28m28s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 567h29m15s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 567h30m51s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 568h2m20s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 568h3m22s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 30s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m34s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m39s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m18s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 39s
AI Code Review / AI Code Review (pull_request) Failing after 2m14s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 32s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m35s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m17s
CI/CD Pipeline / Validate - Code Quality (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
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 / ACR Image Cleanup (pull_request) Failing after 567h28m26s
CI/CD Pipeline / PR Build Web Image (pull_request) Failing after 567h28m32s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 567h29m13s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 567h30m47s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 567h30m49s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 567h28m28s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 567h29m15s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 567h30m51s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 568h2m20s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 568h3m22s
根因:render_audio.mix_audio 中 main_clips = [] 无条件丢弃主图层源视频 原声(PR #1249 "避免录入杂音"),导致走新路径 render_plan 生成的成片 没有原声,与预览不一致(用户反馈"音频没有")。 修复: - mix_audio 保留 main/broll 原声,按 clip.config.volume 控制音量, 无音频流/静音(volume=0)的 clip 安全过滤 - concat_main_audio 单clip与多clip路径均应用 per-clip volume 滤镜 - 直通渲染(_render_pass_through)改为先 probe_has_audio 再决定是否编码 音频,并应用 per-clip volume;不再无条件假设 main 带音频 - stream copy 在音量非默认时回退重编码 - 新增 _clip_volume 辅助函数(render_audio + UnifiedRenderService) - 更新 test_unified_render_service 旧"丢弃原声"断言为新行为 - 新增 test_original_audio_retained.py 9 个回归测试 验证:161 个相关单测全部通过
This commit is contained in:
@@ -36,6 +36,7 @@ from video_processing.ffmpeg_utils import (
|
||||
DEFAULT_TRANSITION_DURATION,
|
||||
FFMPEG_BIN,
|
||||
probe_duration,
|
||||
probe_has_audio,
|
||||
probe_video_info,
|
||||
run_ffmpeg,
|
||||
)
|
||||
@@ -1000,6 +1001,11 @@ class UnifiedRenderService:
|
||||
if abs(speed - 1.0) >= 1e-6:
|
||||
return False, f"有调速: speed={speed:.2f}x"
|
||||
|
||||
# 音量非默认(静音/放大)→ 需要音频滤镜重编码 → 不能 copy
|
||||
vol = UnifiedRenderService._clip_volume(clip)
|
||||
if abs(vol - 1.0) >= 1e-6:
|
||||
return False, f"音量非默认: volume={vol:.2f}"
|
||||
|
||||
# 有倒放 → 需要重编码 → 不能 copy
|
||||
reverse_config = ReverseConfig.from_dict(clip.config.get("reverse"))
|
||||
if reverse_config.enabled and (reverse_config.reverse_video or reverse_config.reverse_audio):
|
||||
@@ -1270,12 +1276,23 @@ class UnifiedRenderService:
|
||||
"+faststart",
|
||||
]
|
||||
|
||||
# 音频处理:background 通常是图片无音频,跳过;其他编码为 aac
|
||||
# background 以外的视频素材,默认带音频
|
||||
has_audio = role != "background"
|
||||
# 音频处理:background 通常是图片无音频,跳过;其他角色先探测是否真有音频流。
|
||||
# volume=0 表示该片段静音,直接丢弃音频(与多片段 mix_audio 行为一致)。
|
||||
clip_volume = UnifiedRenderService._clip_volume(clip)
|
||||
if role != "background" and clip_volume > 0:
|
||||
try:
|
||||
has_audio = probe_has_audio(clip.local_path)
|
||||
except Exception:
|
||||
has_audio = True
|
||||
else:
|
||||
has_audio = False
|
||||
if has_audio:
|
||||
af_parts: list[str] = []
|
||||
|
||||
# 片段音量(原声=1.0,静音已在上层排除)
|
||||
if abs(clip_volume - 1.0) >= 1e-6:
|
||||
af_parts.append(f"volume={clip_volume:.4f}")
|
||||
|
||||
# 音频降噪
|
||||
try:
|
||||
from video_processing.noise_reduction_engine import NoiseReductionConfig, NoiseReductionEngine
|
||||
@@ -1976,6 +1993,16 @@ class UnifiedRenderService:
|
||||
"""
|
||||
return _clip_playback_speed_pure(getattr(clip, "playback_speed", 1.0))
|
||||
|
||||
@staticmethod
|
||||
def _clip_volume(clip: ResolvedClip) -> float:
|
||||
"""获取 clip 的音量(config.volume)。缺省 1.0 原声,0.0 静音。"""
|
||||
cfg = getattr(clip, "config", None) or {}
|
||||
try:
|
||||
vol = float(cfg.get("volume", 1.0))
|
||||
except (TypeError, ValueError):
|
||||
return 1.0
|
||||
return max(0.0, vol)
|
||||
|
||||
@staticmethod
|
||||
def _clip_adjusted_duration(clip: ResolvedClip) -> float:
|
||||
"""计算调速后的 clip 实际时长(用于拼接计算)。
|
||||
|
||||
Reference in New Issue
Block a user