音频处理与音视频融合接口设计文档
文档信息
| 项目 | 内容 |
|---|
| 标题 | /api/audio/process/ 音频处理与音视频融合接口设计文档 |
| 版本 | v1.0 |
| 作者 | - |
| 日期 | 2024-12-24 |
| 适用范围 | TransDubPlatform 译配平台 - 音频处理模块 |
目录
1. 背景与现状
1.1 业务背景
TransDubPlatform(译配平台)是一个视频翻译配音平台,核心业务流程包括:
- 视频上传与音频提取
- 语音分离(人声/背景音分离)
- 语音识别(ASR)生成台本
- 台本翻译
- AI配音(TTS)与音频合并
- 音视频融合输出
本文档聚焦于 /api/audio/process/{dubDetailId} 接口,该接口负责:
- 将多段TTS生成的配音音频按时间轴合并为完整音轨
- 将合并后的人声配音与背景音、原视频进行融合
- 输出最终的配音视频文件
依据: transdubplatform-service/.../AudioController.java
1.2 技术背景
| 技术栈 | 说明 | 依据 |
|---|
| 语言/框架 | Java 8 + Spring Boot | Dockerfile, build.gradle |
| 音视频处理 | FFmpeg (内嵌于JAR包) | Dockerfile, FfmpegInitializer.java |
| 对象存储 | 阿里云OSS | OSSService.java, application.yml |
| 数据库 | MySQL | application.yml (druid配置) |
| 缓存 | Redis | application.yml |
1.3 当前痛点与约束
| 痛点/约束 | 说明 | 依据 |
|---|
| 性能瓶颈 | 大量音频片段顺序合并耗时长 | AudioSilenceAdder.java 采用归并树优化 |
| 视频重编码耗时 | 原方案需重新编码视频流 | VideoAudioMergeService.java 使用 -c:v copy 优化 |
| 背景音可选 | 部分场景无背景音,需兼容处理 | 新增 checkBackgroundAudioExists() 方法 |
| 临时文件管理 | 大量临时文件需及时清理 | cleanupTempDirectory() 方法 |
2. 目标与非目标
2.1 目标(Goals)
| 目标 | 衡量指标/验收方式 |
|---|
| G1: 支持多段音频按时间轴精确合并 | 输出音频与原视频时间轴对齐,误差 < 50ms |
| G2: 支持有/无背景音两种融合模式 | 背景音不存在时自动切换为纯人声替换模式 |
| G3: 视频流零重编码 | FFmpeg命令使用 -c:v copy,处理速度提升10倍以上 |
| G4: 并行处理优化 | 文件下载并行化,音频合并采用归并树并行 |
| G5: 输出MP4格式视频 | 输出文件可直接播放,支持流媒体快速启动 |
2.2 非目标(Non-Goals)
| 非目标 | 说明 |
|---|
| NG1: 视频转码/分辨率调整 | 本接口不处理视频画面,仅处理音轨 |
| NG2: 实时流处理 | 本接口为批处理模式,不支持实时流 |
| NG3: 多语言音轨 | 当前仅支持单一目标语言音轨输出 |
3. 术语表与关键实体
3.1 术语表
| 术语 | 定义 |
|---|
| dubDetailId | 配音详情ID,关联 program_dub_detail 表主键 |
| partNumber | 视频片段编号,一个视频可切分为多个片段 |
| TTS | Text-to-Speech,文本转语音服务(使用ElevenLabs) |
| 背景音(International Audio) | 从原视频分离出的非人声音轨(音乐、环境音等) |
| 纯人声(Pure Voice) | TTS生成的配音人声音轨 |
| 音频合并 | 将多段TTS音频按时间偏移量拼接为完整音轨 |
| 音视频融合 | 将音轨与视频流合并为最终视频文件 |
| adelay | FFmpeg音频延迟滤镜,用于时间轴对齐 |
| amix | FFmpeg音频混合滤镜,用于多音轨混合 |
3.2 关键实体
| 实体 | 说明 | 依据 |
|---|
ProgramDubDetail | 配音详情实体,记录配音任务状态 | ProgramDubDetail.java |
DubAudioInfo | 配音音频信息,记录每段TTS音频的时间和路径 | DubAudioInfo.java |
DubInfoParams | 配音信息参数,包含视频/背景音/人声路径 | DubInfoParams.java |
VideoAudioMergeRequest | 音视频融合请求DTO | VideoAudioMergeRequest.java |
VideoAudioMergeResponse | 音视频融合响应DTO | VideoAudioMergeResponse.java |
4. 系统上下文(C4: Context)
4.1 系统边界与外部依赖
这张图回答:音频处理模块与哪些外部系统交互?
flowchart TB
subgraph Users["用户/调用方"]
FE["前端应用"]
API["上游API服务"]
end
subgraph TransDubPlatform["TransDubPlatform 译配平台"]
AudioAPI["音频处理API<br/>/api/audio/process/"]
end
subgraph ExternalSystems["外部系统"]
OSS["阿里云OSS<br/>对象存储"]
MySQL["MySQL<br/>业务数据库"]
Redis["Redis<br/>缓存/进度"]
ElevenLabs["ElevenLabs<br/>TTS服务"]
end
subgraph Infrastructure["基础设施"]
FFmpeg["FFmpeg<br/>音视频处理引擎"]
end
FE -->|"HTTP POST"| AudioAPI
API -->|"HTTP POST"| AudioAPI
AudioAPI -->|"下载/上传文件"| OSS
AudioAPI -->|"读写配音数据"| MySQL
AudioAPI -->|"进度缓存"| Redis
AudioAPI -->|"调用TTS"| ElevenLabs
AudioAPI -->|"音视频处理命令"| FFmpeg
4.2 信任边界
| 边界 | 信任级别 | 说明 |
|---|
| 前端 → API | 需认证 | 通过Token认证(依据: application.yml token配置) |
| API → OSS | 内部信任 | 使用AK/SK认证 |
| API → MySQL | 内部信任 | 内网访问 |
| API → FFmpeg | 完全信任 | 本地进程调用 |
5. 容器与组件视图(C4: Container + Component)
5.1 服务/模块清单
| 模块 | 职责 | 运行方式 | 依据 |
|---|
transdubplatform-service | Web服务主模块,提供REST API | Spring Boot JAR | Dockerfile |
TransDubPlatform-system | 业务逻辑模块,包含音频处理核心服务 | 依赖库 | build.gradle |
TransDubPlatform-common | 公共工具模块 | 依赖库 | settings.gradle |
5.2 组件映射表
这张图回答:音频处理涉及哪些核心组件及其依赖关系?
flowchart TB
subgraph Controller["Controller层"]
AC["AudioController<br/>/api/audio/*"]
end
subgraph Service["Service层"]
PTS["ProgramTtsServiceImpl<br/>TTS配音服务"]
ASA["AudioSilenceAdder<br/>音频合并服务"]
VAMS["VideoAudioMergeService<br/>音视频融合服务"]
AS["AudioService<br/>音频信息服务"]
end
subgraph Infrastructure["基础设施层"]
OSS["OSSService<br/>对象存储服务"]
FFI["FfmpegInitializer<br/>FFmpeg初始化"]
end
subgraph Repository["数据访问层"]
DAIR["DubAudioInfoRepository"]
PDDR["ProgramDubDetailRepository"]
end
AC --> PTS
AC --> VAMS
PTS --> ASA
ASA --> VAMS
ASA --> AS
ASA --> OSS
VAMS --> OSS
VAMS --> FFI
ASA --> FFI
AS --> DAIR
ASA --> PDDR
5.3 目录到组件映射
| 目录路径 | 组件 | 职责 | 入口文件 |
|---|
transdubplatform-service/.../controller/business/ | Controller | REST API入口 | AudioController.java |
TransDubPlatform-system/.../service/impl/ | Service | 业务逻辑 | AudioSilenceAdder.java, VideoAudioMergeService.java |
TransDubPlatform-system/.../service/oss/ | OSS服务 | 文件存储 | OSSService.java |
TransDubPlatform-system/.../service/utils/ | 工具类 | FFmpeg封装 | FfmpegInitializer.java |
TransDubPlatform-system/.../domain/ | 实体/DTO | 数据模型 | DubAudioInfo.java |
TransDubPlatform-system/.../mapper/ | Repository | 数据访问 | DubAudioInfoRepository.java |
6. 核心数据流与控制流
6.1 链路1:音频处理主流程(/api/audio/process/ {dubDetailId})
这张图回答:音频处理接口的完整调用链路是什么?
6.1.1 时序图
sequenceDiagram
participant Client as 客户端
participant AC as AudioController
participant PTS as ProgramTtsService
participant ASA as AudioSilenceAdder
participant AS as AudioService
participant VAMS as VideoAudioMergeService
participant OSS as OSSService
participant FFmpeg as FFmpeg进程
participant DB as MySQL
Client->>AC: POST /api/audio/process/{dubDetailId}
AC->>PTS: mergeDubbingAudio(dubDetailId, partNumber)
Note over PTS: 更新状态为 MERGE_VOICE_PROGRESS
PTS->>ASA: processDubbingAudio(dubDetailId, params)
ASA->>DB: 查询 ProgramDubDetail
ASA->>AS: getAudioInfoList(dubDetailId)
AS->>DB: 查询 DubAudioInfo 列表
AS-->>ASA: 返回音频信息列表
Note over ASA: 转换为 DialogueLine 列表
ASA->>ASA: convertToDialogueLines()
Note over ASA: 归并树并行合并音频
loop 每批次音频
ASA->>FFmpeg: 执行 amix + adelay 合并
FFmpeg-->>ASA: 返回中间文件
end
ASA->>OSS: putObject(合并后音频)
OSS-->>ASA: 返回OSS路径
Note over ASA: 准备音视频融合
ASA->>VAMS: mergeVideoAudio(request)
VAMS->>OSS: checkBackgroundAudioExists()
alt 有背景音
par 并行下载
VAMS->>OSS: downloadToFile(视频)
VAMS->>OSS: downloadToFile(背景音)
VAMS->>OSS: downloadToFile(人声)
end
VAMS->>FFmpeg: amix混合背景音+人声
else 无背景音
par 并行下载
VAMS->>OSS: downloadToFile(视频)
VAMS->>OSS: downloadToFile(人声)
end
VAMS->>FFmpeg: 人声直接替换音轨
end
FFmpeg-->>VAMS: 输出MP4文件
VAMS->>OSS: putObject(融合视频)
VAMS-->>ASA: 返回融合结果
ASA->>DB: 更新 videoFilePath
ASA-->>PTS: 返回音频OSS路径
Note over PTS: 更新状态为 MERGE_VOICE_COMPLETED
PTS-->>AC: 完成
AC-->>Client: 返回成功
6.1.2 数据流图
这张图回答:数据在各存储系统间如何流转?
flowchart LR
subgraph Input["输入数据"]
TTS_FILES["TTS音频文件<br/>(OSS)"]
VIDEO["原视频文件<br/>(OSS)"]
BG_AUDIO["背景音文件<br/>(OSS)"]
end
subgraph Process["处理过程"]
LOCAL_TEMP["本地临时目录<br/>/opt/startimes/service/temp/"]
FFMPEG["FFmpeg处理"]
end
subgraph Output["输出数据"]
MERGED_AUDIO["合并音频<br/>(OSS)"]
FINAL_VIDEO["融合视频<br/>(OSS)"]
end
subgraph DB["数据库"]
DUB_AUDIO_INFO["dub_audio_info<br/>(读取)"]
PROGRAM_DUB_DETAIL["program_dub_detail<br/>(读写)"]
end
DUB_AUDIO_INFO -->|"音频路径列表"| TTS_FILES
TTS_FILES -->|"下载"| LOCAL_TEMP
VIDEO -->|"下载"| LOCAL_TEMP
BG_AUDIO -->|"下载(可选)"| LOCAL_TEMP
LOCAL_TEMP -->|"音频合并"| FFMPEG
FFMPEG -->|"输出"| LOCAL_TEMP
LOCAL_TEMP -->|"上传"| MERGED_AUDIO
LOCAL_TEMP -->|"上传"| FINAL_VIDEO
FINAL_VIDEO -->|"更新videoFilePath"| PROGRAM_DUB_DETAIL
6.1.3 关键数据结构
DubAudioInfo(音频片段信息)
// 依据: DubAudioInfo.java
{
"id": 12345,
"orderNumber": 0, // 序号,用于排序
"programDubDetailId": 100, // 关联配音详情ID
"startTime": "00:00:05,200", // 开始时间 HH:mm:ss,SSS
"endTime": "00:00:08,500", // 结束时间
"audioPath": "/opt/.../0.mp3", // 本地音频路径
"deleteFlag": 1 // 删除标识
}
VideoAudioMergeRequest(融合请求)
// 依据: VideoAudioMergeRequest.java
{
"videoPath": "/path/to/video.mp4", // OSS视频路径
"backgroundAudioPath": "/path/to/bg.mp3", // OSS背景音路径(可选)
"voiceAudioPath": "/path/to/voice.mp3", // OSS人声路径
"outputFileName": "output.mp4", // 输出文件名(可选)
"outputFilePath": "/output/path/" // 输出目录
}
6.2 链路2:音频归并合并流程
这张图回答:大量音频片段如何高效合并?
6.2.1 时序图
sequenceDiagram
participant ASA as AudioSilenceAdder
participant Pool as ExecutorService线程池
participant FFmpeg as FFmpeg进程
Note over ASA: 输入: N个音频片段
ASA->>ASA: 计算全局最小起始时间
loop 归并层级 (直到剩余1个)
ASA->>ASA: 分批 (每批10个)
par 并行处理每批
ASA->>Pool: 提交批次1合并任务
Pool->>FFmpeg: amix + adelay
FFmpeg-->>Pool: 中间文件1
ASA->>Pool: 提交批次2合并任务
Pool->>FFmpeg: amix + adelay
FFmpeg-->>Pool: 中间文件2
end
Pool-->>ASA: 收集所有中间文件
Note over ASA: 中间文件作为下一层输入
end
ASA->>FFmpeg: 最终层应用 volume=10
FFmpeg-->>ASA: 最终合并文件
6.2.2 归并树示意
输入: 25个音频片段
Level 0: [1-10] [11-20] [21-25] <- 3批并行
↓ ↓ ↓
Level 1: [A] [B] [C] <- 3个中间文件
↓ ↓ ↓
Level 2: [Final] <- 最终输出
6.3 链路3:音视频融合流程(有/无背景音)
这张图回答:两种融合模式的处理差异是什么?
flowchart TB
Start["开始融合"] --> CheckBG{"检查背景音<br/>ossService.exitObject()"}
CheckBG -->|"存在"| HasBG["有背景音模式"]
CheckBG -->|"不存在/为空"| NoBG["无背景音模式"]
subgraph HasBG_Flow["有背景音流程"]
HasBG --> Download3["并行下载3个文件<br/>视频+背景音+人声"]
Download3 --> FFmpeg_Mix["FFmpeg amix混合<br/>[1:a][2:a]amix=inputs=2"]
FFmpeg_Mix --> Map_Mix["映射: 0:v + [aout]"]
end
subgraph NoBG_Flow["无背景音流程"]
NoBG --> Download2["并行下载2个文件<br/>视频+人声"]
Download2 --> FFmpeg_Replace["FFmpeg直接映射<br/>-map 0:v -map 1:a"]
FFmpeg_Replace --> Map_Replace["映射: 0:v + 1:a"]
end
Map_Mix --> Encode["音频编码<br/>AAC 96k 22050Hz"]
Map_Replace --> Encode
Encode --> VideoCopy["视频流复制<br/>-c:v copy"]
VideoCopy --> Output["输出MP4<br/>+faststart"]
Output --> Upload["上传OSS"]
Upload --> End["完成"]
7. API与接口设计
7.1 对外API
7.1.1 POST /api/audio/process/ {dubDetailId}
| 属性 | 值 |
|---|
| 端点 | /api/audio/process/{dubDetailId} |
| 方法 | POST |
| 描述 | 处理配音音频:合并TTS音频片段 + 音视频融合 |
| 依据 | AudioController.java |
路径参数
| 参数 | 类型 | 必填 | 说明 |
|---|
| dubDetailId | Long | 是 | 配音详情ID |
请求体
{
"partNumber": 1 // 片段编号
}
| 字段 | 类型 | 必填 | 说明 | 依据 |
|---|
| partNumber | Integer | 是 | 视频片段编号 | FFmpegProcessAudioParams.java |
响应
| 状态码 | 说明 |
|---|
| 200 | 处理成功(异步处理,立即返回) |
| 500 | 处理失败 |
鉴权: 需要Token认证(依据: application.yml token配置)
幂等性: 非幂等,重复调用会重新处理
7.1.2 POST /api/audio/merge
| 属性 | 值 |
|---|
| 端点 | /api/audio/merge |
| 方法 | POST |
| 描述 | 视频音频融合:将背景音和人声融合到视频中 |
| 依据 | AudioController.java |
请求体
{
"videoPath": "/path/to/video.mp4",
"backgroundAudioPath": "/path/to/background.mp3",
"voiceAudioPath": "/path/to/voice.mp3",
"outputFileName": "output.mp4",
"outputFilePath": "/output/path/"
}
| 字段 | 类型 | 必填 | 说明 |
|---|
| videoPath | String | 是 | OSS视频文件路径 |
| backgroundAudioPath | String | 否 | OSS背景音文件路径(不存在则使用纯人声模式) |
| voiceAudioPath | String | 是 | OSS人声配音文件路径 |
| outputFileName | String | 否 | 输出文件名 |
| outputFilePath | String | 是 | 输出目录路径 |
响应体
{
"success": true,
"outputOssPath": "/output/path/output.mp4",
"outputSignedUrl": "https://oss.../output.mp4?sign=...",
"costTimeMs": 12500,
"errorMessage": null
}
| 字段 | 类型 | 说明 |
|---|
| success | boolean | 是否成功 |
| outputOssPath | String | 输出文件OSS路径 |
| outputSignedUrl | String | 带签名的下载URL(1小时有效) |
| costTimeMs | long | 处理耗时(毫秒) |
| errorMessage | String | 错误信息(失败时) |
错误码
| 错误信息 | 说明 |
|---|
| 请求参数不能为空 | request为null |
| 视频文件路径不能为空 | videoPath为空 |
| 配音人声文件路径不能为空 | voiceAudioPath为空 |
| 融合处理失败: xxx | FFmpeg执行失败 |
7.2 内部接口
7.2.1 AudioSilenceAdder.processDubbingAudio()
// 依据: AudioSilenceAdder.java
public String processDubbingAudio(Long dubDetailId, FFmpegProcessAudioParams params)
| 参数 | 类型 | 说明 |
|---|
| dubDetailId | Long | 配音详情ID |
| params | FFmpegProcessAudioParams | 包含partNumber |
| 返回值 | String | 合并后音频的OSS路径,失败返回null |
7.2.2 VideoAudioMergeService.mergeVideoAudio()
// 依据: VideoAudioMergeService.java
public VideoAudioMergeResponse mergeVideoAudio(VideoAudioMergeRequest request)
| 参数 | 类型 | 说明 |
|---|
| request | VideoAudioMergeRequest | 融合请求参数 |
| 返回值 | VideoAudioMergeResponse | 融合结果 |
7.2.3 OSSService.exitObject()
// 依据: OSSService.java
public Boolean exitObject(String objectName)
| 参数 | 类型 | 说明 |
|---|
| objectName | String | OSS对象路径 |
| 返回值 | Boolean | 文件是否存在 |
8. 数据存储设计
8.1 数据库表
8.1.1 dub_audio_info(配音音频信息表)
依据: V1.2.2_20250616__DB_init.sql
CREATE TABLE `dub_audio_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`order_number` int(11) NOT NULL COMMENT '序号',
`fk_program_dub_detail_id` bigint(20) NOT NULL COMMENT '配音详细id',
`start_time` varchar(255) NOT NULL COMMENT '开始时间',
`end_time` varchar(255) NOT NULL COMMENT '结束时间',
`audio_path` varchar(1024) NOT NULL COMMENT '音频文件路径',
`delete_flag` tinyint(4) NOT NULL DEFAULT '1' COMMENT '删除标识',
`create_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='配音输出音频信息表';
| 字段 | 类型 | 索引 | 说明 |
|---|
| id | bigint | PK | 主键 |
| order_number | int | - | 序号,用于排序 |
| fk_program_dub_detail_id | bigint | 建议添加索引 | 关联配音详情 |
| start_time | varchar(255) | - | 格式: HH:mm:ss,SSS |
| end_time | varchar(255) | - | 格式: HH:mm:ss,SSS |
| audio_path | varchar(1024) | - | 本地文件路径 |
| delete_flag | tinyint | - | 0已删除/1未删除 |
8.1.2 program_dub_detail(配音详情表)
依据: V1.1.0_20250328__DB_init.sql
CREATE TABLE `program_dub_detail` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`fk_program_process_detail_id` bigint(20) NOT NULL COMMENT '详单id',
`part_number` int(11) NOT NULL COMMENT '片段编号',
`delete_flag` tinyint(4) NOT NULL DEFAULT 1,
`start_time` timestamp NULL,
`completed_time` timestamp NULL,
`status` tinyint(4) NOT NULL COMMENT '状态',
`audio_file_path` varchar(1024) COMMENT '音频文件路径',
`video_file_path` varchar(1024) COMMENT '视频文件路径',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='配音详表';
| 字段 | 类型 | 说明 |
|---|
| audio_file_path | varchar(1024) | 合并后音频OSS路径 |
| video_file_path | varchar(1024) | 融合后视频OSS路径 |
8.2 文件存储(OSS)
| 文件类型 | 路径模式 | 说明 |
|---|
| TTS音频片段 | /opt/startimes/service/{dubDetailId}/{lang}/{episode}/DUB/{partNumber}/{lineNo}.mp3 | 本地临时存储 |
| 合并音频 | /{programContentId}/{lang}/{episode}/DUB/{partNumber}/complete_audio_with_silence.mp3 | OSS存储 |
| 融合视频 | /{path}/merged/{filename}_merged.mp4 | OSS存储 |
| 背景音 | 由上游提供 | OSS存储 |
| 原视频 | 由上游提供 | OSS存储 |
8.3 临时文件
| 目录 | 用途 | 清理策略 |
|---|
/opt/startimes/service/temp/merge/{jobId}/ | 音视频融合临时文件 | 任务完成后立即清理 |
/opt/startimes/service/temp/{dubDetailId}/{partNumber}/ | 音频合并临时文件 | 任务完成后立即清理 |
/opt/startimes/service/temp/merge/output/{jobId}/ | 融合输出临时文件 | 上传OSS后清理 |
8.4 缓存(Redis)
| Key模式 | 用途 | TTL | 依据 |
|---|
program:tts:progress:{dubDetailId} | TTS进度缓存 | 1天 | ProgramTtsServiceImpl.java |
9. 关键算法与业务规则
9.1 音频时间轴对齐算法
依据: AudioSilenceAdder.java
输入: List<DubAudioInfo> 包含 startTime, endTime, audioPath
输出: 按时间轴对齐的完整音频
算法:
1. 解析时间字符串为毫秒: "HH:mm:ss,SSS" -> milliseconds
2. 计算全局最小起始时间: globalMinStartTime = min(all startTime)
3. 对每个音频片段计算相对延迟: relativeDelay = startTime - globalMinStartTime
4. 使用FFmpeg adelay滤镜添加延迟: adelay={relativeDelay}:all=1
5. 使用amix滤镜混合所有音频流
9.2 归并树合并算法
依据: AudioSilenceAdder.mergeAudioClipsWithTree()
输入: N个音频片段
参数: MERGE_BATCH_SIZE = 10
算法:
1. 如果N=1,直接处理单个片段返回
2. 将N个片段分成 ceil(N/10) 个批次
3. 并行处理每个批次:
- 计算批次内最小起始时间
- 使用amix合并批次内所有音频
- 输出中间文件,记录绝对起始时间
4. 收集所有中间文件作为下一层输入
5. 重复步骤2-4直到只剩1个文件
6. 最终层应用volume=10增益
复杂度: O(N) 音频处理,O(log N) 层级
9.3 背景音存在性判断规则
依据: VideoAudioMergeService.checkBackgroundAudioExists()
规则:
1. 如果 backgroundAudioPath 为空或空白 -> 无背景音模式
2. 如果 ossService.exitObject(backgroundAudioPath) 返回false -> 无背景音模式
3. 如果检查过程发生异常 -> 无背景音模式(降级处理)
4. 其他情况 -> 有背景音模式
9.4 FFmpeg命令构建规则
有背景音模式:
ffmpeg -y -threads 0 \
-i {video} -i {background} -i {voice} \
-filter_complex "[1:a][2:a]amix=inputs=2:duration=longest:normalize=0[aout]" \
-map 0:v -map [aout] \
-c:v copy -c:a aac -b:a 96k -ar 22050 -ac 1 \
-shortest -movflags +faststart \
{output}
无背景音模式:
ffmpeg -y -threads 0 \
-i {video} -i {voice} \
-map 0:v -map 1:a \
-c:v copy -c:a aac -b:a 96k -ar 22050 -ac 1 \
-shortest -movflags +faststart \
{output}
10. 质量属性与横切关注点
10.1 性能与容量
| 指标 | 当前值/策略 | 依据 |
|---|
| FFmpeg超时 | 600秒 | FFMPEG_TIMEOUT_SECONDS |
| FFprobe超时 | 30秒 | FFPROBE_TIMEOUT_SECONDS |
| 下载线程池 | CPU核心数×2(最小4) | downloadExecutor |
| 音频合并线程池 | CPU核心数 | executorService |
| 归并批次大小 | 10个/批 | MERGE_BATCH_SIZE |
| 音频采样率 | 22050 Hz | SAMPLE_RATE |
| 音频比特率 | 32k (合并) / 96k (融合) | AUDIO_BITRATE |
瓶颈点:
- OSS文件下载(已通过并行下载优化)
- FFmpeg音频编码(已通过低质量参数优化)
- 大量音频片段合并(已通过归并树优化)
10.2 可靠性
| 机制 | 实现方式 | 依据 |
|---|
| 超时控制 | process.waitFor(timeout, TimeUnit.SECONDS) | VideoAudioMergeService.java |
| 进程强制终止 | process.destroyForcibly() | 超时后执行 |
| 输出文件验证 | 检查文件存在性和大小 | executeFfmpegMerge() |
| 异常降级 | 背景音检查失败时降级为纯人声模式 | checkBackgroundAudioExists() |
| 资源清理 | finally块中清理临时文件 | cleanupTempDirectory() |
10.3 可观测性
日志
| 日志级别 | 内容 | 依据 |
|---|
| INFO | 任务开始/结束、各阶段耗时、FFmpeg进度 | 各Service类 |
| WARN | 临时文件清理失败、背景音检查异常 | cleanupTempDirectory() |
| ERROR | FFmpeg执行失败、文件下载失败 | 异常处理块 |
| DEBUG | FFmpeg完整命令、FFprobe输出 | 调试信息 |
关键日志格式:
[{jobId}] 开始视频音频融合任务, 模式: {mode}, 请求参数: {request}
[{jobId}] FFmpeg命令: {command}
[{jobId}] FFmpeg进度: time={time} speed={speed}
[{jobId}] 视频音频融合任务完成,模式: {mode}, 总耗时: {cost}ms
指标(未知/需确认)
当前代码中未发现Prometheus指标埋点,但配置文件中有Prometheus端点配置:
# 依据: application.yml
management:
endpoints:
web:
exposure:
include: health,prometheus
10.4 安全
| 安全点 | 措施 | 依据 |
|---|
| API认证 | Token认证 | application.yml token配置 |
| OSS访问 | AK/SK认证,环境变量注入 | ${OSS_KEY}, ${OSS_SECRET} |
| 文件路径 | 使用UUID生成jobId,避免路径遍历 | UUID.randomUUID() |
| 临时文件 | 任务完成后立即清理 | cleanupTempDirectory() |
11. 部署与运维
11.1 部署架构
这张图回答:服务如何部署和依赖哪些基础设施?
flowchart TB
subgraph K8s["Kubernetes集群"]
subgraph Pod["transdubplatform-service Pod"]
Container["Java容器<br/>8u212-jdk-slim"]
FFmpeg["FFmpeg<br/>/opt/startimes/service/ffmpeg/"]
TempDir["临时目录<br/>/opt/startimes/service/temp/"]
end
end
subgraph External["外部服务"]
OSS["阿里云OSS<br/>ai-dubbing-platform-*"]
MySQL["MySQL<br/>业务数据库"]
Redis["Redis<br/>缓存服务"]
end
Container --> FFmpeg
Container --> TempDir
Container --> OSS
Container --> MySQL
Container --> Redis
11.2 Dockerfile分析
依据: transdubplatform-service/config/Dockerfile
FROM common-repos.startimes.me/startimes-ops/8u212-jdk-slim:latest
# FFmpeg从JAR包中提取
RUN jar xf transdubplatform-service.jar BOOT-INF/classes/static/ffmpeg ...
RUN chmod +x /opt/startimes/service/ffmpeg/ffmpeg
ENTRYPOINT java ${JAVA_OPTS} -jar /opt/startimes/service/transdubplatform-service.jar
关键点:
- 基础镜像: Java 8 (8u212)
- FFmpeg: 内嵌于JAR包,部署时提取
- 路径:
/opt/startimes/service/ffmpeg/ffmpeg
11.3 环境配置
| 环境变量 | 用途 | 依据 |
|---|
JAVA_OPTS | JVM参数 | Dockerfile |
REDIS_HOST | Redis地址 | application.yml |
OSS_KEY | OSS AccessKey | application.yml |
OSS_SECRET | OSS SecretKey | application.yml |
FTP_HOST/USER/PASS | FTP配置 | application.yml |
token_secret | Token密钥 | application.yml |
11.4 环境划分
| 环境 | 配置文件 | 依据 |
|---|
| local | config/resources/local/application.yml | 目录结构 |
| dev | config/resources/dev/application.yml | 目录结构 |
| test1 | config/resources/test1/application.yml | 目录结构 |
| production | config/resources/production/application.yml | 目录结构 |
12. 测试策略
12.1 测试层级
| 层级 | 覆盖范围 | 状态 |
|---|
| 单元测试 | Service方法 | 未知 - 未找到测试代码 |
| 集成测试 | API端到端 | 未知 |
| E2E测试 | 完整业务流程 | 未知 |
12.2 关键测试用例(建议)
| 用例ID | 场景 | 预期结果 |
|---|
| TC01 | 正常流程-有背景音 | 输出包含背景音+人声的视频 |
| TC02 | 正常流程-无背景音 | 输出仅人声的视频 |
| TC03 | 背景音路径为空 | 自动切换无背景音模式 |
| TC04 | 背景音文件不存在 | 自动切换无背景音模式 |
| TC05 | 视频文件不存在 | 返回错误信息 |
| TC06 | 人声文件不存在 | 返回错误信息 |
| TC07 | FFmpeg执行超时 | 进程被终止,返回超时错误 |
| TC08 | 大量音频片段(100+) | 归并合并成功 |
| TC09 | 单个音频片段 | 直接处理成功 |
12.3 Mock策略
| 依赖 | Mock方式 |
|---|
| OSSService | Mock下载/上传方法,使用本地文件 |
| FFmpeg | 使用真实FFmpeg或Mock进程输出 |
| MySQL | H2内存数据库或TestContainers |
13. 方案对比与取舍
13.1 音频合并方案
| 方案 | 优点 | 缺点 | 选择 |
|---|
| 方案A: 顺序合并 | 实现简单 | O(N²)复杂度,大量片段时极慢 | ❌ |
| 方案B: 归并树并行 | O(N)复杂度,并行处理 | 实现复杂,需管理中间文件 | ✅ 当前方案 |
| 方案C: 单次amix | 最简单 | FFmpeg对大量输入支持差 | ❌ |
选择理由: 归并树方案在处理大量音频片段时性能优势明显,虽然实现复杂但可维护。
13.2 音视频融合方案
| 方案 | 优点 | 缺点 | 选择 |
|---|
| 方案A: 视频重编码 | 兼容性好 | 极慢,CPU密集 | ❌ |
| 方案B: 视频流复制 | 极快,无质量损失 | 需要容器格式兼容 | ✅ 当前方案 |
选择理由: -c:v copy 避免视频重编码,处理速度提升10倍以上。
13.3 背景音处理方案
| 方案 | 优点 | 缺点 | 选择 |
|---|
| 方案A: 强制要求背景音 | 逻辑简单 | 不灵活,部分场景无背景音 | ❌ |
| 方案B: 运行时检测 | 灵活,自动适配 | 需额外OSS检查 | ✅ 当前方案 |
选择理由: 通过 ossService.exitObject() 检测背景音存在性,自动切换处理模式,向后兼容。
14. 风险清单与缓解措施
| 风险ID | 风险描述 | 影响 | 概率 | 缓解措施 |
|---|
| R01 | FFmpeg进程hang住 | 任务阻塞 | 中 | 600秒超时 + destroyForcibly |
| R02 | 临时文件未清理导致磁盘满 | 服务不可用 | 低 | finally块清理 + 定期巡检 |
| R03 | OSS下载失败 | 任务失败 | 低 | 异常捕获 + 错误日志 |
| R04 | 大量并发任务导致OOM | 服务崩溃 | 中 | 线程池限制 + JVM参数调优 |
| R05 | 音频时间轴不对齐 | 配音与画面不同步 | 低 | 毫秒级精度 + 上游已处理偏移 |
| R06 | 视频编码不兼容copy | 融合失败 | 低 | 错误日志 + 人工介入 |
15. 未决问题与需补充材料
15.1 需确认的问题
| 问题ID | 问题描述 | 需要的材料/信息 |
|---|
| Q01 | 是否有单元测试覆盖? | 测试代码目录 |
| Q02 | Prometheus指标是否已埋点? | 指标定义代码 |
| Q03 | 生产环境的JVM参数配置? | K8s deployment yaml |
| Q04 | 任务失败后的重试机制? | 重试逻辑代码 |
| Q05 | 并发任务数限制? | 限流配置 |
| Q06 | 视频格式兼容性范围? | 支持的编码格式列表 |
15.2 需补充的文件
| 文件类型 | 用途 |
|---|
| K8s Deployment YAML | 了解资源限制、副本数 |
| 完整的application.yml | 了解所有配置项 |
| 测试代码 | 了解测试覆盖情况 |
| CI/CD配置 | 了解发布流程 |
| 监控告警配置 | 了解可观测性 |
16. 附录
16.1 关键文件索引
| 模块 | 文件路径 | 说明 |
|---|
| Controller | transdubplatform-service/src/main/java/com/star/transdubplatform/web/controller/business/AudioController.java | API入口 |
| 音频合并 | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/service/impl/AudioSilenceAdder.java | 音频合并服务 |
| 音视频融合 | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/service/impl/VideoAudioMergeService.java | 融合服务 |
| TTS服务 | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/service/impl/ProgramTtsServiceImpl.java | TTS配音服务 |
| OSS服务 | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/service/oss/OSSService.java | 对象存储 |
| FFmpeg工具 | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/service/utils/FfmpegInitializer.java | FFmpeg初始化 |
| 音频服务 | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/service/impl/AudioService.java | 音频信息查询 |
| 请求DTO | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/domain/dto/VideoAudioMergeRequest.java | 融合请求 |
| 响应DTO | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/domain/dto/VideoAudioMergeResponse.java | 融合响应 |
| 音频实体 | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/domain/DubAudioInfo.java | 音频信息实体 |
| 配音实体 | TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/domain/ProgramDubDetail.java | 配音详情实体 |
| 数据库DDL | transdubplatform-service/src/main/resources/db/migration/V1.2.2_20250616__DB_init.sql | 表结构 |
| Dockerfile | transdubplatform-service/config/Dockerfile | 部署配置 |
| 生产配置 | transdubplatform-service/config/resources/production/application.yml | 生产环境配置 |
16.2 参考
本文档所有内容均基于以下仓库文件:
- 代码文件:如上表所列
- 配置文件:
application.yml, Dockerfile - 数据库脚本:
db/migration/*.sql
文档结束