一个用 Rust 写的轻量流媒体服务器:RTMP 推流,HTTP-FLV 拉流,附带一个 Web 控制台。
推流端用 OBS / ffmpeg 往 1935 端口推 RTMP,浏览器端通过 HTTP-FLV 拉流播放, 不需要 RTMP 插件,也不依赖 Flash。
- RTMP 推流:监听 1935,支持流 key 鉴权
- HTTP-FLV 拉流:一个普通 HTTP 响应持续写 FLV 字节,前端挂 flv.js 即可播放
- 多观众:一条流可以同时被多个客户端拉取,各自独立
- 新观众补发:中途连入的客户端会先收到 SPS/PPS 和最近的关键帧,避免黑屏
- Web 控制台:列出当前所有流、推流方 IP、观众数,并可一键在线播放
ffmpeg / OBS
│ RTMP (1935)
▼
┌─────────────────┐
│ RtmpHandler │ on_publish / on_media_tag / on_unpublish
└────────┬────────┘
│ 每个媒体 tag
▼
┌─────────────────┐
│ Hub │ tokio::sync::broadcast,每个流一条通道
│ (广播 + 缓存) │ 缓存顺序头与最近关键帧,供新观众补发
└────────┬────────┘
│ 订阅
▼
┌─────────────────┐
│ HTTP-FLV 端点 │ axum,Body::from_stream 边写边发
└────────┬────────┘
│ HTTP (8080)
▼
浏览器 + flv.js
核心是中间的 Hub:RTMP 的回调是“推送式”的(帧到了就调你), HTTP 拉流是“拉取式”的(客户端按自己节奏读),两者节奏对不上, 中间必须有一层带缓冲的广播。
rust-flv-server/
├── Cargo.toml
├── .env
├── src/
│ ├── main.rs # 启动 RTMP + HTTP 两个服务
│ ├── hub.rs # 广播中枢与流缓存
│ ├── flv.rs # FlvTag -> FLV 字节流编码
│ └── handler.rs # RTMP 回调实现
└── static/
└── index.html # Web 控制台
.env:
RUST_LOG=info
RTMP_BIND=0.0.0.0:1935
HTTP_BIND=0.0.0.0:8080
cargo run启动后会看到:
HTTP 控制台监听于 http://0.0.0.0:8080
RTMP 服务监听于 0.0.0.0:1935
ffmpeg -re -stream_loop -1 -i .\cmzw_h264.mp4 -c copy -f flv rtmp://localhost:1935/live/live_test
- 流 key 必须以
live_开头 - 必须是 H.264
-re不能省,它让 ffmpeg 按真实时间发送- -stream_loop -1 循环推送,mp4播放完毕后重新播放
控制台里点“播放”即可在线观看。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/streams |
返回当前所有流的 JSON 列表 |
| GET | /live/{key} |
HTTP-FLV 拉流端点 |
| GET | / |
Web 控制台页面 |
/api/streams 返回示例:
[
{
"stream_key": "live_test",
"publisher": "127.0.0.1:52034",
"app": "live",
"viewers": 2,
"flv_url": "http://localhost:8080/live/live_test"
}
]flv.js 只支持 H.264(AVC)视频,不支持 H.265 / HEVC。 如果用 HEVC 编码的视频推流,浏览器控制台会报:
Flv: Unsupported codec in video frame: 1
推流前用 ffprobe 确认编码:
ffprobe test.mp4Video: 一行应显示 h264。
流 key 非空且以 live_ 开头才会被接受,否则推流会被拒绝。
规则写在 src/handler.rs 的 on_publish 里,可以按需修改。
1935:RTMP 推流8080:HTTP 拉流与控制台
两个端口都在 .env 里配置。
| crate | 用途 |
|---|---|
rtmp-rs |
RTMP 服务端与协议解析 |
tokio |
异步运行时 |
axum |
HTTP 服务与流式响应 |
tokio-stream |
把 broadcast 接收端包成 Stream |
tower-http |
静态文件服务 |
bytes |
零拷贝字节缓冲 |
serde / serde_json |
控制台 JSON 接口 |
tracing / tracing-subscriber |
日志 |
dotenvy |
读取 .env |
- HLS:挂在同一个
Hub上即可,广播层不用动 - 鉴权:把
on_publish里的规则换成查数据库或校验 token - 录制:在
on_media_tag里把 tag 另存为 FLV 文件
