Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Rust写一个rtmp流媒体服务

项目是一个 RTMP 流媒体服务器。功能很具体:接收推流端(比如 FFmpeg)送来的音视频数据,按流 key 分发给所有拉流端(比如播放器),并在连接和推流时做鉴权——只有 app 正确、stream key 符合约定的客户端才放行。跑起来之后,你可以一边用 FFmpeg 推、一边用播放器拉,同时看到服务器打出的连接、推流、拉流、结束的完整日志。

用到的库分两类。

标准库(std 自带,不用写进依赖):

  • std::collections 的 HashSet 和 HashMap:前者记录谁在推流,后者接收元数据回调传来的键值对
  • std::sync 的 Mutex 和 AtomicUsize:让多份连接共享的状态能被安全修改
  • std::net 的地址类型:解析 0.0.0.0:1935

第三方库(写进 Cargo.toml):

  • rtmp-rs:RTMP 协议的实现,提供服务器骨架 RtmpServer 和一份回调契约 RtmpHandler。协议细节由它处理,你只写业务规则
  • tokio:异步运行时。服务器要同时应付很多连接,靠它驱动
  • tracing 和 tracing-subscriber:日志。前者是打日志的宏,后者负责输出到终端并读 RUST_LOG 控制级别
  • dotenvy:读取 .env 文件,让日志级别不用改代码就能调

RTMP 是一个基于 TCP 的直播传输协议,用 rtmp://host:port/app/stream_key 定位一路流,推流端交帧、拉流端取帧,服务器按流 key 转发。

flowchart LR
    A["ffmpeg<br>推流端"] -->|"publish<br>stream_key = live_test"| S["RTMP 服务器<br>:1935"]
    S -->|"转发帧"| B["播放器 A<br>拉流 live_test"]
    S -->|"转发帧"| C["播放器 B<br>拉流 live_test"]
Loading

跑起来验证

先编译:

cargo build

编译成功后运行起来:

# 启动服务器
cargo run

另开一个终端 推流:

ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost:1935/live/live_test

-re 不能省,加上它才按原始帧率送帧,模拟真实的直播节奏。

推流成功后,服务器那个终端会打出 开始推流 的 info 日志,其中的 online_streams=1 就是当前的在线流数量——这正是 publishing 集合在统计的东西。

再开一个终端 拉流:

ffplay rtmp://localhost:1935/live/live_test

验证流 key 鉴权。 用不合规的流 key 推一次:

ffmpeg -re -i test.mp4 -c copy -f flv rtmp://localhost:1935/live/badkey

服务器端会打出 FCPublish 拒绝 的 warn 日志,因为 badkey 不以 live_ 开头。

推拉流示例

推拉流播放示例

运行成功log

启动成功


2026-10-03T05:58:06.343218Z  INFO rtmp_server: RTMP 服务器启动,等待推流 addr=0.0.0.0:1935
2026-10-03T05:58:06.354590Z  INFO rtmp_rs::server::listener: RTMP server listening addr=0.0.0.0:1935

推流成功


2026-10-03T05:58:48.679254Z DEBUG rtmp_rs::server::listener: New connection session_id=1 peer=127.0.0.1:4356
2026-10-03T05:58:48.679939Z DEBUG rtmp_server::handler: 新建 TCP 连接 session_id=1 peer=127.0.0.1:4356 connections=1
2026-10-03T05:58:48.694464Z DEBUG rtmp_rs::server::connection: Handshake complete session_id=1 remaining_buf=0
2026-10-03T05:58:48.694846Z DEBUG rtmp_server::handler: 握手完成 session_id=1
2026-10-03T05:58:48.694969Z DEBUG rtmp_rs::server::connection: Sending set chunk size session_id=1 chunk_size=4096
2026-10-03T05:58:48.695145Z DEBUG rtmp_rs::server::connection: Entering main message loop session_id=1
2026-10-03T05:58:48.695346Z DEBUG rtmp_rs::server::connection: Received command session_id=1 command="connect" transaction_id=1.0 stream_id=0 args=[]
2026-10-03T05:58:48.695509Z DEBUG rtmp_rs::server::connection: Using legacy RTMP (client doesn't support E-RTMP). The client may still send enhanced audio/video session_id=1
2026-10-03T05:58:48.695613Z DEBUG rtmp_server::handler: connect 通过 session_id=1 app=live
2026-10-03T05:58:48.695839Z  INFO rtmp_rs::server::connection: Connected session_id=1 app="live" enhanced_rtmp=false
2026-10-03T05:58:48.695975Z DEBUG rtmp_rs::server::connection: Peer set chunk size size=4096
2026-10-03T05:58:48.696295Z DEBUG rtmp_rs::server::connection: Received command session_id=1 command="releaseStream" transaction_id=2.0 stream_id=0 args=[String("live_test")]
2026-10-03T05:58:48.696581Z DEBUG rtmp_rs::server::connection: Received command session_id=1 command="FCPublish" transaction_id=3.0 stream_id=0 args=[String("live_test")]
2026-10-03T05:58:48.696737Z DEBUG rtmp_server::handler: FCPublish 通过 session_id=1 stream_key="live_test"
2026-10-03T05:58:48.696956Z DEBUG rtmp_rs::server::connection: Received command session_id=1 command="createStream" transaction_id=4.0 stream_id=0 args=[]
2026-10-03T05:58:48.697135Z DEBUG rtmp_rs::server::connection: Stream created stream_id=1
2026-10-03T05:58:48.697271Z DEBUG rtmp_rs::server::connection: Received command session_id=1 command="publish" transaction_id=5.0 stream_id=1 args=[String("live_test"), String("live")]
2026-10-03T05:58:48.697397Z  INFO rtmp_server::handler: 开始推流 session_id=1 stream_key=live_test publish_type=live online_streams=1
2026-10-03T05:58:48.697572Z  INFO rtmp_rs::registry::store: Publisher registered (new stream) stream=live/live_test session_id=1
2026-10-03T05:58:48.697763Z  INFO rtmp_rs::server::connection: Publishing started session_id=1 stream_key="live_test"
2026-10-03T05:58:48.698350Z DEBUG rtmp_server::handler: 收到元数据 stream_key=live_test fields=17
2026-10-03T05:58:48.698508Z  INFO rtmp_rs::server::connection: Video codec detected session_id=1 codec="H.265/HEVC" enhanced=true
2026-10-03T05:58:48.698714Z DEBUG rtmp_server::handler: 原始 FLV tag stream_key=live_test
2026-10-03T05:58:48.698822Z DEBUG rtmp_server::handler: E-RTMP 视频帧 stream_key=live_test timestamp=0
2026-10-03T05:58:48.698980Z  INFO rtmp_rs::server::connection: Audio codec detected session_id=1 codec="AAC" enhanced=false
2026-10-03T05:58:48.699069Z DEBUG rtmp_server::handler: 原始 FLV tag stream_key=live_test
2026-10-03T05:58:48.699189Z DEBUG rtmp_server::handler: 音频帧 stream_key=live_test timestamp=0
2026-10-03T05:58:48.701045Z DEBUG rtmp_server::handler: 原始 FLV tag stream_key=live_test
2026-10-03T05:58:48.701676Z DEBUG rtmp_server::handler: E-RTMP 视频帧 stream_key=live_test timestamp=0
2026-10-03T05:58:48.701890Z DEBUG rtmp_server::handler: 关键帧 stream_key=live_test timestamp=0
2026-10-03T05:58:48.702004Z DEBUG rtmp_server::handler: 原始 FLV tag stream_key=live_test

结束推流


2026-10-03T05:58:54.872353Z DEBUG rtmp_server::handler: 原始 FLV tag stream_key=live_test
2026-10-03T05:58:54.872454Z DEBUG rtmp_server::handler: 音频帧 stream_key=live_test timestamp=6789
2026-10-03T05:58:54.902714Z DEBUG rtmp_server::handler: 原始 FLV tag stream_key=live_test
2026-10-03T05:58:54.903156Z DEBUG rtmp_server::handler: E-RTMP 视频帧 stream_key=live_test timestamp=6792
2026-10-03T05:58:54.903442Z DEBUG rtmp_rs::server::connection: Received command session_id=1 command="FCUnpublish" transaction_id=6.0 stream_id=0 args=[String("live_test")]
2026-10-03T05:58:54.903783Z DEBUG rtmp_rs::server::connection: Received command session_id=1 command="deleteStream" transaction_id=7.0 stream_id=0 args=[Number(1.0)]
2026-10-03T05:58:54.903932Z  INFO rtmp_server::handler: 停止推流 session_id=1 stream_key="live_test" event="publish_stop" online_streams=0
2026-10-03T05:58:54.904055Z DEBUG rtmp_server::handler: 收到结束事件,但该 key 不在推流集合中 session_id=1 stream_key="live_test" event="unpublish"
2026-10-03T05:58:54.904342Z DEBUG rtmp_rs::server::connection: Processing error error=I/O error: 你的主机中的软件中止了一个已建立的连接。 (os error 10053)
2026-10-03T05:58:54.905327Z  INFO rtmp_rs::registry::store: Publisher disconnected, no subscribers stream=live/live_test session_id=1
2026-10-03T05:58:54.905546Z DEBUG rtmp_rs::server::connection: Unregistered publisher on disconnect session_id=1 stream=live/live_test
2026-10-03T05:58:54.905644Z  INFO rtmp_server::handler: 连接断开 session_id=1 app=live connections=0
2026-10-03T05:58:54.905911Z DEBUG rtmp_rs::server::listener: Connection error session_id=1 error=I/O error: 你的主机中的软件中止了一个已建立的连接。 (os error 10053)
2026-10-03T05:58:54.906089Z DEBUG rtmp_rs::server::listener: Connection closed session_id=1
2026-10-03T05:59:26.364025Z  INFO rtmp_rs::registry::store: Stream removed by cleanup stream=live/live_test

公众号

期待你的关注!这里会持续分享 Rust 相关的技术干货,也欢迎随时找我交流讨论,共同进步。

公众号

About

Rust

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages