oxideav-rtmp
Pure-Rust RTMP for the
oxideav framework — accept an
incoming publisher (server / source) or push your own stream to a
remote RTMP server (client / sink). Zero external dependencies,
blocking-thread-per-connection.
Server (accept a publisher)
use ;
let server = bind?;
let req = server.accept?; // blocks until one publisher connects
if req.stream_name != "my-secret-key"
let mut session = req.accept?; // sends NetStream.Publish.Start
while let Some = session.next_packet?
Multi-client variant — one thread per connection:
server.serve?;
Client (push to a remote RTMP server)
use RtmpClient;
let mut client = connect?;
client.send_video_sequence_header?; // AVCDecoderConfigurationRecord
client.send_audio_sequence_header?; // 2-byte AudioSpecificConfig
loop
client.close?;
Scope
- RTMP (
rtmp://, plain TCP port 1935). No RTMPS yet — wrap ourRead + Writewith rustls if you need it, or request anrtmpsfeature. - Publish direction only. The server accepts incoming publishers; the client pushes to remote servers. RTMP play (subscribe / pull) is a follow-up.
- AMF0 command flow. AMF3, shared objects, RTMFP, and the Adobe digest-verified handshake are not implemented; they're essentially unused in modern ingest workflows.
- H.264 + AAC are the expected payloads. The crate passes
through FLV tag bytes verbatim, so other codecs (MP3, H.263,
Speex, …) work end-to-end but the framing helpers focus on
VIDEO_CODEC_AVC+AUDIO_FORMAT_AAC.
Pipeline integration (SourceRegistry)
Wire rtmp:// URIs into the workspace's
[oxideav_core::SourceRegistry] so the pipeline executor reads
RTMP streams via the same dispatch as file:// and http(s)://:
use SourceRegistry;
let mut reg = new;
register;
// `rtmp://host:port/app/stream-name` opens a one-shot listener
// that accepts a single publisher and surfaces it as a
// `PacketSource` (audio = stream 0, video = stream 1, both with
// time_base 1/1000). Codec ids are auto-detected from the
// publisher's first audio + video tags (h264, aac, mp3, h263,
// vp6f / vp6a, flashsv / flashsv2).
let _src = reg.open?;
The opener is listen-style: each open() binds the URL's
host:port, accepts one publisher, validates the announced
app + stream_name against the URL path (rejects on
mismatch), then hands packets to the registry. For multi-client
service, keep using [RtmpServer::serve] directly.
Reusable building blocks
The lower-level modules are public so callers can compose something non-standard:
amf::{encode, decode, encode_command, Amf0Value}chunk::{ChunkReader, ChunkWriter, Message}handshake::{client_handshake, server_handshake}flv::{parse_video, build_video, parse_audio, build_audio}message::build_*— builders for every protocol-control / command message we emit