摄像头现在能直接开播了——MiBeeNVR v0.8.1 直播推流上线
一个跑在树莓派上的开源 NVR,凭什么能跟 OBS 抢活干?这篇讲讲我们怎么把"监控摄像头 → 直播间"这条路从能用走到好用,中间踩了多少协议层的坑。

上图:手机 App 上看到的直播画面,推流由 MiBeeNVR 原生 Go relay 完成,无 FFmpeg 依赖。
先说结论
MiBeeNVR v0.8.1 支持将任意接入的摄像头画面,通过 RTMP 协议直接推流到直播平台。 纯 Go 实现,不需要额外安装 FFmpeg,不依赖外部进程,配置好推流地址就能开播。
⚠️ 目前作者仅在斗鱼平台完成实测,其它平台理论上兼容(同为 FMS 标准),但尚未验证。遇到问题请到 GitHub Issues 反馈。
树莓派 3B 以上即可运行,支持 H.264/H.265 摄像头。
MiBeeNVR 是什么
MiBeeNVR 是一个开源的网络视频录像机(NVR)项目,用 Go 语言编写,专为嵌入式设备(树莓派、香蕉派等)优化。在 v0.8.1 之前,它已经具备:
-
多路摄像头管理与录像(RTSP/ONVIF/小米双摄)
-
H.264/H.265 硬件转码
-
延时摄影录制与合成
-
LL-HLS / WebRTC 低延迟播放
-
Docker 一键部署
唯独缺一样东西:直播推流。
社区里一直有人问:"能不能把摄像头画面直接推到直播间?"——养宠物的想开宠物直播间,做安防的想做个公开监控频道,玩硬件的就想折腾。v0.8.1 把这个能力补上了。
直播推流:看着简单,做起来要命
第一步:先跑通
最直观的方案是调 FFmpeg——它是直播行业的瑞士军刀,OBS 底层也用它。在 MiBeeNVR 的"转推输出"里配一个 RTMP 目标,后端起一个 FFmpeg 子进程,画面确实能推到直播间。
[Image: MiBeeNVR 转推输出配置界面截图]
但问题是:FFmpeg 是外部进程。要单独安装、单独管理生命周期、占用额外内存。在一个目标是"树莓派上轻量运行"的项目里,拖着一个几十 MB 的外部进程跑推流,不够优雅。
我们想要的是:纯 Go 实现的 RTMP 推流,嵌进 NVR 进程里,零外部依赖。
第二步:纯 Go 推流的六层地狱
RTMP 协议看着简单——不就是握手、连接、推数据嘛。但真要对接国内直播平台(它们的后端都是 Adobe FMS 兼容实现),会发现每一层都能卡住你。
我们遇到的不是一个问题,是六层叠加的问题:
L1握手digestHMAC-SHA256签名L2chunksize分块大小不匹配L3chunkheaderType0/1/2/3格式L4后台读消息缓冲区撑爆L5元数据字段缺width/height/fpsL6streamID字节序大端vs小端⭐✅推流成功
前四层修完,握手通过了、连接建立了、推流命令成功了——但第一个视频帧发出去 74 毫秒,连接就被对方重置了。
这个 74ms 非常关键:它说明不是网络超时(那种要等 5 秒),而是对方在帧级别就拒绝了你的数据。
最终 Boss:一个字节序的故事
最后的根因藏得极深,以至于我们花了整整一天才找到。
RTMP 消息里有个字段叫 MessageStreamID,用来标识消息属于哪条流。按 RTMP 规范,这个字段应该是小端序(little-endian)。我们代码里就老老实实按规范写了小端序。
但所有主流实现(FFmpeg、OBS、gortmplib)都用了大端序。
直播平台也期望大端序。
我们的代码在大部分消息上走的是第三方库的标准路径(大端序),但在某些自定义写入路径上用了我们自己的实现(小端序)。同一个连接里,一会儿大端一会儿小端——对方收到这种精神分裂的数据包,直接 RST 断开。
发现这个 bug 靠的是进程内字节级 dump——把每个发出去的字节都记下来,逐条解析 chunk header,才发现 streamID 字段的大小端不一致。修复只改了 4 行代码,但找到它花了一整天。
Go标准WriterstreamID=大端直播平台期望大端✅我们的WriterstreamID=小端直播平台收到小端❌RST
一句话总结:规范说往左走,全世界都往右走,你跟着规范走,就被全世界拒绝。
最终效果
六层全部修复后:
-
✅ 纯 Go relay 推流成功,无 FFmpeg 依赖
-
✅ TCP 连接稳定保持,持续推流无中断
-
✅ 树莓派摄像头画面实时推到直播间,延迟约 2-3 秒
-
✅ 支持 H.264 摄像头,后续将支持 H.265 推流
⚠️ 不建议开启
use_ffmpeg选项。 FFmpeg 推流存在已知问题:播放中途可能因帧率不匹配导致卡顿,且不会自动恢复。纯 Go relay 路径已经过充分验证,是推荐的推流方式。
💡 纯 Go relay 是本次开发的重点。如果在其它平台遇到 Go 推流的问题,请优先提 Issue 反馈 Go 方面的问题,帮助项目持续改进推流能力——而不是切回 FFmpeg。
怎么用
方式一:MiBeeNVR 界面配置
在摄像头设置里找到"转推输出",填入直播平台的 RTMP 推流地址即可:
rtmp://<推流地址>/live/<推流码>
推流码从直播平台的推流设置里获取。
方式二:Docker 部署
docker run -d \
--name mibee-nvr \
-p 8080:8080 \
-v mibee-nvr-data:/var/lib/mibee-nvr \
ghcr.io/mi-bee-studio/mibee-nvr:latest
打开 http://<设备IP>:8080 进入管理界面,添加摄像头后配置转推输出。
测试情况与注意事项
已测试平台:斗鱼直播(直播伴侣模式,局域网推流)✅
理论兼容:所有 FMS 标准的直播平台后端。但各平台实现细节有差异,未经实测的平台可能遇到问题——遇到问题请到 GitHub Issues 反馈,作者会跟进。
⚠️ 重要提醒
不建议长期在国内直播平台持续推流。 各平台有自己的推流政策和限制,长时间无人值守的摄像头推流可能触发:
-
限流(降低码率/分辨率)
-
推流码过期后断连
-
账号警告甚至封禁
请务必了解你所用平台的相关政策,遵守平台规则。MiBeeNVR 提供的是技术能力,怎么用、用在哪个平台、推什么内容,责任在用户自己。
踩坑感悟
这次做直播推流,最大的感悟有三条:
1. 对比实验是调试的黄金标准
当 Go 代码推流失败时,我们不确定是代码问题还是环境问题。直到在同一个设备上用 FFmpeg 命令行手动推流成功,才确认是 Go 协议层的锅。没有这个对照,再多猜测都是空谈。
2. 二进制协议的 bug,必须看字节
字节序这种问题,靠读代码是看不出来的——因为代码"看起来是对的"(按规范写的)。只有把实际发出的字节 dump 出来逐条解析,才能发现大小端不一致。调试二进制协议,先加 dump,再分析。
3. 规范不等于现实
RTMP 规范说 MessageStreamID 是小端,但全世界都用大端。这种"规范与实践脱节"的情况在旧协议里非常常见。当你按规范写代码却到处碰壁时,去看看别人的实现是怎么做的——事实标准比纸面规范更有说服力。
关于 MiBeeNVR
MiBeeNVR 是 Mi-Bee-Studio 的开源项目,使用 Go 语言编写,已为树莓派等嵌入式设备优化。
GitHub: https://github.com/Mi-Bee-Studio/MiBeeNvr
主要特性:
-
多路摄像头管理(RTSP / ONVIF / 小米双摄)
-
H.264/H.265 硬件转码(V4L2 / VAAPI / NVENC 自动探测)
-
延时摄影录制与合成(纯 Go muxer,零编码开销)
-
LL-HLS / WebRTC 低延迟播放
-
RTMP 直播推流(v0.8.1 新增,纯 Go 实现)
-
Docker 一键部署
运行环境:树莓派 3B+ 及以上 / x86_64 / ARM64 / Docker
MiBeeNVR v0.8.1 已发布,完整变更列表见 GitHub Release Notes。
如果你也在用树莓派做有意思的项目,欢迎来 GitHub 交流。
更多推荐

所有评论(0)