feat: BGM音轨混音能力(音量/淡入淡出/人声闪避/预设BGM库) (#291)
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 43s
CI/CD Pipeline / Unit Tests (push) Successful in 1m35s
CI/CD Pipeline / Frontend Lint (push) Successful in 1m37s
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
CI/CD Pipeline / Validate Code Quality And Tests (push) Successful in 43s
CI/CD Pipeline / Unit Tests (push) Successful in 1m35s
CI/CD Pipeline / Frontend Lint (push) Successful in 1m37s
CI/CD Pipeline / Build Production Runtime Images (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (push) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (push) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (push) Has been cancelled
This commit was merged in pull request #291.
This commit is contained in:
Regular → Executable
+33
-3
@@ -70,6 +70,9 @@ def mix_audio(
|
||||
ctx: RenderContext,
|
||||
layers: list[RenderLayer],
|
||||
video_duration: float,
|
||||
*,
|
||||
bgm_path: str | None = None,
|
||||
bgm_config: dict | None = None,
|
||||
) -> Path | None:
|
||||
"""音频后处理混音.
|
||||
|
||||
@@ -79,11 +82,14 @@ def mix_audio(
|
||||
3. 独立音频轨(audio role)用 amix 混入
|
||||
4. 输出时长截断到 video_duration
|
||||
5. 无音频流的 clip 会被自动跳过,避免 FFmpeg 引用 [i:a] 失败
|
||||
6. 如果提供了 bgm_path,则额外混入 BGM(支持淡入淡出、循环、人声闪避)
|
||||
|
||||
Args:
|
||||
ctx: 渲染上下文
|
||||
layers: 图层列表
|
||||
video_duration: 视频总时长(用于截断音频)
|
||||
bgm_path: BGM 音频本地路径,为 None 时不混入 BGM
|
||||
bgm_config: BGM 配置字典(volume/fade_in/fade_out/sidechain 等)
|
||||
|
||||
Returns:
|
||||
混音后的音频文件路径,无音频时返回 None
|
||||
@@ -116,6 +122,15 @@ def mix_audio(
|
||||
audio_clips = [c for c in audio_clips if clip_has_audio(ctx, c)]
|
||||
|
||||
if not main_clips and not audio_clips:
|
||||
# 没有主音频也没有独立音频 → 检查是否有 BGM
|
||||
if bgm_path and bgm_config and bgm_config.get("enabled", False):
|
||||
from video_processing.bgm_mixer import BGMConfig, build_bgm_only
|
||||
|
||||
bgm_cfg = BGMConfig.from_config_dict(bgm_path, bgm_config)
|
||||
try:
|
||||
return build_bgm_only(ctx, bgm_cfg, video_duration)
|
||||
except Exception:
|
||||
logger.exception("[bgm] 纯BGM生成失败: plan_id=%s", ctx.plan_id)
|
||||
return None
|
||||
|
||||
# 构建音频处理命令
|
||||
@@ -124,10 +139,25 @@ def mix_audio(
|
||||
# 简单场景:只有主图层 + 无独立音频 → 直接从视频提取音频并拼接
|
||||
if main_clips and not audio_clips:
|
||||
concat_main_audio(ctx, main_clips, output_path, video_duration)
|
||||
return output_path
|
||||
else:
|
||||
# 有独立音频轨 → amix 混音
|
||||
mix_with_independent_audio(ctx, main_clips, audio_clips, output_path, video_duration)
|
||||
|
||||
# ── BGM 混音 ──
|
||||
if bgm_path and bgm_config and bgm_config.get("enabled", False):
|
||||
from video_processing.bgm_mixer import BGMConfig, mix_bgm_with_main
|
||||
|
||||
bgm_cfg = BGMConfig.from_config_dict(bgm_path, bgm_config)
|
||||
bgm_output = ctx.work_dir / f"audio_with_bgm_{ctx.plan_id}.aac"
|
||||
|
||||
try:
|
||||
# 这里 main_audio 就是 output_path,先有主音频再混 BGM
|
||||
final_path = mix_bgm_with_main(ctx, output_path, bgm_cfg, video_duration)
|
||||
return final_path
|
||||
except Exception:
|
||||
logger.exception("[bgm] BGM 混音失败,回退到无 BGM 音频: plan_id=%s", ctx.plan_id)
|
||||
return output_path
|
||||
|
||||
# 有独立音频轨 → amix 混音
|
||||
mix_with_independent_audio(ctx, main_clips, audio_clips, output_path, video_duration)
|
||||
return output_path
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user