Test
最近在项目中遇到了需要实时合成多路视频流的需求,尝试了几种方案后,发现 FFmpeg 的滤镜系统真是神器!记录一下踩坑经验,希望对你有帮助~

一、为什么需要画中画方案?
在实际项目中经常遇到这些典型问题:
- 同步问题 :多路视频流时间戳不同步导致音画不同步
- 资源消耗 :软件渲染时 CPU 直接飙到 90%+
- 内存泄漏 :频繁创建 / 释放 AVFrame 导致内存碎片
- 兼容性问题 :不同分辨率的子流合成时画面变形
二、技术选型对比
我们测试了三种常见方案(测试环境:4 路 1080P@30fps 流合成)
| 方案 | 平均延迟 | CPU 占用 | GPU 显存占用 |
|—————-|———|——–|————|
| FFmpeg 滤镜链 | 120ms | 65% | 0 |
| NVENC 硬件加速 | 80ms | 15% | 2.1GB |
| OpenCV 混合 | 210ms | 95% | 1.5GB |
结论 :没有 GPU 的机器用 FFmpeg 滤镜链性价比最高,有 N 卡时 NVENC 方案更优。
三、核心代码实现
关键点在于 overlay 滤镜的正确使用,这是 Python 示例的核心片段:
# 创建滤镜图(线程安全等级:部分 API 需加锁)filter_graph = av.filter.Graph()
# 主视频流输入
main_input = filter_graph.add_buffer(template=main_stream)
# 子视频流输入(注意 PTS 同步)sub_input = filter_graph.add_buffer(template=sub_stream)
# 动态调整位置的 overlay 滤镜
overlay = filter_graph.add(
'overlay',
x='if(gte(t,5), 100, main_w-overlay_w-10)', # 第 5 秒后移动到左侧
y='if(gte(t,5), 50, main_h-overlay_h-10)', # 动态 Y 坐标
alpha=0.9 # 透明度设置
)
# 连接滤镜链(数据流向)filter_graph.connect(main_input, overlay, 0) # 主输入→overlay 主端口
filter_graph.connect(sub_input, overlay, 1) # 子输入→overlay 次端口

四、性能优化技巧
通过实测发现的几个有效方法:
- 线程池配置
ffmpeg -threads 8 -i input1 -i input2 -filter_complex "overlay" output - 8 线程时比单线程快 3.2 倍
-
超过 CPU 核心数反而会变慢
-
内存池化方案
// 初始化时创建内存池 AVBufferPool* pool = av_buffer_pool_init(sizeof(AVFrame), NULL); // 使用时获取 AVFrame* frame = av_frame_alloc(); frame->buf[0] = av_buffer_pool_get(pool); - 减少 35% 的内存分配耗时
五、避坑指南
这些坑我们踩过,你千万别踩:
- PTS 校准 :
- 错误做法:直接使用系统时间戳
-
正确做法:以首帧为基准做相对时间计算
-
分辨率适配 :
scale=filter_graph.add( 'scale', width='min(iw,main_w/3)', # 不超过主画面 1 / 3 宽度 height='-1', # 保持宽高比 force_original_aspect_ratio='decrease' ) -
流中断处理 :
while (1) {ret = av_read_frame(fmt_ctx, &pkt); if (ret == AVERROR(EAGAIN)) {av_usleep(10000); // 10ms 重试间隔 continue; } }
六、延伸思考
现在我们已经能在服务端实现高效合流,下一步可以尝试:
– 用 WebAssembly 编译 FFmpeg 在浏览器端运行
– 结合 WebRTC 实现实时传输
– 测试 H265 编码下的性能差异
测试环境构建 :
docker run -it \
-v $(pwd):/workspace \
jrottenberg/ffmpeg:4.4-ubuntu \
ffmpeg -i input1 -i input2 -filter_complex "overlay" output.mp4
希望这篇笔记能帮你少走弯路,有什么问题欢迎交流讨论~
正文完
评论(没有评论)