lvqr_transcode/lib.rs
1//! Server-side transcoding for LVQR (Tier 4 item 4.6).
2//!
3//! Generates an ABR ladder (720p / 480p / 240p by default) from a
4//! single high-resolution source broadcast, with the output
5//! renditions re-injected into the caller-supplied
6//! [`lvqr_fragment::FragmentBroadcasterRegistry`] under
7//! `<source>/<rendition>` broadcast names. Every egress surface
8//! (LL-HLS, DASH, MoQ relay, archive) picks them up without
9//! per-protocol wiring; the LL-HLS master playlist composer emits
10//! one `#EXT-X-STREAM-INF` per rendition automatically.
11//!
12//! ## What this crate ships
13//!
14//! Always available (default features):
15//!
16//! * [`Transcoder`] trait + [`TranscoderFactory`] +
17//! [`TranscoderContext`] -- the "subscribe / drain / panic-isolate"
18//! shape generalised from `lvqr_agent::Agent`. Each factory
19//! carries its own [`RenditionSpec`].
20//! * [`TranscodeRunner`] + [`TranscodeRunnerHandle`] -- registry-side
21//! installer + cheaply-cloneable handle, mirroring
22//! `lvqr_agent::AgentRunner`.
23//! * [`RenditionSpec`] with width / height / bitrate fields,
24//! [`RenditionSpec::preset_720p`] / `preset_480p` / `preset_240p`
25//! constructors, and [`RenditionSpec::default_ladder`].
26//! * [`PassthroughTranscoder`] -- in-memory observer that proves
27//! the registry-callback / drain / panic-isolation wiring; useful
28//! for tests and as a metrics tap.
29//! * [`AudioPassthroughTranscoder`] -- copies `<source>/1.mp4` audio
30//! fragments verbatim into every rendition's audio track so each
31//! rendition broadcaster is a self-contained mp4 the LL-HLS
32//! bridge drains without special-casing the missing audio.
33//!
34//! Behind the `transcode` feature (pulls gstreamer-rs 0.23 +
35//! base/good/bad/ugly + gst-libav from the host):
36//!
37//! * [`SoftwareTranscoder`] / [`SoftwareTranscoderFactory`] -- the
38//! `appsrc -> qtdemux -> h264parse -> avdec_h264 -> videoscale ->
39//! x264enc -> ... -> mp4mux -> appsink` ladder, one worker thread
40//! per `(source, rendition)` pair, bounded mpsc.
41//! * [`AacToOpusEncoder`] / [`AacToOpusEncoderFactory`] -- AAC -> Opus
42//! transcoder used by `lvqr-whep` (under its `aac-opus` feature)
43//! so AAC publishers reach Opus-negotiated WHEP subscribers.
44//!
45//! Behind one of the `hw-*` features (each implies `transcode`):
46//!
47//! * `hw-videotoolbox` -- [`VideoToolboxTranscoder`] /
48//! [`VideoToolboxTranscoderFactory`] for macOS via Apple's
49//! `vtenc_h264_hw` (the `applemedia` plugin from gst-plugins-bad).
50//! * `hw-nvenc` -- [`NvencTranscoder`] / [`NvencTranscoderFactory`]
51//! for Linux + Nvidia GPUs via `nvh264enc` (the `nvcodec` plugin
52//! from gst-plugins-bad, driven by the CUDA runtime).
53//! * `hw-vaapi` -- [`VaapiTranscoder`] / [`VaapiTranscoderFactory`]
54//! for Linux + Intel iGPU / AMD via `vah264enc` (the modern `va`
55//! plugin from gst-plugins-bad, superseding the deprecated
56//! `vaapih264enc` from `gstreamer-vaapi`).
57//! * `hw-qsv` -- [`QsvTranscoder`] / [`QsvTranscoderFactory`] for
58//! Linux + Intel Quick Sync via `qsvh264enc` (the `qsv` plugin
59//! from gst-plugins-bad, driving Intel Media SDK / oneVPL).
60//!
61//! All four HW backends mirror `SoftwareTranscoderFactory` shape
62//! verbatim -- same `Transcoder` trait, same lifecycle, same
63//! `<source>/<rendition>` output broadcast naming, same bounded
64//! mpsc + dedicated worker thread per `(source, rendition)` pair --
65//! and only swap the GStreamer encoder element + property mapping.
66//! HW-only path is intentional across all four: a factory that
67//! silently falls back to CPU encoding under load defeats the point
68//! of an operator-pickable hardware tier. Each factory's
69//! `is_available()` probes the required encoder element at
70//! construction and `build()` opts out cleanly with a warn log when
71//! missing.
72//!
73//! Future sessions may extract the shared scaffolding into a
74//! dedicated `pipeline.rs` module (per the "three is the threshold
75//! for an abstraction" rule). The current shape is intentional code
76//! duplication: each backend stays readable on its own and the cost
77//! of cross-backend changes is small enough that the mechanical-
78//! sharing tradeoff is not yet a win.
79//!
80//! ## Where this crate fits in the consumer family
81//!
82//! Pattern-matches the existing
83//! [`lvqr_fragment::FragmentBroadcasterRegistry`] consumers:
84//!
85//! | Crate | Wires | Purpose |
86//! |-------|-------|---------|
87//! | `lvqr_cli::hls::BroadcasterHlsBridge` | `on_entry_created` | LL-HLS playlist composition |
88//! | `lvqr_cli::archive::BroadcasterArchiveIndexer` | `on_entry_created` | DVR archive index + on-disk segments |
89//! | `lvqr_wasm::install_wasm_filter_bridge` | `on_entry_created` | Per-fragment WASM filter tap |
90//! | `lvqr_cli::cluster_claim::install_cluster_claim_bridge` | `on_entry_created` | Renew cluster broadcast claim |
91//! | `lvqr_agent::AgentRunner` | `on_entry_created` | Per-broadcast user-defined agents |
92//! | `lvqr_transcode::TranscodeRunner` | `on_entry_created` | Per-broadcast ABR-ladder transcoders |
93//!
94//! ## Operator wiring
95//!
96//! `lvqr-cli` exposes `--transcode-rendition 720p,480p,240p` (or a
97//! `.toml` `RenditionSpec` path) and, on `hw-videotoolbox` builds,
98//! `--transcode-encoder software|videotoolbox`. End-to-end shape:
99//! ingest one source RTMP stream, the LL-HLS master playlist
100//! advertises one variant per rendition + the source.
101
102mod audio_passthrough;
103mod passthrough;
104mod rendition;
105mod runner;
106mod transcoder;
107
108#[cfg(feature = "transcode")]
109mod aac_opus;
110#[cfg(feature = "transcode")]
111mod software;
112#[cfg(feature = "transcode")]
113pub mod test_support;
114
115#[cfg(feature = "hw-videotoolbox")]
116mod videotoolbox;
117
118#[cfg(feature = "hw-nvenc")]
119mod nvenc;
120
121#[cfg(feature = "hw-vaapi")]
122mod vaapi;
123
124#[cfg(feature = "hw-qsv")]
125mod qsv;
126
127pub use audio_passthrough::{AudioPassthroughTranscoder, AudioPassthroughTranscoderFactory};
128pub use passthrough::{PassthroughTranscoder, PassthroughTranscoderFactory};
129pub use rendition::RenditionSpec;
130pub use runner::{TranscodeRunner, TranscodeRunnerHandle, TranscoderStats};
131pub use transcoder::{Transcoder, TranscoderContext, TranscoderFactory};
132
133#[cfg(feature = "transcode")]
134pub use aac_opus::{AacAudioConfig, AacToOpusEncoder, AacToOpusEncoderFactory, OpusFrame};
135#[cfg(feature = "transcode")]
136pub use software::{SoftwareTranscoder, SoftwareTranscoderFactory};
137
138#[cfg(feature = "hw-videotoolbox")]
139pub use videotoolbox::{VideoToolboxTranscoder, VideoToolboxTranscoderFactory};
140
141#[cfg(feature = "hw-nvenc")]
142pub use nvenc::{NvencTranscoder, NvencTranscoderFactory};
143
144#[cfg(feature = "hw-vaapi")]
145pub use vaapi::{VaapiTranscoder, VaapiTranscoderFactory};
146
147#[cfg(feature = "hw-qsv")]
148pub use qsv::{QsvTranscoder, QsvTranscoderFactory};