Releases: OpenRTMP/librtmp2
Releases · OpenRTMP/librtmp2
Release list
librtmp2 0.4.0
Added
- Server-side E-RTMP v2 connect negotiation on the live session path:
fourCcList, numericcapsExcapability bitmask,videoFourCcInfoMap,
reconnect, and NetConnection_errorresponses when capability negotiation
fails. - Multitrack media support (E-RTMP v2
AudioPacketType::Multitrack/
VideoPacketType::Multitrack): opaque relay of full multitrack messages,
per-trackon_frame_cbdelivery withFrame.track_id, and init-cache replay
of multitrack sequence-start headers to late-joining players. - Enhanced init-frame classification via
exvideo/exaudioparsers (HEVC, AV1,
Opus, AAC) inmedia/init_cache.rs, replacing legacy nibble-only detection. onMetaDatascript caching inStreamCacheand replay to players that join
after the publisher has already sent metadata.- Client receive path for AMF0/AMF3
onMetaDataand RTMP Aggregate messages
(0x16), unpacking sub-tags into the normal A/V and script frame callbacks. - Legacy RTMP commands on the server session path:
pause,seek,
receiveAudio,receiveVideo, andcloseStream. - User Control message handling for
StreamBegin,StreamEOF, and
SetBufferLength; AMF3 Shared Object messages are accepted as no-ops. examples/dump_frames.rs— play a stream and print one line per received frame
(type, timestamp, size, codec details) for debugging live publishers.- libFuzzer harnesses under
fuzz/for chunk reading, handshake parsing, AMF0
skipping, E-RTMP parsers, and RTMP control-message decoders. - CI jobs:
.github/workflows/sanitizers.yml(ASan unit tests, overflow-check
unit tests, ASan example builds) and.github/workflows/fuzz.yml(scheduled
libFuzzer smoke runs).
Changed
- The built-in relay fan-out budget is configurable through
Server::max_relay_sends_per_poll(default: 4096 sends per poll). The first
eligible frame in a poll is always processed even when its audience exceeds
the budget, preventing an oversized fan-out frame from being re-queued forever. - Connect AMF helpers now parse and write E-RTMP v2 capability representations,
including wildcard FourCC entries, numericcapsEx, and per-codec
videoFourCcInfoMapmasks. The built-in client continues to connect without
advertising capabilities; negotiation is active on the server session path. Framenow carries populated codec/header fields (video_fourcc,audio_fourcc,
composition time, etc.) and optionaltrack_idfor multitrack callbacks.- Corrected E-RTMP audio/video packet-type constants for sequence-end,
multichannel, and multitrack values. - Removed
docs/roadmap.md; release status lives inREADME.mdand
CHANGELOG.md.
Fixed
- ModEx prefix bytes are normalized only when the ModEx capability was negotiated
and the leading bytes form an unambiguous ModEx wrapper; enhanced
ExVideo/ExAudio, legacy AAC, and multitrack tags are preserved. Relay always
forwards the original payload. - Aggregate processing preserves relative sub-tag timestamps for A/V and metadata,
rejects malformed or truncated payloads, and routes script tags through the
normal metadata path. - Re-enabling
receiveAudioorreceiveVideoschedules init-cache replay;
paused players and disabled media types are filtered during relay. - Enhanced
CodedFramesXkeyframes and multitrack sequence headers are classified
correctly for late-join cache replay. - Complex RTMP handshake (digest/HMAC) is not implemented; peers requesting it
still receive a legacy simple S1/S2 response so ffmpeg and similar clients connect.
Security
- Bound E-RTMP capability blobs to 4 KiB and Aggregate messages to 4096 sub-tags
on both server and client receive paths; stream-cache resource accounting now
includes metadata and per-track headers.
librtmp2 0.3.1
Fixed
Client::connect()DNS resolution now respects the TCP connect deadline
instead of blocking indefinitely in the system resolver; lookups run on a
single shared worker thread with a bounded job queue, and worker startup
failures returnErrorCode::Internalinstead of panicking.send_frame_payload()andlrtmp2_client_send_framenow service inbound
RTMP UserControl ping requests before sending media, so in-tree clients
stay connected when the server enforces ping timeouts.- Ping responses issued while draining inbound messages during
poll()or
publishing now use nonblockingtry_sendinstead of blocking
Transport::send(), so a zero-timeout poll cannot stall for up to 10
seconds on a peer that stops reading. service_inbound_nonblocking()now honors the same per-poll byte and
message budgets aspoll(), and stops reading on transient EAGAIN instead
of spinning until the socket blocks.- Server-side ping RTT tracking starts only after the ping has fully left
send_buffer; unflushed pings queued behind a slow reader no longer start
the timeout early, and pings stuck unflushed longer thanPING_TIMEOUT
now close the connection. RTT timing now tracks the ping's own queued byte
range instead of waiting for the entiresend_bufferto drain, so prompt
ping responses are not discarded when later media remains queued. Client::poll()now works while publishing to service inbound pings and
retry queued ping responses after transientEAGAIN, and the shared DNS
worker is re-created after a transient thread-spawn failure instead of
permanently caching the error in aOnceLock.- Publishing clients now poll for socket writability (
POLLOUT) when queued
pong bytes remain after a transientEAGAIN, so idle publishers can flush
keepalive responses without sitting out the full read timeout. - Publishing clients also poll
POLLOUTwhen the send queue backs up during
media upload, and preserve the correct TLS poll direction when flushing
queued pong bytes after a transientEAGAIN. - Nonblocking publish sends now propagate
try_flush_send_buffer()errors
instead of dropping flush failures on the floor.
Security
- Inbound peers that open TCP but never complete the AMF connect exchange
(including partial legacy handshake bytes) are now closed after a 10-second
setup deadline instead of holding a connection slot until
max_connectionsis reached. - Server connections with unanswered or stale outbound RTMP pings are now
closed instead of accumulating indefinitely inpending_pings. - DNS lookups abandoned after the connect deadline no longer spawn unbounded
detached resolver threads, and the shared resolver job queue is capped so
wedged lookups cannot grow heap usage without bound.
librtmp2 0.3.0
Fixed
lrtmp2_client_create()silently ignoredServerConfig.tls_ca_fileand
ServerConfig.tls_insecure— the client always verifiedrtmps://peers
against only the system trust store regardless of what those fields were
set to, even though the ABI documented them as controlling client TLS
verification.Client/Transport::connect_tlsnow honor a caller-supplied
CA bundle or an explicit opt-out of verification.
Changed
Transport::connect_tls()(Rust-only API, not part of the FFI ABI) gained
two new parameters (ca_file: Option<&str>,insecure: bool) to support
the fix above. Bumped the minor version per this crate's pre-1.0
versioning policy (breaking Rust API change, ABI/FFI surface unaffected).
librtmp2 0.2.1
Added
lrtmp2_tls_supported()runtime capability check exported to FFI- Parse
onMetaDatafrom RTMP data messages intoConnfields (duration, width, height, framerate, videodatarate, audiodatarate, codec info)
Fixed
flv::audio_tag/video_tag/script_tagparsers now reset the
caller-owned tag struct at the start of everyparse()call, so switching
between codecs mid-stream (or a shorter value following a longer one, e.g.
a script tag name) no longer leaves stale fields from a previous parse- CodeQL invalid-pointer alert in FFI
server_createtest resolved - Borrow checker errors in AMF0
skip_value_depth onMetaDataparsing scope and lifecycle per review feedback
Security
- Cap and copy FFI frame payloads before sending and reject an oversized
frame.size; ignore inbound media whosemsg_stream_iddoesn't match
the current stream; retainon_frame_cbpayloads in connection-scoped
scratch buffers instead of a shared one - Add per-poll (256 KiB) and per-command-wait (256 KiB) byte budgets to the
RTMP client's recv path, mirroring the server's existing fairness cap, so
a malicious server can no longer monopolize the embedder's event-loop
thread or force hundreds of megabytes through the AMF connect handshake lrtmp2_server_createnow substitutes a defaultmax_connections(256)
when the FFI caller passes a zero-initializedServerConfig(e.g. via
calloc/{0}), which previously disabled all connection limiting; an
explicit negative value continues to mean "unlimited", matching
Server::new
Documentation
- Improved
onMetaDataparsing robustness per CodeRabbit/Codex reviews
librtmp2 0.2.0
Added
- RTMP Aggregate message (
0x16) handling: aggregate-framed audio/video
from a publisher is now unpacked and relayed through the normal
media-frame path instead of being silently dropped
Fixed
Client::connect()now actually acceptsrtmps://URLs via the new
Transport::connect_tls()(dialing over TLS and verifying the server
certificate against the system trust store) — previously only the
server side implemented RTMPS, despite the 0.1.0 notes describing
client-sidertmps://support;parse_rtmp_url()now recognizes the
scheme and defaults to port 443flv::audio_tag/video_tag/script_tagparsers now reset the
caller-owned tag struct at the start of everyparse()call, so switching
between codecs mid-stream (or a shorter value following a longer one, e.g.
a script tag name) no longer leaves stale fields from a previous parse- The RTMP client's inbound recv budget accounting no longer discards an
already-read chunk once it slightly exceeds the remaining budget; the read
itself is now capped at the remaining budget so bytes belonging to the
response being waited for are never dropped Client::poll()no longer risks blocking inpoll(2)for the full
timeout when TLS already has decrypted plaintext buffered internally from
a previous budget-limited drain- FFI
lrtmp2_client_send_frameandClient::send_frame_payloadnow
enforce the max client frame-size cap consistently on every call path,
and reject a stale/non-owned frame pointer when the client isn't in the
Publishingstate, instead of only checking on one of two paths - The server poll loop keeps draining a connection's already-buffered
messages (up to 3 extra passes) when a batch exceeds the per-recv message
budget, instead of stalling until the peer happens to send more bytes - The RTMPS client now checks TLS support before dialing, instead of
opening a plaintext TCP connection first even in a
--no-default-featuresbuild that given anrtmps://URL would only
reject after connecting - The client's blocking read helpers now poll for write-readiness (not
just read-readiness) when a TLS read reportsWANT_WRITEduring
renegotiation, instead of stalling until timeout on the wrong direction - The client's TLS handshake is now bounded by the same 10s timeout used
elsewhere in the transport, instead of blocking indefinitely if a peer
stalls mid-handshake - Retry
poll(2)onEINTRin the client's transport-readiness wait
helper instead of surfacing it as a hard I/O error and aborting the
caller's read/handshake
Security
- Cap and copy FFI frame payloads before sending and reject an oversized
frame.size; ignore inbound media whosemsg_stream_iddoesn't match
the current stream; retainon_frame_cbpayloads in connection-scoped
scratch buffers instead of a shared one - Add per-poll (256 KiB) and per-command-wait (256 KiB) byte budgets to the
RTMP client's recv path, mirroring the server's existing fairness cap, so
a malicious server can no longer monopolize the embedder's event-loop
thread or force hundreds of megabytes through the AMF connect handshake lrtmp2_server_createnow substitutes a defaultmax_connections(256)
when the FFI caller passes a zero-initializedServerConfig(e.g. via
calloc/{0}), which previously disabled all connection limiting; an
explicit negative value continues to mean "unlimited", matching
Server::new
librtmp2 0.1.1
Fixed
- Cap per-connection recv drain in
process_connections()to 256 KiB per
poll pass, preventing a peer that keeps its kernel recv buffer full from
starving other sessions in the single-threaded poll loop
Documentation
- Update docs.rs badge to track the latest published version
librtmp2 0.1.0
First tagged pre-release. librtmp2 is a Rust crate (built via Cargo as
cdylib/staticlib/lib) exposing both an idiomatic Rust API and an
FFI-compatible extern "C" layer for consumption from C, Go, Python, PHP,
and others.
Added
- TLS / RTMPS support via OpenSSL, enabled by default through the
tls
Cargo feature (cargo build --no-default-featuresfor a zero-dependency,
plaintext-only build) - Transport abstraction shared by plaintext RTMP and TLS so the layers above
never branch on the wire type - Server-side TLS termination and client-side
rtmps://connect with SNI
and certificate verification lrtmp2_tls_supported()runtime capability check- Legacy RTMP protocol support (handshake, chunk, message, AMF0)
- Enhanced RTMP v1 support (ExVideo/ExAudio headers, FourCC registry, HDR/colorInfo)
- Enhanced RTMP v2 support (capsEx, reconnect, multitrack, ModEx)
- Full server API with callbacks (
on_connect,on_publish,on_play,on_frame,on_close) - Full client API with publish/play flows
- Frame API supporting audio, video, script, and metadata types
- H.264, H.265, AV1, and legacy video codec support
- AAC, Opus, MP3, G.711 audio codec support
- Example programs:
minimal_server,minimal_client,play_pull,ffmpeg_ingest - Inline unit tests throughout
src/, an end-to-end loopback integration
test (tests/server_client_loopback.rs), and interop shell scripts
(tests/interop/) - ABI baseline tooling (
scripts/abi-baseline.sh) for0.xcompatibility checks - Automated ABI compliance checks in CI (
cargo-semver-checksvia.github/workflows/abi-check.yml)
Security
- Bounds-checked parsers for all network-provided length fields
- Constant-time RNG for handshake
- Safe handling of unknown E-RTMP v2 ModEx types (degrades to NOP, not panic)
Documentation
CLAUDE.mdwith build commands and architecture guidedocs/abi-policy.mdwith ABI compliance checklist- Protocol mapping documents for legacy, E-RTMP v1, and E-RTMP v2
CONTRIBUTING.mdguidelines