Go调用FFmpeg切片HLS卡住的根本原因是未实时读取stderr/stdout导致管道阻塞,需用goroutine持续读取;清理.ts文件须基于内存队列按生成顺序删除,并原子更新m3u8;Safari播放失败主因是MIME类型错误或时间戳偏差;低延迟HLS需设-g、-bf 0、-hls_time等参数。

Go 启动 FFmpeg 进程切片 HLS 时,为什么 ffmpeg 命令总卡住或输出不全?
根本原因是 Go 的 os/exec.Cmd 默认不会自动处理 FFmpeg 的缓冲输出和实时 stderr 日志流。FFmpeg 在生成 HLS 时会持续写入 stderr(如分片进度、时间戳),若不显式读取,进程可能因管道阻塞而挂起,尤其在 Windows 或高负载环境下更明显。
- 必须同时启动 goroutine 读取
cmd.Stderr和cmd.Stdout,哪怕只是丢弃(io.Copy(ioutil.Discard, ...)) - 避免用
cmd.Output()或cmd.CombinedOutput()——它们会等待命令结束,但 HLS 是长时运行任务 - 设置
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}(Linux/macOS)防止子进程被父进程信号意外终止 - Windows 下需额外注意路径分隔符和 shell 环境,建议用
cmd := exec.Command("ffmpeg", "-i", inputPath, ...)显式传参,而非依赖shell -c
用 Go 管理 HLS 切片生命周期:如何安全清理过期 .ts 文件而不中断播放?
HLS 客户端靠 playlist.m3u8 的 #EXT-X-MEDIA-SEQUENCE 和文件存在性做连续播放判断。删错一个 ts 文件,可能导致客户端解码失败或跳播。不能简单按修改时间删除。
- 维护一个内存队列(如
list.List或环形 buffer),记录最近 N 个切片的filename和start_time - 每次生成新切片后,检查队列长度;超出保留数(如 10 个)时,从队头取出并
os.Remove()对应.ts文件和其索引项 -
playlist.m3u8必须原子更新:先写临时文件(如playlist.m3u8.tmp),再os.Rename()覆盖原文件,避免客户端读到半截内容 - 切勿在
http.ServeFile直接暴露切片目录——应通过自定义http.HandlerFunc检查请求的.ts是否仍在有效队列中,否则返回 404
Go HTTP 服务提供 HLS 时,为什么 Safari 播放失败而 Chrome 正常?
核心差异在于 Safari(尤其是 iOS/macOS)对 HLS 的 MIME 类型和响应头极其敏感。它要求 .m3u8 必须返回 application/vnd.apple.mpegurl,且 .ts 必须是 video/MP2T;缺一不可,且大小写敏感。
- 注册自定义
http.ServeMux处理器,对*.m3u8路径手动设置w.Header().Set("Content-Type", "application/vnd.apple.mpegurl") - 对
*.ts路径,设为"video/MP2T"(注意是MP2T,不是mp2t或mpegts) - 禁用 gzip 压缩 HLS 文件:Safari 不接受压缩后的 m3u8/ts,可在 handler 中加
w.Header().Set("Content-Encoding", "")并确保没全局启用压缩中间件 - 务必确认服务器时间准确——Safari 会校验 m3u8 中的
#EXT-X-PROGRAM-DATE-TIME时间戳与本地时间偏差,超 5 分钟可能静音
FFmpeg 参数组合影响 HLS 实时性:哪些开关必须开,哪些必须关?
直播 HLS 的关键指标是端到端延迟(通常目标 ≤ 10s)。默认 FFmpeg 参数偏重质量而非低延迟,需针对性调整。
立即学习“go语言免费学习笔记(深入)”;
- 强制关键帧间隔:
-g 60(假设 30fps,则每 2 秒一个 I 帧),配合-sc_threshold 0关闭场景切换检测,保证 GOP 稳定 - 关闭 B 帧:
-bf 0,避免解码依赖顺序混乱,降低播放器卡顿风险 - 使用
-hls_time 4(而非默认 2)平衡分片数与延迟;-hls_list_size 10控制 m3u8 中最多保留 10 个条目 - 必须加
-hls_flags +independent_segments+discont_start,否则多路推流重启时可能出现序列号断层 - 慎用
-preset:用-preset ultrafast,但避免-tune zerolatency(它已隐含在 ultrafast 中,重复设置可能触发 FFmpeg 内部冲突)
curl -I 看 header 和 tail -f 看 ffmpeg stderr 的那几秒里。










