Skip to content

Releases: OpenRTMP/librtmp2

librtmp2 0.4.0

Choose a tag to compare

@github-actions github-actions released this 15 Jul 14:59
99a2347

Added

  • Server-side E-RTMP v2 connect negotiation on the live session path:
    fourCcList, numeric capsEx capability bitmask, videoFourCcInfoMap,
    reconnect, and NetConnection _error responses when capability negotiation
    fails.
  • Multitrack media support (E-RTMP v2 AudioPacketType::Multitrack /
    VideoPacketType::Multitrack): opaque relay of full multitrack messages,
    per-track on_frame_cb delivery with Frame.track_id, and init-cache replay
    of multitrack sequence-start headers to late-joining players.
  • Enhanced init-frame classification via exvideo/exaudio parsers (HEVC, AV1,
    Opus, AAC) in media/init_cache.rs, replacing legacy nibble-only detection.
  • onMetaData script caching in StreamCache and replay to players that join
    after the publisher has already sent metadata.
  • Client receive path for AMF0/AMF3 onMetaData and 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, and closeStream.
  • 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, numeric capsEx, and per-codec
    videoFourCcInfoMap masks. The built-in client continues to connect without
    advertising capabilities; negotiation is active on the server session path.
  • Frame now carries populated codec/header fields (video_fourcc, audio_fourcc,
    composition time, etc.) and optional track_id for multitrack callbacks.
  • Corrected E-RTMP audio/video packet-type constants for sequence-end,
    multichannel, and multitrack values.
  • Removed docs/roadmap.md; release status lives in README.md and
    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 receiveAudio or receiveVideo schedules init-cache replay;
    paused players and disabled media types are filtered during relay.
  • Enhanced CodedFramesX keyframes 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

Choose a tag to compare

@github-actions github-actions released this 13 Jul 17:35

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 return ErrorCode::Internal instead of panicking.
  • send_frame_payload() and lrtmp2_client_send_frame now 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 nonblocking try_send instead 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 as poll(), 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 than PING_TIMEOUT
    now close the connection. RTT timing now tracks the ping's own queued byte
    range instead of waiting for the entire send_buffer to 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 transient EAGAIN, and the shared DNS
    worker is re-created after a transient thread-spawn failure instead of
    permanently caching the error in a OnceLock.
  • Publishing clients now poll for socket writability (POLLOUT) when queued
    pong bytes remain after a transient EAGAIN, so idle publishers can flush
    keepalive responses without sitting out the full read timeout.
  • Publishing clients also poll POLLOUT when the send queue backs up during
    media upload, and preserve the correct TLS poll direction when flushing
    queued pong bytes after a transient EAGAIN.
  • 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_connections is reached.
  • Server connections with unanswered or stale outbound RTMP pings are now
    closed instead of accumulating indefinitely in pending_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

Choose a tag to compare

@github-actions github-actions released this 12 Jul 21:09
245b4fb

Fixed

  • lrtmp2_client_create() silently ignored ServerConfig.tls_ca_file and
    ServerConfig.tls_insecure — the client always verified rtmps:// 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_tls now 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

Choose a tag to compare

@github-actions github-actions released this 10 Jul 20:58

Added

  • lrtmp2_tls_supported() runtime capability check exported to FFI
  • Parse onMetaData from RTMP data messages into Conn fields (duration, width, height, framerate, videodatarate, audiodatarate, codec info)

Fixed

  • flv::audio_tag / video_tag / script_tag parsers now reset the
    caller-owned tag struct at the start of every parse() 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_create test resolved
  • Borrow checker errors in AMF0 skip_value_depth
  • onMetaData parsing scope and lifecycle per review feedback

Security

  • Cap and copy FFI frame payloads before sending and reject an oversized
    frame.size; ignore inbound media whose msg_stream_id doesn't match
    the current stream; retain on_frame_cb payloads 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_create now substitutes a default max_connections (256)
    when the FFI caller passes a zero-initialized ServerConfig (e.g. via
    calloc/{0}), which previously disabled all connection limiting; an
    explicit negative value continues to mean "unlimited", matching
    Server::new

Documentation

  • Improved onMetaData parsing robustness per CodeRabbit/Codex reviews

librtmp2 0.2.0

Choose a tag to compare

@github-actions github-actions released this 10 Jul 17:30
d421cc5

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 accepts rtmps:// 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-side rtmps:// support; parse_rtmp_url() now recognizes the
    scheme and defaults to port 443
  • flv::audio_tag / video_tag / script_tag parsers now reset the
    caller-owned tag struct at the start of every parse() 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 in poll(2) for the full
    timeout when TLS already has decrypted plaintext buffered internally from
    a previous budget-limited drain
  • FFI lrtmp2_client_send_frame and Client::send_frame_payload now
    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
    Publishing state, 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-features build that given an rtmps:// 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 reports WANT_WRITE during
    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) on EINTR in 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 whose msg_stream_id doesn't match
    the current stream; retain on_frame_cb payloads 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_create now substitutes a default max_connections (256)
    when the FFI caller passes a zero-initialized ServerConfig (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

Choose a tag to compare

@github-actions github-actions released this 08 Jul 18:20
1527e80

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

Choose a tag to compare

@github-actions github-actions released this 07 Jul 22:31
e801199

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-features for 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) for 0.x compatibility checks
  • Automated ABI compliance checks in CI (cargo-semver-checks via .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.md with build commands and architecture guide
  • docs/abi-policy.md with ABI compliance checklist
  • Protocol mapping documents for legacy, E-RTMP v1, and E-RTMP v2
  • CONTRIBUTING.md guidelines