我的博客
中文English日本語
  • 文章
  • 关于

音频处理与音视频融合接口设计文档

音频处理与音视频融合接口设计文档

文档信息

项目内容
标题/api/audio/process/ 音频处理与音视频融合接口设计文档
版本v1.0
作者-
日期2024-12-24
适用范围TransDubPlatform 译配平台 - 音频处理模块

目录

  • 1. 背景与现状
  • 2. 目标与非目标
  • 3. 术语表与关键实体
  • 4. 系统上下文
  • 5. 容器与组件视图
  • 6. 核心数据流与控制流
  • 7. API与接口设计
  • 8. 数据存储设计
  • 9. 关键算法与业务规则
  • 10. 质量属性与横切关注点
  • 11. 部署与运维
  • 12. 测试策略
  • 13. 方案对比与取舍
  • 14. 风险清单与缓解措施
  • 15. 未决问题与需补充材料
  • 16. 附录

1. 背景与现状

1.1 业务背景

TransDubPlatform(译配平台)是一个视频翻译配音平台,核心业务流程包括:

  1. 视频上传与音频提取
  2. 语音分离(人声/背景音分离)
  3. 语音识别(ASR)生成台本
  4. 台本翻译
  5. AI配音(TTS)与音频合并
  6. 音视频融合输出

本文档聚焦于 /api/audio/process/{dubDetailId} 接口,该接口负责:

  • 将多段TTS生成的配音音频按时间轴合并为完整音轨
  • 将合并后的人声配音与背景音、原视频进行融合
  • 输出最终的配音视频文件

依据: transdubplatform-service/.../AudioController.java

1.2 技术背景

技术栈说明依据
语言/框架Java 8 + Spring BootDockerfile, build.gradle
音视频处理FFmpeg (内嵌于JAR包)Dockerfile, FfmpegInitializer.java
对象存储阿里云OSSOSSService.java, application.yml
数据库MySQLapplication.yml (druid配置)
缓存Redisapplication.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视频片段编号,一个视频可切分为多个片段
TTSText-to-Speech,文本转语音服务(使用ElevenLabs)
背景音(International Audio)从原视频分离出的非人声音轨(音乐、环境音等)
纯人声(Pure Voice)TTS生成的配音人声音轨
音频合并将多段TTS音频按时间偏移量拼接为完整音轨
音视频融合将音轨与视频流合并为最终视频文件
adelayFFmpeg音频延迟滤镜,用于时间轴对齐
amixFFmpeg音频混合滤镜,用于多音轨混合

3.2 关键实体

实体说明依据
ProgramDubDetail配音详情实体,记录配音任务状态ProgramDubDetail.java
DubAudioInfo配音音频信息,记录每段TTS音频的时间和路径DubAudioInfo.java
DubInfoParams配音信息参数,包含视频/背景音/人声路径DubInfoParams.java
VideoAudioMergeRequest音视频融合请求DTOVideoAudioMergeRequest.java
VideoAudioMergeResponse音视频融合响应DTOVideoAudioMergeResponse.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-serviceWeb服务主模块,提供REST APISpring Boot JARDockerfile
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/ControllerREST 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

路径参数

参数类型必填说明
dubDetailIdLong是配音详情ID

请求体

{
    "partNumber": 1    // 片段编号
}
字段类型必填说明依据
partNumberInteger是视频片段编号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/"
}
字段类型必填说明
videoPathString是OSS视频文件路径
backgroundAudioPathString否OSS背景音文件路径(不存在则使用纯人声模式)
voiceAudioPathString是OSS人声配音文件路径
outputFileNameString否输出文件名
outputFilePathString是输出目录路径

响应体

{
    "success": true,
    "outputOssPath": "/output/path/output.mp4",
    "outputSignedUrl": "https://oss.../output.mp4?sign=...",
    "costTimeMs": 12500,
    "errorMessage": null
}
字段类型说明
successboolean是否成功
outputOssPathString输出文件OSS路径
outputSignedUrlString带签名的下载URL(1小时有效)
costTimeMslong处理耗时(毫秒)
errorMessageString错误信息(失败时)

错误码

错误信息说明
请求参数不能为空request为null
视频文件路径不能为空videoPath为空
配音人声文件路径不能为空voiceAudioPath为空
融合处理失败: xxxFFmpeg执行失败

7.2 内部接口

7.2.1 AudioSilenceAdder.processDubbingAudio()

// 依据: AudioSilenceAdder.java
public String processDubbingAudio(Long dubDetailId, FFmpegProcessAudioParams params)
参数类型说明
dubDetailIdLong配音详情ID
paramsFFmpegProcessAudioParams包含partNumber
返回值String合并后音频的OSS路径,失败返回null

7.2.2 VideoAudioMergeService.mergeVideoAudio()

// 依据: VideoAudioMergeService.java
public VideoAudioMergeResponse mergeVideoAudio(VideoAudioMergeRequest request)
参数类型说明
requestVideoAudioMergeRequest融合请求参数
返回值VideoAudioMergeResponse融合结果

7.2.3 OSSService.exitObject()

// 依据: OSSService.java
public Boolean exitObject(String objectName)
参数类型说明
objectNameStringOSS对象路径
返回值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='配音输出音频信息表';
字段类型索引说明
idbigintPK主键
order_numberint-序号,用于排序
fk_program_dub_detail_idbigint建议添加索引关联配音详情
start_timevarchar(255)-格式: HH:mm:ss,SSS
end_timevarchar(255)-格式: HH:mm:ss,SSS
audio_pathvarchar(1024)-本地文件路径
delete_flagtinyint-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_pathvarchar(1024)合并后音频OSS路径
video_file_pathvarchar(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.mp3OSS存储
融合视频/{path}/merged/{filename}_merged.mp4OSS存储
背景音由上游提供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 HzSAMPLE_RATE
音频比特率32k (合并) / 96k (融合)AUDIO_BITRATE

瓶颈点:

  1. OSS文件下载(已通过并行下载优化)
  2. FFmpeg音频编码(已通过低质量参数优化)
  3. 大量音频片段合并(已通过归并树优化)

10.2 可靠性

机制实现方式依据
超时控制process.waitFor(timeout, TimeUnit.SECONDS)VideoAudioMergeService.java
进程强制终止process.destroyForcibly()超时后执行
输出文件验证检查文件存在性和大小executeFfmpegMerge()
异常降级背景音检查失败时降级为纯人声模式checkBackgroundAudioExists()
资源清理finally块中清理临时文件cleanupTempDirectory()

10.3 可观测性

日志

日志级别内容依据
INFO任务开始/结束、各阶段耗时、FFmpeg进度各Service类
WARN临时文件清理失败、背景音检查异常cleanupTempDirectory()
ERRORFFmpeg执行失败、文件下载失败异常处理块
DEBUGFFmpeg完整命令、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_OPTSJVM参数Dockerfile
REDIS_HOSTRedis地址application.yml
OSS_KEYOSS AccessKeyapplication.yml
OSS_SECRETOSS SecretKeyapplication.yml
FTP_HOST/USER/PASSFTP配置application.yml
token_secretToken密钥application.yml

11.4 环境划分

环境配置文件依据
localconfig/resources/local/application.yml目录结构
devconfig/resources/dev/application.yml目录结构
test1config/resources/test1/application.yml目录结构
productionconfig/resources/production/application.yml目录结构

12. 测试策略

12.1 测试层级

层级覆盖范围状态
单元测试Service方法未知 - 未找到测试代码
集成测试API端到端未知
E2E测试完整业务流程未知

12.2 关键测试用例(建议)

用例ID场景预期结果
TC01正常流程-有背景音输出包含背景音+人声的视频
TC02正常流程-无背景音输出仅人声的视频
TC03背景音路径为空自动切换无背景音模式
TC04背景音文件不存在自动切换无背景音模式
TC05视频文件不存在返回错误信息
TC06人声文件不存在返回错误信息
TC07FFmpeg执行超时进程被终止,返回超时错误
TC08大量音频片段(100+)归并合并成功
TC09单个音频片段直接处理成功

12.3 Mock策略

依赖Mock方式
OSSServiceMock下载/上传方法,使用本地文件
FFmpeg使用真实FFmpeg或Mock进程输出
MySQLH2内存数据库或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风险描述影响概率缓解措施
R01FFmpeg进程hang住任务阻塞中600秒超时 + destroyForcibly
R02临时文件未清理导致磁盘满服务不可用低finally块清理 + 定期巡检
R03OSS下载失败任务失败低异常捕获 + 错误日志
R04大量并发任务导致OOM服务崩溃中线程池限制 + JVM参数调优
R05音频时间轴不对齐配音与画面不同步低毫秒级精度 + 上游已处理偏移
R06视频编码不兼容copy融合失败低错误日志 + 人工介入

15. 未决问题与需补充材料

15.1 需确认的问题

问题ID问题描述需要的材料/信息
Q01是否有单元测试覆盖?测试代码目录
Q02Prometheus指标是否已埋点?指标定义代码
Q03生产环境的JVM参数配置?K8s deployment yaml
Q04任务失败后的重试机制?重试逻辑代码
Q05并发任务数限制?限流配置
Q06视频格式兼容性范围?支持的编码格式列表

15.2 需补充的文件

文件类型用途
K8s Deployment YAML了解资源限制、副本数
完整的application.yml了解所有配置项
测试代码了解测试覆盖情况
CI/CD配置了解发布流程
监控告警配置了解可观测性

16. 附录

16.1 关键文件索引

模块文件路径说明
Controllertransdubplatform-service/src/main/java/com/star/transdubplatform/web/controller/business/AudioController.javaAPI入口
音频合并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.javaTTS配音服务
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.javaFFmpeg初始化
音频服务TransDubPlatform-system/src/main/java/com/star/transdubplatform/system/service/impl/AudioService.java音频信息查询
请求DTOTransDubPlatform-system/src/main/java/com/star/transdubplatform/system/domain/dto/VideoAudioMergeRequest.java融合请求
响应DTOTransDubPlatform-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配音详情实体
数据库DDLtransdubplatform-service/src/main/resources/db/migration/V1.2.2_20250616__DB_init.sql表结构
Dockerfiletransdubplatform-service/config/Dockerfile部署配置
生产配置transdubplatform-service/config/resources/production/application.yml生产环境配置

16.2 参考

本文档所有内容均基于以下仓库文件:

  • 代码文件:如上表所列
  • 配置文件:application.yml, Dockerfile
  • 数据库脚本:db/migration/*.sql

文档结束

你好,世界

这是第一篇文章(中文)。

© 我的博客 2025