Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
bd0b4d3
docs: align README with code-accurate implementation status
AlexanderWagnerDev Jul 14, 2026
030e454
feat: E-RTMP v2 connect negotiation and multitrack relay (0.4.0)
AlexanderWagnerDev Jul 14, 2026
551909a
fix: resolve compile errors for 0.4.0 CI
AlexanderWagnerDev Jul 14, 2026
6991837
fix(tests): update Stream and multitrack test for CI
AlexanderWagnerDev Jul 14, 2026
feab86e
fix: address Bugbot findings for ModEx strip and multitrack callbacks
AlexanderWagnerDev Jul 14, 2026
d064938
fix(ci): handshake, multitrack cache test, and e2e frame lifetime
AlexanderWagnerDev Jul 14, 2026
aae745b
fix: honor zero capsEx and split multitrack on play client
AlexanderWagnerDev Jul 14, 2026
110a6f6
fix: preserve legacy capsEx object negotiation
AlexanderWagnerDev Jul 14, 2026
17b334b
fix: address Codex review findings for E-RTMP v2 path
AlexanderWagnerDev Jul 14, 2026
d9d5a81
fix(ci): qualify Amf0Type in read_close_stream
AlexanderWagnerDev Jul 14, 2026
617d3d2
fix(tests): expect combined multitrack init in avc_header slot
AlexanderWagnerDev Jul 14, 2026
77571e7
fix: preserve metadata timestamps and ignore malformed pause
AlexanderWagnerDev Jul 14, 2026
a9972ee
chore: stage automated PR 128 fixes
AlexanderWagnerDev Jul 14, 2026
77d79f5
ci: apply and validate PR 128 fixes
AlexanderWagnerDev Jul 14, 2026
17ffe99
ci: trigger PR 128 fix validation on synchronize
AlexanderWagnerDev Jul 14, 2026
f882604
ci: supersede stale PR 128 fix runs
AlexanderWagnerDev Jul 14, 2026
2ff20a5
ci: make PR 128 fix runner self-reporting
AlexanderWagnerDev Jul 14, 2026
f391039
ci: capture PR 128 fix failure
github-actions[bot] Jul 14, 2026
cc42835
ci: patch RelayFrame test helper
AlexanderWagnerDev Jul 14, 2026
bd0b63f
ci: validate RelayFrame follow-up
AlexanderWagnerDev Jul 14, 2026
2ba7f5c
ci: capture PR 128 fix failure
github-actions[bot] Jul 14, 2026
b99e7aa
ci: fix wildcard parser and multicodec fixture
AlexanderWagnerDev Jul 14, 2026
ba2b1a1
ci: validate wildcard and multicodec fixes
AlexanderWagnerDev Jul 14, 2026
36dc884
ci: capture PR 128 fix failure
github-actions[bot] Jul 14, 2026
a270a29
ci: validate tests without executing benchmarks
AlexanderWagnerDev Jul 14, 2026
10e5e8c
fix: address E-RTMP v2 review findings
github-actions[bot] Jul 14, 2026
44e3b5b
test: cover E-RTMP v2 review regressions
AlexanderWagnerDev Jul 14, 2026
3af87f4
chore: stage ModEx multitrack gate fix
AlexanderWagnerDev Jul 14, 2026
b282c3f
ci: validate ModEx multitrack gate fix
AlexanderWagnerDev Jul 14, 2026
50c54ad
ci: trigger ModEx gate validation on PR sync
AlexanderWagnerDev Jul 14, 2026
ff051de
fix: gate ModEx-wrapped multitrack relay
github-actions[bot] Jul 14, 2026
ad021f9
test: cover wildcard video capability maps
AlexanderWagnerDev Jul 14, 2026
92757eb
chore: apply PR 128 Cursor findings
AlexanderWagnerDev Jul 14, 2026
63b1673
fix: make PR 128 Cursor patch application robust
AlexanderWagnerDev Jul 14, 2026
7a6fa7c
chore: add robust PR 128 fix script
AlexanderWagnerDev Jul 14, 2026
1620376
chore: run robust PR 128 fix script
AlexanderWagnerDev Jul 14, 2026
293086f
chore: capture PR 128 test failure details
AlexanderWagnerDev Jul 14, 2026
bdfe6e6
chore: save PR 128 test diagnostics
github-actions[bot] Jul 14, 2026
c02dd5a
chore: validate PR 128 findings with cargo test
AlexanderWagnerDev Jul 14, 2026
d2f66d0
fix: address Cursor findings on PR 128
github-actions[bot] Jul 14, 2026
da4524a
docs: update 0.4.0 changelog date and coverage
AlexanderWagnerDev Jul 14, 2026
4ef9ae6
docs: sync Debian 0.4.0 changelog date and notes
AlexanderWagnerDev Jul 14, 2026
037012c
ci: apply latest PR 128 review fixes
AlexanderWagnerDev Jul 15, 2026
4764098
ci: safely apply latest PR review fixes
AlexanderWagnerDev Jul 15, 2026
fea7e2e
ci: run one-shot review fixes on PR update
AlexanderWagnerDev Jul 15, 2026
7a9a4ec
ci: add one-shot PR 128 fix script
AlexanderWagnerDev Jul 15, 2026
23496f7
ci: run PR 128 fixes from standalone script
AlexanderWagnerDev Jul 15, 2026
f6555a7
ci: capture PR 128 patch diagnostics
AlexanderWagnerDev Jul 15, 2026
59324a7
ci: record PR 128 patch failure
github-actions[bot] Jul 15, 2026
3e5afbd
ci: preserve Rust indentation in PR 128 patch
AlexanderWagnerDev Jul 15, 2026
0cc356f
ci: capture PR 128 validation diagnostics
AlexanderWagnerDev Jul 15, 2026
475cefe
ci: record PR 128 validation failure
github-actions[bot] Jul 15, 2026
ca1bc41
ci: add PR 128 validation follow-up
AlexanderWagnerDev Jul 15, 2026
7815c0b
ci: apply PR 128 validation follow-up
AlexanderWagnerDev Jul 15, 2026
fe0a203
ci: reliably record PR 128 diagnostics
AlexanderWagnerDev Jul 15, 2026
f5d500d
ci: record PR 128 validation failure
github-actions[bot] Jul 15, 2026
349928e
ci: validate PR 128 fixes without benchmarks
AlexanderWagnerDev Jul 15, 2026
09192b0
fix: address latest PR review findings
github-actions[bot] Jul 15, 2026
96bbda5
chore: stage PR #130 conflict resolution
AlexanderWagnerDev Jul 15, 2026
4965475
fix(security): cap built-in relay fan-out per poll tick
github-actions[bot] Jul 15, 2026
aa64aa3
ci: apply PR 128 review fixes
AlexanderWagnerDev Jul 15, 2026
1b81ca0
ci: run PR 128 fixes as pull request check
AlexanderWagnerDev Jul 15, 2026
1fa7e0a
ci: apply PR 128 findings before validation
AlexanderWagnerDev Jul 15, 2026
966bdf1
ci: add PR 128 review patch script
AlexanderWagnerDev Jul 15, 2026
f31daf0
ci: run valid PR 128 review fixer
AlexanderWagnerDev Jul 15, 2026
fe82870
fix: harden multitrack callback handling
github-actions[bot] Jul 15, 2026
89d7038
ci: trigger validation for PR 128 fixes
AlexanderWagnerDev Jul 15, 2026
2806866
ci: remove PR 128 validation trigger
AlexanderWagnerDev Jul 15, 2026
6f083b4
ci: prepare PR 128 relay budget fixes
AlexanderWagnerDev Jul 15, 2026
2c17e5b
ci: run PR 128 relay budget fixes
AlexanderWagnerDev Jul 15, 2026
4390983
ci: make PR 128 relay budget run diagnosable
AlexanderWagnerDev Jul 15, 2026
9e99387
ci: apply PR 128 relay budget fix directly
AlexanderWagnerDev Jul 15, 2026
fb2132d
fix(server): make relay budget configurable and starvation-free
github-actions[bot] Jul 15, 2026
090f69c
ci: trigger validation for PR 128 relay budget fixes
AlexanderWagnerDev Jul 15, 2026
d0a1ec0
ci: remove PR 128 validation trigger
AlexanderWagnerDev Jul 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,27 @@ begin at `1.0.0`.

## [Unreleased]

## [0.4.0] — 2026-07-15

### 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
Expand All @@ -23,9 +43,41 @@ begin at `1.0.0`.
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.

## [0.3.1] — 2026-07-13

### Fixed
Expand Down Expand Up @@ -245,7 +297,8 @@ and others.
- Protocol mapping documents for legacy, E-RTMP v1, and E-RTMP v2
- `CONTRIBUTING.md` guidelines

[Unreleased]: https://github.com/OpenRTMP/librtmp2/compare/v0.3.1...HEAD
[Unreleased]: https://github.com/OpenRTMP/librtmp2/compare/v0.4.0...HEAD
[0.4.0]: https://github.com/OpenRTMP/librtmp2/compare/v0.3.1...v0.4.0
[0.3.1]: https://github.com/OpenRTMP/librtmp2/compare/v0.3.0...v0.3.1
[0.3.0]: https://github.com/OpenRTMP/librtmp2/compare/v0.2.1...v0.3.0
[0.2.1]: https://github.com/OpenRTMP/librtmp2/compare/v0.2.0...v0.2.1
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "librtmp2"
version = "0.3.1"
version = "0.4.0"
edition = "2024"
rust-version = "1.93"
authors = ["OpenRTMP <info@openrtmp.org>", "AlexanderWagnerDev <info@alexanderwagnerdev.com>"]
Expand Down
121 changes: 78 additions & 43 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,20 @@ A modern, open-source **Rust library** for Legacy RTMP and Enhanced RTMP v1/v2.

## Overview

`librtmp2` implements the lowest protocol layer of RTMP: handshake, chunking, AMF, commands, audio/video tags, Enhanced RTMP extensions, state machine, and a clean callback API.
`librtmp2` is a Rust RTMP protocol library: handshake, chunking, AMF commands, live publish/play relay, and an `extern "C"` FFI layer. It is meant to be embedded in custom servers, clients, and relay tools.

It is designed to be embedded into custom servers, clients, relay tools, OBS/FFmpeg integrations, or any future product that needs a solid RTMP foundation.

**What it is:**
- A complete Legacy RTMP implementation (handshake, chunk streams, message reassembly, commands, control messages)
- E-RTMP v1 support: ExVideoTagHeader, FourCC codecs (`hvc1`, `av01`, `vp09`), HDR metadata
- E-RTMP v2 support: capability negotiation (`capsEx`, `videoFourCcInfoMap`), reconnect, multitrack, ModEx
- An idiomatic Rust API plus an `extern "C"` FFI layer for use from C, Go, Python, PHP, and others
**What it is good at today:**
- Live ingest and relay for OBS/FFmpeg-style workflows (H.264/AAC and Enhanced-RTMP passthrough for HEVC/AV1)
- Minimal RTMP server and client (`connect` → `createStream` → `publish` / `play`)
- RTMPS (RTMP over TLS) via the optional `tls` Cargo feature (OpenSSL), enabled by default
- Parser/serializer modules for E-RTMP v1/v2 structures (usable from embedders; not all are wired into the session layer yet)

**What it is not:**
- Not an HTTP server or web UI
- Not a media server with business logic
- Not a push-relay to third-party platforms
- Not an FFmpeg wrapper
- Not a complete Adobe RTMP 1.0 implementation (no VOD commands, shared objects, encrypted handshake, etc.)
- Not a full E-RTMP v2 session stack (`capsEx` negotiation, reconnect, multitrack, ModEx are library code only today)
- Not an HTTP server, media policy layer, or FFmpeg wrapper

See [Implementation status](#implementation-status) for the code-accurate breakdown.

---

Expand All @@ -39,15 +37,14 @@ OBS / FFmpeg / App
librtmp2 ← this library
├── Handshake
├── Chunking
├── AMF (AMF0 / AMF3)
├── RTMP Commands
├── E-RTMP v1
└── E-RTMP v2
├── Handshake / Chunking / Control
├── AMF0 commands (connect, publish, play, …)
├── Session relay (publisher → players)
├── ertmp/ + flv/ parsers (library modules)
└── RTMPS transport (optional)
```

The library processes bytes, frames, commands, and protocol states. What a host program does with them is decided entirely via callbacks and configuration structures.
The live path is implemented in `session/conn.rs` and `server/mod.rs`. Parser modules under `ertmp/` and `flv/` can be used directly by embedders even when they are not yet called from the default session flow.

---

Expand All @@ -57,14 +54,15 @@ The library processes bytes, frames, commands, and protocol states. What a host
TCP_ACCEPTED
→ HANDSHAKE
→ CONNECTED
→ CAPS_NEGOTIATED (E-RTMP v2)
→ APP_CONNECTED
→ APP_CONNECTED ← connect today skips CAPS_NEGOTIATED
→ STREAM_CREATED
→ PUBLISHING | PLAYING
→ CLOSING
→ CLOSED
```

`CAPS_NEGOTIATED` exists in `ConnState` for a future E-RTMP v2 capability exchange but is not entered by the current session code.

---

## Build
Expand Down Expand Up @@ -168,16 +166,14 @@ librtmp2/
│ ├── buffer.rs Growable byte buffers
│ ├── bytes.rs Big-endian byte helpers
│ ├── chunk.rs Chunk reader/writer/state (per-csid)
│ ├── client.rs Outbound client: connect → publish/play
│ ├── ertmp.rs E-RTMP v1/v2 extensions
│ ├── flv.rs FLV audio/video/script tag parsing
│ ├── client/ Outbound client: connect → publish/play
│ ├── ertmp/ E-RTMP v1/v2 parsers (see Implementation status)
│ ├── flv/ FLV tag parsers (library; not used in live relay path)
│ ├── handshake.rs C0/C1/C2 ↔ S0/S1/S2
│ ├── log.rs Logging
│ ├── message.rs Message reassembly, control, commands
│ ├── net.rs Network helpers
│ ├── server.rs Listening socket, accept loop, per-connection poll
│ ├── session.rs Connection state machine, stream bookkeeping
│ ├── transport.rs TLS/plaintext transport abstraction
│ ├── message/ Message reassembly, control, commands
│ ├── server/ Listening socket, accept loop, relay
│ ├── session/ Connection state, publish/play handling
│ ├── transport.rs TLS/plaintext transport
│ └── types.rs Shared types
├── tests/
│ ├── interop/
Expand All @@ -189,19 +185,58 @@ librtmp2/

---

## Roadmap

| Feature | Status |
|---------|--------|
| Legacy RTMP server — minimal | Implemented |
| Legacy RTMP client — minimal | Implemented |
| E-RTMP v1 receive (HEVC/AV1 detection) | Implemented |
| E-RTMP v1 send | Implemented |
| E-RTMP v2 capability layer | Implemented |
| Multitrack / reconnect / ModEx | Implemented |
| RTMPS (TLS) | Implemented |
| End-to-end test suites | In progress |
| Performance benchmarks | Planned |
## Implementation status

Status reflects what is **wired into the live session path** (`conn.rs`, `server/mod.rs`, `client/mod.rs`), not merely what exists as parser code elsewhere in the repo.

### Legacy RTMP — live path

| Area | Status |
|------|--------|
| Standard handshake (C0–C2) | Done |
| Encrypted / Adobe-digest handshake | Not implemented |
| Chunking, control messages, ping | Done |
| Commands `connect`, `createStream`, `publish`, `play` | Done |
| Commands `pause`, `seek`, `receiveAudio`, `receiveVideo`, `closeStream` | Not implemented |
| `FCPublish` / `releaseStream` | Ignored (no-op) |
| `FCUnpublish` / `deleteStream` | Partial (publish route cleanup only) |
| Audio / video ingest and relay | Done |
| Aggregate messages | Done (unpack → relay) |
| Publisher `onMetaData` parsing (stats) | Done — not relayed to players |
| AMF3 shared objects | Not implemented |
| User Control `StreamBegin` / `StreamEOF` / `SetBufferLength` | Encode/decode helpers only — not sent or handled in session |
| One stream per connection (`current_stream`) | By design today |
| Init-frame cache for late joiners | Legacy H.264 (`0x17`) + AAC only |

### E-RTMP v1

| Area | Status |
|------|--------|
| Enhanced A/V passthrough (HEVC/AV1/Opus from FFmpeg/OBS) | Done (opaque byte relay) |
| `exvideo_parse` / `exaudio_parse` in session hot path | Not wired — codec detection uses lightweight heuristics |
| `fourCcList` in `connect` | Skipped on read; not sent on connect |
| HDR / `colorInfo` (`metadata.rs`) | Parser only |
| Enhanced sequence-start cache for players | Not implemented (see init-frame cache above) |
| `exvideo_write` / `exaudio_write` helpers | Not present — send raw enhanced payloads via `send_frame` |

### E-RTMP v2

| Area | Status |
|------|--------|
| `capsEx`, `videoFourCcInfoMap`, `reconnect`, `multitrack`, `modex` parse/write | Library code + unit tests |
| v2 capability negotiation in session | Not implemented |
| Reconnect / multitrack / ModEx in session | Not implemented |

### Client, TLS, tests

| Area | Status |
|------|--------|
| Minimal publish client | Done |
| Minimal play client (A/V receive callback) | Done — no metadata/aggregate on play |
| RTMPS | Done |
| Loopback + FFmpeg interop tests | Present (`tests/`, `tests/interop/`) |

Parser-level details and spec mappings: [`docs/protocol-mapping-legacy.md`](docs/protocol-mapping-legacy.md), [`docs/protocol-mapping-ertmp-v1.md`](docs/protocol-mapping-ertmp-v1.md), [`docs/protocol-mapping-ertmp-v2.md`](docs/protocol-mapping-ertmp-v2.md).

---

Expand Down
3 changes: 2 additions & 1 deletion benches/protocol.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use criterion::{Criterion, Throughput, black_box, criterion_group, criterion_main};
use librtmp2::buffer::Buffer;
use librtmp2::chunk::reader::{chunk_read, ChunkMessage};
use librtmp2::chunk::reader::{ChunkMessage, chunk_read};
use librtmp2::chunk::state::ChunkRegistry;
use librtmp2::chunk::writer::chunk_write;
use librtmp2::ertmp::fourcc;
Expand Down Expand Up @@ -67,6 +67,7 @@ fn bench_amf_connect(c: &mut Criterion) {
"FMLE/3.0",
3191,
252,
None,
)
.unwrap();
black_box(buf.write_pos())
Expand Down
11 changes: 4 additions & 7 deletions benches/relay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use std::time::{Duration, Instant};

use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use criterion::{BenchmarkId, Criterion, Throughput, black_box, criterion_group, criterion_main};
use librtmp2::client::Client;
use librtmp2::server::Server;
use librtmp2::types::*;
Expand Down Expand Up @@ -35,9 +35,7 @@ fn relay_n_frames(frame_count: u32) {
tls_insecure: 0,
})
.unwrap();
server
.listen(&format!("127.0.0.1:{RTMP_PORT}"))
.unwrap();
server.listen(&format!("127.0.0.1:{RTMP_PORT}")).unwrap();
server.on_publish_cb = Some(allow);
server.on_play_cb = Some(allow);

Expand Down Expand Up @@ -74,14 +72,13 @@ fn relay_n_frames(frame_count: u32) {
video_fourcc: FourCc::default(),
video_frame_type: 1,
is_metadata: 0,
track_id: u8::MAX,
};
publisher.send_frame(&frame).unwrap();
}

let deadline = Instant::now() + Duration::from_secs(10);
while RECEIVED.load(Ordering::SeqCst) < frame_count as usize
&& Instant::now() < deadline
{
while RECEIVED.load(Ordering::SeqCst) < frame_count as usize && Instant::now() < deadline {
player.poll(10).unwrap();
}
});
Expand Down
15 changes: 15 additions & 0 deletions debian/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
librtmp2 (0.4.0-1) UNRELEASED; urgency=medium

* Server-side E-RTMP v2 connect negotiation (fourCcList, capsEx bitmask,
videoFourCcInfoMap, reconnect) with capability-aware multitrack relay,
per-track callbacks, and init-cache replay for late-joining players.
* Enhanced init-frame classification for HEVC, AV1, Opus, AAC, and
CodedFramesX keyframes; onMetaData caching and timestamp-preserving relay.
* Client AMF0/AMF3 metadata and Aggregate receive callbacks, populated
codec/track fields, and legacy pause/seek/receiveAudio/receiveVideo/
closeStream command handling on the server session path.
* Correct E-RTMP packet-type constants and per-codec capability masks;
bound capability blobs, Aggregate sub-tags, and multitrack cache storage.

-- OpenRTMP <info@openrtmp.org> Wed, 15 Jul 2026 00:00:00 +0000

librtmp2 (0.3.1-1) UNRELEASED; urgency=medium

* Harden DNS resolution, RTMP ping keepalive, and publishing send-path
Expand Down
23 changes: 15 additions & 8 deletions examples/dump_frames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ static FRAME_COUNT: AtomicUsize = AtomicUsize::new(0);
static MAX_FRAMES: AtomicUsize = AtomicUsize::new(usize::MAX);

fn fourcc_str(fourcc: &FourCc) -> String {
let end = fourcc.cc.iter().position(|&b| b == 0).unwrap_or(fourcc.cc.len());
let end = fourcc
.cc
.iter()
.position(|&b| b == 0)
.unwrap_or(fourcc.cc.len());
String::from_utf8_lossy(&fourcc.cc[..end]).into_owned()
}

Expand Down Expand Up @@ -138,14 +142,17 @@ fn main() -> ExitCode {
eprintln!("[dump_frames] play failed");
return ExitCode::from(1);
}
eprintln!("[dump_frames] playing (timeout={timeout_s}s, max_frames={})", {
let cap = MAX_FRAMES.load(Ordering::SeqCst);
if cap == usize::MAX {
"unlimited".to_string()
} else {
cap.to_string()
eprintln!(
"[dump_frames] playing (timeout={timeout_s}s, max_frames={})",
{
let cap = MAX_FRAMES.load(Ordering::SeqCst);
if cap == usize::MAX {
"unlimited".to_string()
} else {
cap.to_string()
}
}
});
);

let deadline = Instant::now() + Duration::from_secs(timeout_s);
while Instant::now() < deadline {
Expand Down
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/chunk_read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use libfuzzer_sys::fuzz_target;
use librtmp2::buffer::Buffer;
use librtmp2::chunk::reader::{chunk_read, ChunkMessage};
use librtmp2::chunk::reader::{ChunkMessage, chunk_read};
use librtmp2::chunk::state::ChunkRegistry;

fuzz_target!(|data: &[u8]| {
Expand Down
2 changes: 1 addition & 1 deletion fuzz/fuzz_targets/handshake_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use libfuzzer_sys::fuzz_target;
use librtmp2::buffer::Buffer;
use librtmp2::handshake::{server_init, server_read_c0, server_read_c1, server_read_c2, Handshake};
use librtmp2::handshake::{Handshake, server_init, server_read_c0, server_read_c1, server_read_c2};

fuzz_target!(|data: &[u8]| {
let mut hs = Handshake::default();
Expand Down
Loading