ffmpeg卡住主因是stderr未读取致管道阻塞;提取元数据应优先用ffprobe而非ffmpeg -i;解析JSON需处理字段缺失与类型漂移;Windows下需确保架构匹配且路径正确。

为什么 os/exec.Command 调用 ffmpeg 常卡住或没输出
根本原因是 ffmpeg 默认把进度信息(如帧数、码率、时间戳)写到 stderr,而很多 Go 代码只等 stdout 或直接忽略错误流,导致进程看似“卡死”——其实它在持续往 stderr 打印。更糟的是,如果没显式读取 stderr,管道缓冲区满后 ffmpeg 就会阻塞。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 必须同时启动
stdout和stderr的读取 goroutine,哪怕你只关心其中一部分 - 用
cmd.StdoutPipe()和cmd.StderrPipe()分别获取,别用cmd.CombinedOutput()——它会吞掉关键的进度流,且无法实时解析 - 对
stderr建议用bufio.Scanner行读取,避免因某行超长导致阻塞 - 加上
-v quiet -print_format json这类静默+结构化输出参数,比自己 parse 文本行更稳
提取时长/分辨率/编码格式,该用 ffprobe 还是 ffmpeg -i
ffprobe 是专为元数据设计的工具,输出稳定、字段明确、无副作用;ffmpeg -i 只是把输入信息当日志打出来,格式随版本浮动,还可能触发解码(比如遇到损坏帧就卡住)。除非你顺带想验证文件可播性,否则别用 ffmpeg -i 提取基本信息。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 优先调用
ffprobe -v quiet -show_entries format=duration:stream=width,height,codec_name -of default=nw=1 - 若需 JSON 输出便于 Go 解析,加
-of json,但注意:Go 的json.Unmarshal对ffprobe的数组字段(如多路 stream)默认映射为[]interface{},需提前定义 struct - 别漏掉
-v quiet——否则ffprobe会在开头打印一堆版本和编译信息,污染结构化输出 - 对 MP4/MKV 等容器,
format.duration可能为空(依赖 moov 位置),此时得 fallback 到stream_tags.DURATION或靠ffprobe -show_entries packet=duration_ts算总时长
Go 中解析 ffprobe JSON 输出容易崩在哪几个点
常见崩法不是语法错,而是字段缺失或类型漂移:ffprobe 对不同格式返回的字段不一致(比如 GIF 没 duration,WebP 可能没有 codec_name),且同一字段在不同版本里可能从 string 变成 number(如 bit_rate)。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 定义 struct 时所有字段用指针或
interface{},例如Duration *float64,避免 Unmarshal 失败 - 用
json.RawMessage先接整个 stream 数组,再按需解析每项,防止某路流字段异常拖垮全部 - 对数值型字段(如
width,height),先用json.Number解,再转int,避免 float64 精度误差 - 永远检查
err != nil后再访问字段,别信 “这个视频肯定有宽高”
Windows 下找不到 ffprobe.exe 或提示 “不是有效的 Win32 应用程序”
前者多半是 PATH 没配或路径含空格没引号;后者八成是你下了 32 位 ffprobe 却在 64 位 Go 程序里调用(或反过来),Windows 会静默拒绝加载。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 用绝对路径调用,例如
C:\tools\ffprobe.exe,并在 Go 里用filepath.Join拼接,避免手动拼错斜杠 - 检查 Go 构建目标:运行
go env GOARCH,确保下载的 FFmpeg 包匹配(amd64 / arm64) - 别用第三方打包版(如某些“绿色版”),它们常删了
ffprobe.exe只留ffmpeg.exe;去官网 ffmpeg.org/download 下 full build - 调用前先
os.Stat检查文件是否存在且可执行,比等 runtime 报错更早发现问题
真正麻烦的从来不是调用命令本身,而是 ffprobe 返回的字段像天气一样 unpredictable,还有 Windows 上二进制 ABI 的隐形对齐问题——这两块不提前压住,后面加再多重试逻辑都没用。










