应避免使用 ffmpeg-go 或 goav 等 cgo 绑定库做流式解包,因其依赖 libavcodec 状态机,对无完整文件头、不规则 eof 或传输中断帧的恢复能力弱且内存不可控;稳定方案是通过 exec.command 调用 ffmpeg 进程,go 仅负责喂数据和解析 stdout 的 mkv 流,配合 stderr 提取 pts,并注意 windows 下 stdin 卡死问题。

为什么不用 ffmpeg-go 或 goav 做流式解包
直接用 github.com/u2takey/ffmpeg-go 或 github.com/3d0c/gocv 这类 cgo 绑定库封装流式解包,容易在生产环境卡死或 panic。它们依赖 libavcodec 的状态机管理,而流式数据(如 RTMP 推流、HTTP chunked 视频流)往往不带完整文件头、有不规则 EOF、或含传输中断帧——cgo 层对这些“非标准输入”的错误恢复能力极弱,且无法控制内存生命周期。真正稳定的做法是让 FFmpeg 自己做解封装,Go 只管喂数据、读输出。
用 exec.Command + stdin/stdout 实现流式解包
核心思路:让 FFmpeg 从 -i - 读取原始流,用 -f null -vstats 或 -c:v copy -c:a copy -f matroska - 输出标准化容器帧,Go 侧只处理字节流边界与 PTS 提取。关键点:
-
cmd := exec.CommandContext(ctx, "ffmpeg", "-i", "-", "-c:v", "copy", "-c:a", "copy", "-f", "matroska", "-")—— 不转码,只解封装+重封装为 MKV(自带时间戳和流索引) - 必须显式调用
stdin.Close(),且要在io.Copy完成后、cmd.Run()之前执行;否则 FFmpeg 卡在等待 EOF - FFmpeg 的 stderr 会持续输出解析日志(如
frame= 1234 fps=24.0 q=-1.0 Lsize=N/A time=00:00:51.23 bitrate=N/A speed=1.01x),需另起 goroutine 实时扫描匹配time=提取 PTS - stdout 是二进制 MKV 流,不能直接按行读;要用
io.ReadFull配合 EBML 头解析(或更简单:用github.com/viktorstanchev/go-mkv解析 Cluster)
如何识别并分离音视频帧(不依赖完整文件头)
流式解包最难的是“不知道下一帧是视频还是音频”,尤其当源流混有广告插入点或非连续 PTS。FFmpeg 默认输出不保证音视频帧严格交错,所以 Go 模块必须自己做帧路由:
- 用
-vbsf h264_mp4toannexb和-absf aac_adtstoasc强制统一裸流格式,避免 decoder 端因 Annex-B/AVCC 切换失败 - 解析 MKV 的
SimpleBlock或Block元素:TrackNumber字段决定所属流(1=video, 2=audio),Timecode是相对 Cluster 的毫秒偏移 - 别信
ffprobe -v quiet -show_entries format=duration—— 流式输入下它返回 N/A;真实 PTS 必须从每个 Block 解出再累加 - 若源流无时间戳(如某些 RTMP 直推),FFmpeg 会伪造单调递增 PTS,此时需结合
-use_wallclock_as_timestamps 1参数对齐系统时间
Windows 下 stdin 写入失败的静默卡死问题
不是权限问题,也不是路径问题,而是 Windows 控制台子进程默认继承父进程的 stdin 句柄,但 Go 的 cmd.StdinPipe() 创建的是匿名管道——当杀毒软件(如 Windows Defender)启用“进程行为监控”时,会拦截该句柄传递,导致 io.Copy 一直阻塞且无 error。解决方式只有两个:
- 在
cmd.SysProcAttr中设置&syscall.SysProcAttr{HideWindow: true, CreationFlags: syscall.CREATE_NO_WINDOW} - 改用命名管道(Named Pipe)替代匿名 pipe:先
CreateNamedPipe,再把 pipe path 传给 FFmpeg 的-i \.pipemy_stream,Go 侧用syscall.Open写入
后者更可靠,但意味着模块必须区分 Windows/Linux 构建标签;前者能覆盖 90% 场景,但需提醒用户关闭实时防护临时放行。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











