ZLMediaKit 多路无人机流管理与分发架构
单架无人机推流到服务器很简单,调一个 API 就行。但当 10 架、50 架无人机同时接入,H265 转码把 CPU 吃满,WebRTC 连接数飙升,问题就来了。本文记录多路无人机流管理的完整架构设计,包含转码策略、协议分发和性能调优。
一、ZLMediaKit 在无人机平台中的定位

ZLMediaKit 的职责边界:
| 职责 | 说明 |
|---|---|
| 流接入 | 接收多路无人机 RTMP/RTSP 推流 |
| 转码调度 | 调用 FFmpeg 进行 H265 → H264 转码 |
| 协议转换 | 统一转出 WebRTC / HLS / HTTP-FLV |
| 流管理 | app/stream/vhost 三级管理 |
| 客户端管理 | WebSocket 信令、播放会话管理 |
不负责:视频存储(交给对象存储)、AI 分析(交给独立 AI 服务)、业务逻辑(交给后端应用)。
二、多路无人机接入方案
2.1 流命名规范
多路无人机接入,首先要建立统一的命名规范:
bash
rtmp://服务器IP:1935/{app}/{stream}
app 命名:uav(所有无人机统一 app)
stream 命名:{无人机ID}_{码流类型}
示例:
rtmp://server:1935/uav/uav_001_main # 1号机主码流
rtmp://server:1935/uav/uav_001_sub # 1号机辅码流
rtmp://server:1935/uav/uav_002_main # 2号机主码流
rtmp://server:1935/uav/uav_002_sub # 2号机辅码流
好处:
- 按无人机 ID 查询流地址,一目了然
- 主码流和辅码流分开管理,按需拉取
- 统一 app 前缀,方便 ZLMediaKit 配置鉴权
2.2 流注册与发现
无人机上线后推流,ZLMediaKit 自动注册流。后端通过 REST API 查询当前在线流:
bash
# 查询所有媒体流列表
curl "http://服务器IP:8080/index/api/getMediaList?secret=你的secret"
# 返回示例
{
"code": 0,
"data": [
{
"app": "uav",
"stream": "uav_001_main",
"originType": "rtmp_push",
"originUrl": "rtmp://192.168.1.100:1935/uav/uav_001_main",
"createStamp": 1690838400,
"aliveSecond": 3600,
"readerCount": 3
},
{
"app": "uav",
"stream": "uav_001_sub",
"originType": "rtmp_push",
"originUrl": "rtmp://192.168.1.100:1935/uav/uav_001_sub",
"createStamp": 1690838400,
"aliveSecond": 3600,
"readerCount": 1
}
]
}
关键字段:
| 字段 | 说明 |
|---|---|
originType |
推流类型:rtmp_push / rtmp_pull / ffmpeg |
readerCount |
当前观看人数,用于判断是否需要转码 |
aliveSecond |
流存活时间,用于超时清理 |
2.3 流上线/下线钩子
ZLMediaKit 支持 WebHook 通知,流上线和下线时自动回调后端:
ini
// config.ini 中配置
[hook]
enable=1
on_stream_changed=http://后端服务/api/stream/changed
后端收到通知后更新流状态表:
less
@PostMapping("/api/stream/changed")
public void onStreamChanged(@RequestBody StreamChangedHook hook) {
if ("on_publish".equals(hook.getAction())) {
// 无人机上线,注册流信息
streamService.register(hook.getApp(), hook.getStream(), hook.getOriginUrl());
// 检查是否需要启动转码
transcodeService.checkAndStart(hook.getApp(), hook.getStream());
} else if ("on_unpublish".equals(hook.getAction())) {
// 无人机下线,清理资源
streamService.unregister(hook.getApp(), hook.getStream());
// 停止转码进程
transcodeService.stop(hook.getApp(), hook.getStream());
}
}
这样就能实现无人机推流后自动转码、下线后自动清理,全程无需人工干预。
三、H265 转 H264 转码策略
3.1 按需转码(核心优化)
50 架无人机同时在线,如果全部转码,服务器扛不住。但实际上不是每路流都有人看。
策略:只有当某路流有 Firefox/Safari 用户请求播放时,才启动转码;无人观看时停止转码进程。
3.2 转码 API 调用
arduino
curl -X POST "http://服务器IP:8080/index/api/addFFmpegSource" \
-d "secret=你的secret" \
-d "src_url=rtmp://127.0.0.1:1935/uav/uav_001_main" \
-d "dst_url=rtmp://127.0.0.1/uav/uav_001_main_h264" \
-d "timeout_ms=10000" \
-d "enable_hls=0" \
-d "enable_mp4=0" \
-d "ffmpeg_cmd_key=live"
转码后的流命名规范:原流名 + _h264 后缀,方便查找。
3.3 FFmpeg 命令模板配置
在 ZLMediaKit 的 config.ini 中定义转码命令模板:
ini
[ffmpeg]
cmd=%s -re -i %s -c:v libx264 -preset ultrafast -tune zerolatency -g 8 -bf 0 -b:v 4M -maxrate 4M -minrate 4M -bufsize 8M -c:a aac -b:a 128k -f flv %s
如果使用 GPU 加速:
ini
[ffmpeg_nvenc]
cmd=%s -re -i %s -c:v h264_nvenc -preset p1 -tune ull -g 8 -bf 0 -b:v 4M -maxrate 4M -minrate 4M -bufsize 8M -c:a aac -b:a 128k -f flv %s
3.4 转码资源规划
| 并发路数 | CPU 软编码 | GPU 硬编码 (NVENC) | 推荐 |
|---|---|---|---|
| 1 路 1080p | 1 核满载 | GPU 8% | 均可 |
| 5 路 1080p | 5 核满载 | GPU 40% | 必须 GPU |
| 10 路 1080p | 10 核满载 | GPU 80% | 必须 GPU + 多卡 |
| 20 路 1080p | 不可行 | 2 张 GPU | 多卡 + 按需转码 |
实际经验:配合「按需转码」策略,一台 4 核服务器 + 1 张 RTX 3060 可支撑 20 路无人机接入,同时最多 5 路并发转码,日常运行 CPU 占用 30%。
四、输出协议选择与分发
4.1 三种输出协议对比
| 协议 | 延迟 | 浏览器原生支持 | 并发能力 | 适用场景 |
|---|---|---|---|---|
| WebRTC | <500ms | 需 JS 库 | 中(每连接占资源) | 指挥中心、实时操控 |
| HTTP-FLV | 1-3s | 需 flv.js | 高(HTTP 无状态) | Web 直播、多路预览 |
| HLS | 3-10s | 原生支持 | 极高(CDN 友好) | 大规模分发、回看 |
4.2 按场景选择输出协议
4.3 多路同屏优化
指挥中心常见需求:同时看 8-16 路无人机画面。
问题:8 路 WebRTC 连接 = 8 路独立的视频流传输,浏览器和服务器压力都很大。
解决方案:
- 小流预览:多路同屏时用辅码流(640x360),单台手机也能看 8 路
- HTTP-FLV 替代 WebRTC:多路预览不需要超低延迟,FLV 的 HTTP 无状态特性更适合高并发
- 点击放大切 WebRTC:默认 FLV 预览,点击某路切 WebRTC 看实时画面
ini
// 前端多路预览切换逻辑
function playStream(streamId, protocol) {
if (protocol === 'flv') {
// 多路预览用 FLV
const url = `http://server:8080/uav/${streamId}.live.flv`;
playWithFlvJs(url);
} else if (protocol === 'webrtc') {
// 单路放大用 WebRTC
const url = `http://server:8080/index/api/webrtc?app=uav&stream=${streamId}&type=play`;
playWithWebRTC(url);
}
}
五、流媒体服务器集群部署
单台 ZLMediaKit 的承载能力有限,路数多了必须水平扩展。
5.1 集群架构
5.2 流路由问题
集群模式下,无人机 A 推流到节点 1,用户请求播放时可能被分配到节点 2。节点 2 上没有这路流,播放失败。
解决方案:
| 方案 | 说明 | 优缺点 |
|---|---|---|
| Redis 注册中心 | 每个节点将流信息写入 Redis,播放请求查 Redis 后转发到源节点 | 简单可靠,推荐 |
| ZLMediaKit 内置集群 | 配置 cluster.originUrl 实现节点间流转发 |
原生支持,但配置复杂 |
| 固定路由 | 同一无人机的流固定推到同一节点 | 简单但负载不均 |
推荐 Redis 方案,后端服务统一调度:
kotlin
// 无人机上线时,记录流到节点的映射
redisTemplate.opsForValue().set(
"stream:" + streamId,
nodeIp,
30, TimeUnit.MINUTES // 30分钟无心跳自动过期
);
// 用户请求播放时,查询流所在节点
String nodeIp = redisTemplate.opsForValue().get("stream:" + streamId);
if (nodeIp == null) {
// 流不存在或已过期
return error("stream not found");
}
// 返回对应节点的播放地址
return playUrl(nodeIp, streamId);
5.3 节点健康检查
bash
# ZLMediaKit 健康检查接口
curl "http://节点IP:8080/index/api/getServerConfig"
# 检查关键指标
{
"code": 0,
"data": [{
"Buffer.AlwaysUseRange": "1",
"Media.heartBeatInterval": "30",
"Rtmp.port": "1935",
"Rtsp.port": "554",
"Http.port": "8080"
}]
}
配合负载均衡的健康检查,自动剔除故障节点。
六、监控与告警
6.1 关键监控指标
| 指标 | 告警阈值 | 说明 |
|---|---|---|
| 在线流数量 | 下降 >50% | 可能无人机批量掉线 |
| 转码进程数 | 接近上限 | 需要扩容或启动 GPU |
| CPU 使用率 | >85% | 转码瓶颈 |
| GPU 使用率 | >90% | NVENC 接近满载 |
| 推流断连次数 | 单机 >3次/分钟 | 网络不稳定 |
| WebRTC 连接数 | 接近上限 | 并发瓶颈 |
6.2 流断线检测
无人机在野外飞行,4G 信号不稳定,推流断连是常态。需要自动检测和重连:
less
@Scheduled(fixedRate = 10000) // 每10秒检查一次
public void checkStreamHealth() {
List<StreamInfo> onlineStreams = streamService.getOnlineStreams();
for (StreamInfo stream : onlineStreams) {
// 检查流最后活跃时间
if (stream.getLastActiveTime() < System.currentTimeMillis() - 30000) {
// 30秒无数据,判定断连
streamService.markOffline(stream.getStreamId());
// 通知前端该路画面已断开
websocketNotify(stream.getStreamId(), "stream_offline");
// 清理对应的转码进程
transcodeService.stop(stream.getApp(), stream.getStreamId());
}
}
}
七、总结
| 问题 | 方案 |
|---|---|
| 多路流命名混乱 | 统一规范:uav/uav_{ID}_{main/sub} |
| 转码 CPU 不够 | 按需转码 + GPU 加速 |
| 浏览器 H265 不兼容 | FFmpeg 实时转 H264 |
| 多路同屏卡顿 | 辅码流 + HTTP-FLV |
| 集群流路由 | Redis 注册中心 |
| 推流断连 | 心跳检测 + 自动重连 |
核心原则:按需分配资源。不是每路流都需要转码,不是每个用户都需要 WebRTC,不是每路都需要大流。根据实际场景动态调度,才能在有限服务器资源下支撑更多无人机。
觉得有用点个赞 👍,有问题评论区交流。