multimux 0.10.0

Multi-input (RTSP/RTP/TS-UDP/TS-HTTP/SRT/HLS-pull/DASH-pull/Smooth-pull/RTMP/file), multi-output (LL-HLS/DASH/LL-DASH/Smooth/TS-HLS/catch-up + SRT/RTMP/RTSP push) just-in-time repackaging HTTP origin (library: tokio + axum), with shared output auth and an external scheme plugin registry.
Documentation
[package]
name         = "multimux"
version      = "0.10.0"
edition      = "2024"
rust-version.workspace = true
license      = "MIT OR Apache-2.0"
description  = "Multi-input (RTSP/RTP/TS-UDP/TS-HTTP/SRT/HLS-pull/DASH-pull/Smooth-pull/RTMP/file), multi-output (LL-HLS/DASH/LL-DASH/Smooth/TS-HLS/catch-up + SRT/RTMP/RTSP push) just-in-time repackaging HTTP origin (library: tokio + axum), with shared output auth and an external scheme plugin registry."
repository   = "https://github.com/fishloa/rust-broadcast"
keywords     = ["rtsp", "hls", "ll-hls", "cmaf", "origin"]
categories   = ["multimedia", "network-programming"]
readme       = "README.md"

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[dependencies]
# Floor is 0.23.1, not 0.23: `push::rtmp` uses
# `flv_sequence_header_payloads` / `flv_frame_payloads`, which do not
# exist in 0.23.0 (verified at the tag). Published multimux 0.8.0 shipped
# with the understated `"0.23"` bound.
transmux          = { path = "../transmux",     version = "0.24" }
container-probe   = { path = "../container-probe", version = "0.1" }
# HLS (M3U8) playlist syntax (issue #878): `source::hls_pull` builds/parses
# playlists via this crate directly rather than through `transmux`.
broadcast-hls     = { path = "../broadcast-hls", version = "0.2" }
rtsp-runtime      = { path = "../rtsp-runtime",  version = "0.6", features = ["tokio"] }
# RTMP push ingest (`source::rtmp`, issue #738): the `tokio` feature pulls
# `rtmp_runtime::io::AsyncRtmpServer`/`RtmpConnection`, the real-socket
# adapter over the sans-IO `ServerSession`.
rtmp-runtime      = { path = "../rtmp-runtime",  version = "0.6", features = ["tokio"] }
# SRT push/pull ingest (`source::srt`, issue #739): the `tokio` feature
# pulls `srt_runtime::io::{SrtListener, SrtSocket}`, the real-socket adapter
# over the sans-IO ARQ/TSBPD/handshake engines.
srt-runtime       = { path = "../srt-runtime",   version = "0.4", features = ["tokio"] }
# The LL-HLS origin engine itself (rolling window/store, blocking-reload +
# part-availability decision logic, playlist rendering — issue #663/#717
# Stage 2). multimux is now a thin tokio+axum adapter over
# `hls_runtime::server`; see `output::llhls`. The `tokio` feature also
# pulls in `hls_runtime::client::tokio_client::TokioClient` — the
# HLS-pull input (`source::hls_pull`, issue #663 P3c) wraps it rather than
# re-implementing LL-HLS playback.
# Floor is 0.6, not 0.5: `catchup.rs` uses `server::ClosedSegment` and
# `HlsOrigin::closed_segments()`, which do not exist in the published
# 0.5.0 (verified: 0 hits at the hls-runtime-v0.5.0 tag). hls-runtime
# must publish 0.6.0 before multimux can publish.
hls-runtime       = { path = "../hls-runtime",   version = "0.6", features = ["std", "tokio"] }
# Needed directly (not just via hls-runtime/transmux) by `source::hls_pull`
# to recover `TrackSpec`s from a pulled stream's init segment via
# `Fmp4Demux::unpackage` (the `Unpackage` trait) — issue #663 P3c.
# API floor is 9.2, not 9: `output::smooth` uses `hex::hex_encode`, added in
# 9.2.0 (absent at the 9.0.0 and 9.1.0 tags — verified). Published
# multimux 0.8.0 shipped with the understated `"9"` bound, so a lock
# resolving 9.0/9.1 fails to compile it today. The pin below is 9.3, above
# that floor, tracking the current published line; same caret bucket, so it
# is not an epoch change.
broadcast-common  = { path = "../broadcast-common", version = "9.3" }
# The media-plane ingress traits this crate's sources port onto (plan step
# 5a): `Dialer`/`Listener`/`IngestSession`/`IngestDriver`/`ListenDriver`, and
# the `Trunk` every ported session publishes into.
media-plane       = { path = "../media-plane", version = "0.4", features = ["std"] }
bytes             = "1"
# Shared Basic/Digest/Bearer auth model + challenge/response helper
# (`source::http_auth`, issue #663 P3b/P3c) — the same crate `rtsp-runtime`
# and `hls_runtime::client::tokio_client` use.
# Floor is 0.2.1, not 0.2: `config.rs` uses `Verifier::signed_url` /
# `SignedUrlKeySet`, added in 0.2.1 (#747). A `"0.2"` bound admits 0.2.0,
# which lacks them — multimux 0.8.0 shipped with that understated bound and
# fails to compile against a lock that pins 0.2.0 (hit for real while
# bumping acap-multimux).
broadcast-auth    = { path = "../broadcast-auth", version = "0.3" }
sdp-types         = "0.1"
tokio             = { version = "1", features = ["rt-multi-thread", "net", "io-util", "macros", "time", "sync", "signal"] }
axum              = "0.7"
# HTTP-layer resource limits (issue #663 P5, audit-concurrency #3): request
# timeout + request-body size cap (`tower-http`) and a concurrency bound
# (`tower::limit`) — see `origin::HttpLimits`/`origin::router`. `util` is
# needed for `tower::ServiceExt::oneshot` in this crate's own axum-router
# tests.
tower-http        = { version = "0.5", default-features = false, features = ["timeout", "limit"] }
tower             = { version = "0.5", default-features = false, features = ["limit", "util"] }
serde             = { version = "1", features = ["derive"] }
serde_json        = "1"
thiserror         = "2"
url               = "2"
percent-encoding  = "2"
tracing           = "0.1"
# Push output driver (`push`, issue #744): `#[async_trait]` drives the
# `PushTransport` trait's async methods over the sans-IO `SrtSocket` handle.
async-trait       = "0.1"
# `tokio_util::sync::CancellationToken` for cooperative cancellation of the
# push loop (`drive_push`).
tokio-util        = { version = "0.7", features = ["rt"] }
metrics           = "0.24"
metrics-exporter-prometheus = { version = "0.18", default-features = false }
tokio-rustls      = { version = "0.26", optional = true }
# `rustls::pki_types::ServerName` for SNI, plus the loopback TLS test server
# (`source::rtsp`'s `rtsps://` connect, issue #804) — pinned to the same
# "0.23" line as `rtsp-runtime`'s own optional `rustls` dep, already resolved
# at that version transitively (via `reqwest`/`hyper-rustls`), so this adds no
# new version to the lock.
rustls            = { version = "0.23", optional = true }
# TS-over-HTTP ingest (`source::ts_http`, issue #663 P3c): a streaming GET
# fed straight into `StreamingTsDemux`. `rustls-tls` for consistency with the
# rest of the workspace's TLS backend (rustls/aws-lc-rs); `stream` for
# `Response::bytes_stream()`.
reqwest           = { version = "0.12", default-features = false, features = ["rustls-tls", "stream"] }
# `.next()` on `reqwest::Response::bytes_stream()` (a bare `Stream`, no
# extension methods of its own).
futures-util      = { version = "0.3", default-features = false, features = ["std"] }
# WHIP input (`source::whip`, issue #740) / WHEP output (`output::whep`,
# issue #743), `whip`/`whep` features only: the ICE + DTLS-SRTP media
# transport this crate has no reason to reimplement.
# Declared as a *plain* optional dep (no extra features) so merely depending
# on it (feature off) never pulls `webrtc-runtime`'s own `media` feature —
# only `multimux`'s `whip`/`whep` features below turn that on. See this
# crate's `rust-version` note on those features for why the split matters.
webrtc-runtime    = { path = "../webrtc-runtime", version = "0.1", optional = true, default-features = false }
# WHEP output (`output::whep`, issue #743) only: builds the typed
# `rtp_packet::RtpPacket` values `webrtc_runtime::media::MediaTransport::
# encrypt_rtp` requires — see that method's own signature. Unlike
# `webrtc-runtime`, this crate has no `rtc-dtls` transitive dependency; it
# is kept behind the `whep` feature (rather than unconditional) purely so it
# is never pulled in by a build that never touches WHEP.
rtp-packet        = { path = "../rtp-packet", version = "0.4", optional = true, default-features = false }
# DVB SI EIT present/following (issue #903, `dvr`): `dvb_si::demux::SiDemux`
# reassembles EIT p/f sections from raw TS packets so `DvrRecorder` can roll
# the archive period on the programme boundary rather than only on the
# clock. `default-features = false` — only `std` (this crate always links
# std) + `ts` (the section reassembler `SiDemux` needs); `chrono`/`serde`
# are not needed since `dvr::EitProgramme` decodes MJD+BCD itself.
dvb-si            = { path = "../dvb-si", version = "10", default-features = false, features = ["std", "ts"] }
# TS packet/PSI-section framing. Two uses: `mpeg_ts::pid::Pid`, which
# `dvr::DvrRecorder::new` wraps the EIT PID (`dvb_si::tables::eit::PID`) in
# to pass to `SiDemux::builder().pid(..)`; and
# `mpeg_ts::mux::SectionPacketiser`, which `dvr`'s own tests use to wrap the
# genuine-bytes transition section back into TS packets. Also still used by
# `source::segment`'s MPTS test (issue #887). Promoted from
# `[dev-dependencies]` because production code (`dvr.rs`) now needs it too.
mpeg-ts           = { path = "../mpeg-ts", version = "0.4" }

[features]
# `tls` is default-on: `rtsps://` sources should work out of the box. It pulls
# rtsp-runtime's `tls` feature (rustls/webpki-roots via its
# `default_tls_client_config` helper) plus `tokio-rustls` directly, needed here
# only to name the connected stream's concrete type
# (`tokio_rustls::client::TlsStream<TcpStream>`).
default     = ["tls"]
tls         = ["rtsp-runtime/tls", "dep:tokio-rustls", "dep:rustls"]
# WHIP (RFC 9725) push input (issue #740): accepts an inbound WHIP publisher
# over HTTP + ICE/DTLS-SRTP, via `webrtc_runtime::media::MediaTransport`.
#
# Opt-in rather than `default` because it pulls the whole ICE/DTLS-SRTP
# dependency tree (`rtc-ice`/`rtc-dtls`/`rtc-srtp`/`rcgen`), which a
# deployment serving only HTTP outputs has no reason to build.
#
# This used to carry an MSRV constraint too — `rcgen ^0.14.8` needs rustc
# >= 1.88, while the workspace MSRV was 1.86 — which is why the feature was
# excluded from the `--all-features` lanes and covered by a dedicated CI job.
# The workspace MSRV is now 1.95.0 (issue #949), so that gap is gone and the
# feature is opt-in purely on dependency weight.
whip        = ["dep:webrtc-runtime", "webrtc-runtime/media"]
# WHEP (draft-ietf-wish-whep) egress (issue #743): accepts a viewer's HTTP
# `POST` + SDP offer, negotiates ICE/DTLS-SRTP, and pushes the route's
# `Trunk` samples out as SRTP RTP — the mirror image of `whip` (see
# `crate::output::whep`'s module doc). A *sibling* feature to `whip` rather
# than folded into it: ingest and egress are independently useful (a route
# may want only one), matching this crate's one-feature-per-protocol
# pattern elsewhere (`InputSpec::Whip` vs. a hypothetical output-side
# equivalent). Both enable `webrtc-runtime/media`, so both pull the same
# ICE/DTLS-SRTP dependency tree described on `whip` above.
whep        = ["dep:webrtc-runtime", "webrtc-runtime/media", "dep:rtp-packet"]

[[example]]
name = "serve_rtsp"

[[example]]
name = "custom_scheme"

[dev-dependencies]
base64           = "0.22"
# Independent RFC 7616 Digest response verification in the mock auth server
# (`testutil`, issue #663 "Finish client-side multi-scheme auth") — the
# `http-auth` crate (this workspace's Digest *client*) has no server-side
# counterpart to reuse, and this is already a transitive dependency of
# `http-auth` at the same version, so this adds no new version to the lock.
md-5             = "0.10"
# Adds `reqwest`'s `json` feature (`Response::json`/`RequestBuilder::json`)
# for test-only use (`tests/admin_api.rs`, issue #749) — Cargo unifies this
# into the one `reqwest` dependency only for test builds, never for the
# library target itself.
reqwest          = { version = "0.12", default-features = false, features = ["json"] }