ZLMediaKit 与 Spring Boot 集成流媒体:30 分钟从部署跑到第一路直播
【免费下载链接】ZLMediaKitWebRTC/RTSP/RTMP/HTTP/HLS/HTTP-FLV/WebSocket-FLV/HTTP-TS/HTTP-fMP4/WebSocket-TS/WebSocket-fMP4/GB28181/SRT/STUN/TURN server and client framework based on C++11项目地址: https://gitcode.com/GitHub_Trending/zl/ZLMediaKit
ZLMediaKit 是一个 C++ 流媒体服务器,支持 RTMP、RTSP、HLS、HTTP-FLV、WebRTC、GB28181、SRT 等协议。把它和 Spring Boot 集成后,流媒体的苦活累活交给 ZLMediaKit,你的 Java 业务系统只管账号、鉴权、数据库和 REST 接口。这篇指南写给会 Java、对流媒体陌生的开发者,走完一遍,你的服务里就有一路可以播放的直播流。
分工:ZLMediaKit 管流,Spring Boot 管业务
ZLMediaKit 负责把流收进来、转成各种协议、分发给多端播放,你不需要写一行 RTP 或 RTSP 协议代码。Spring Boot 负责业务:谁有权限推流、流属于哪个房间、播放记录存哪张表。两者之间只有两条通道:
- HTTP API:你主动调 MediaServer 的 REST 接口,完成拉流代理、开 RTP 端口、查流列表这类控制动作。
- WebHook:MediaServer 在推流、播放、流上下线等时刻回调你的业务接口,你的返回 JSON 决定放行还是拒绝。
一推一拉,控制流和事件流就都闭环了。
从零到第一路流 🎬
部署 MediaServer 并确认 HTTP API 可用
仓库源码入口是 server/,编译后得到 MediaServer 可执行文件:
mkdir build && cd build cmake .. && make -j4 ./release/linux/Debug/MediaServer # 默认加载同目录 config.ini部署注意一点:MediaServer 读的是可执行文件同目录下的 config.ini,不是仓库里的 conf/config.ini,后者只是带注释的范例。
改最小配置:一个 secret,一个回调地址
只改三处,其余保持默认:
[api] secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc # 改成你自己的,Spring Boot 侧保持一致 [hook] enable=1 on_publish=http://127.0.0.1:8080/api/hook/on_publish [ffmpeg] bin=/usr/bin/ffmpeg # 按你机器的实际路径改改完重启 MediaServer。127.0.0.1 访问 API 时 secret 可免,跨机部署则必须传对。
用 addStreamProxy 拉一路流,拿播放地址
Spring Boot 端用 RestTemplate 调拉流代理接口,让 MediaServer 把一路 RTMP 源拉进来再分发:
// 调 ZLMediaKit 的拉流代理接口,成功后 MediaServer 自动多协议分发 MultiValueMap<String, String> p = new LinkedMultiValueMap<>(); p.add("secret", secret); // 与 config.ini 的 [api] secret 一致 p.add("vhost", "__defaultVhost__"); p.add("app", "live"); p.add("stream", "s1"); p.add("url", "rtmp://源地址/live/s1"); // 要拉取的源流 String body = restTemplate.postForObject(apiBase + "/addStreamProxy", p, String.class);code=0即成功,播放地址可直接拼出:http://ip:80/index/live/s1.flv(HTTP-FLV)或http://ip:554/live/s1(RTSP)。完整接口清单可看源码 server/WebApi.cpp,或调/index/api/getApiList自己列一遍。
让 on_publish 拦推流鉴权
设备或推流端直连推流时,靠 hook 做拦截。你的 hook 接口必须快速返回 JSON,code=0放行:
@PostMapping("/api/hook/on_publish") public Map<String, Object> onPublish(@RequestParam Map<String, String> q) { // 推流鉴权:查库校验该 stream 是否属于合法用户 boolean ok = streamService.checkOwner(q.get("app"), q.get("stream")); return ok ? Map.of("code", 0) : Map.of("code", -1, "msg", "无权推流"); }按场景选能力
直播平台:鉴权 + 生命周期
- 推流/播放鉴权用
on_publish和on_playhook,返回code控制放行。 - 流上下线监听
on_stream_changed,在这里写业务库,别自己去轮询。 - 源流拉不动时用
addStreamProxy的retry_count参数配置自动重拉。
安防 GB28181:收流 + 抓拍
openRtpServer打开 RTP 收流端口,设备按国标推流进来。getSnap生成截图,on_rtp_server_timeout处理设备静默离线。- 播放地址统一走
/index/api/getStreamUrl,前端只认一套 HTTP 地址。
WebRTC 互动:信令 + 房间
/index/api/webrtc做 SDP 协商,Spring Boot 负责把 offer/answer 在客户端与 MediaServer 间转发。addWebrtcRoomKeeper建房、listWebrtcRooms查房。on_flow_report上报流量,做计费或带宽监控。
工程化建议
- HTTP 客户端用连接池 + 显式超时:
HttpComponentsClientHttpRequestFactory设 connect 3s、read 10s,避免慢请求拖垮业务线程。 - hook 处理器全部异步:hook 回调有超时(
hook.timeoutSec默认 10 秒,conf/config.ini 可调),超时按拒绝处理。把查库逻辑丢进线程池,别让同步 IO 拖慢响应。 - 流状态以 on_stream_changed 为准:业务侧只记
app/stream/在线映射,播放地址用getStreamUrl现算,别自己拼。 - 高频查询走缓存:
getMediaList这类接口别每个请求都打,本地 Caffeine 缓存 3~5 秒足够。
容易踩的坑
secret 对不上,所有 API 返回无权限现象是调用 API 报forbidden;原因是业务配置和 MediaServer 的[api] secret不一致。解法:两边对齐,并用/index/api/version这个轻量接口先自测连通性。
hook 返回体不规范,推流被拒现象是推流失败但日志没报错;原因是 hook 响应必须是含code: 0的 JSON,且stream_id等附加字段要传字符串。解法:在 hook 接口上加日志,先打印实际收到的参数和返回体。
流状态两边不同步现象是前端显示在线、MediaServer 里流已消失;原因是你只在 API 调用时更新状态,流断开时没有感知。解法:以on_stream_changed回调为唯一状态源,业务库只在这里写。
拉流代理断流后不再恢复现象是addStreamProxy源端抖动一次后彻底失联;原因是没配重试。解法:调用时带retry_count,并用getProxyInfo定期核对代理状态,异常则重新下发。
收尾
这套组合的边界很清楚:ZLMediaKit 把协议、转发、分发这些最难的部分封装成几十个 HTTP API 和一组 hook,Spring Boot 把鉴权、状态、业务逻辑收敛在自己的世界里。两条通道各自独立,任何一方重启都不会影响另一方的核心职责,这也是它适合长期承载生产流量的原因。
【免费下载链接】ZLMediaKitWebRTC/RTSP/RTMP/HTTP/HLS/HTTP-FLV/WebSocket-FLV/HTTP-TS/HTTP-fMP4/WebSocket-TS/WebSocket-fMP4/GB28181/SRT/STUN/TURN server and client framework based on C++11项目地址: https://gitcode.com/GitHub_Trending/zl/ZLMediaKit
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考