1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! Probes for what the linked FFmpeg build contains.
//!
//! FFmpeg builds differ widely in which muxers and protocols are compiled in
//! (e.g. the `whip` muxer requires FFmpeg 8 with a DTLS backend, the `srt`
//! protocol requires `--enable-libsrt`). These helpers let applications check
//! for a component up front and fail with an actionable error instead of a
//! mid-pipeline failure:
//!
//! ```rust,no_run
//! use ez_ffmpeg::capabilities;
//!
//! // Muxers (output formats) and I/O protocols are separate namespaces.
//! let has_whip_muxer = capabilities::is_muxer_available("whip");
//! let has_srt_protocol = capabilities::is_output_protocol_available("srt");
//! ```
//!
//! A `true` result only means the component is compiled into the linked
//! FFmpeg; encoders, TLS backends, endpoint compatibility, and network
//! reachability are separate concerns.
//!
//! The rest of this page documents the two streaming outputs these probes
//! are most often used for: WHIP and SRT.
//!
//! # WHIP output (experimental, FFmpeg 8+)
//!
//! **Status:** FFmpeg 8 ships an upstream `whip` muxer that publishes WebRTC
//! streams to WHIP endpoints (Twitch/IVS, Cloudflare Stream, LiveKit,
//! MediaMTX, ...). The muxer is marked **experimental** upstream and has a
//! known upstream FIXME on Opus timestamp handling, and ez-ffmpeg's own CI
//! cannot exercise it (its FFmpeg builds carry no DTLS backend) — treat this
//! section as status and instructions, not as a verified recipe.
//!
//! Requirements, all imposed by the upstream muxer:
//!
//! - **FFmpeg 8.0 or newer, built with a DTLS-capable TLS backend.** FFmpeg
//! 8.0 supports OpenSSL or Schannel for DTLS; FFmpeg 8.1 accepts any of
//! OpenSSL, GnuTLS, Schannel, or mbedTLS. Without one of these, the `whip`
//! muxer is not compiled in — `is_muxer_available("whip")` returns
//! `false`.
//! - **Video: H.264 with B-frames disabled.** The muxer rejects B-frames
//! (real-time WebRTC playout does not reorder frames) and needs the H.264
//! profile/level present in global headers — ez-ffmpeg raises the
//! encoder's global-header flag automatically whenever a muxer requires
//! it, so that part needs no configuration. The muxer writes whatever
//! profile you encode into the SDP; **Baseline/Constrained Baseline is the
//! conservative interoperability choice** for WebRTC playout (the example
//! pins it), not a muxer-enforced restriction.
//! - **Audio: Opus at 48 kHz stereo** — the muxer enforces this combination.
//!
//! Discover the encoders your build offers with
//! [`get_encoders`](crate::codec::get_encoders). H.264 encoders are commonly
//! `libx264` (GPL — mind your licensing), `libopenh264`, or hardware
//! encoders such as `h264_nvenc` / `h264_videotoolbox`; Opus is commonly
//! `libopus`.
//!
//! ```rust,no_run
//! use ez_ffmpeg::{capabilities, FfmpegContext, FfmpegScheduler, Input, Output};
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! if !capabilities::is_muxer_available("whip") {
//! return Err("this FFmpeg build has no 'whip' muxer \
//! (WHIP needs FFmpeg >= 8.0 with a DTLS backend)"
//! .into());
//! }
//!
//! // File inputs must be paced to real time for live publishing.
//! let input = Input::from("input.mp4").set_readrate(1.0);
//!
//! let output = Output::from("https://example.com/whip/endpoint")
//! .set_format("whip") // required: never auto-guessed from the URL
//! .set_format_opt("authorization", "<token>") // raw token; FFmpeg itself adds "Bearer "
//! .set_video_codec("libx264") // pick from codec::get_encoders()
//! .set_video_codec_opt("profile", "baseline") // conservative WebRTC interop choice
//! .set_video_codec_opt("bf", "0") // WHIP: no B-frames
//! .set_audio_codec("libopus")
//! .set_audio_sample_rate(48000) // the muxer requires 48 kHz stereo Opus
//! .set_audio_channels(2);
//!
//! FfmpegScheduler::new(FfmpegContext::builder().input(input).output(output).build()?)
//! .start()?
//! .wait()?;
//! Ok(())
//! }
//! ```
//!
//! # SRT output
//!
//! SRT output needs two independent components in the linked FFmpeg build:
//! the `srt` **protocol** (built with `--enable-libsrt`) for transport, and
//! a container muxer for the payload — MPEG-TS below. Probe both:
//!
//! ```rust,no_run
//! use ez_ffmpeg::capabilities;
//!
//! let srt_ready = capabilities::is_output_protocol_available("srt")
//! && capabilities::is_muxer_available("mpegts");
//! ```
//!
//! > **Never test SRT streaming support with `is_muxer_available("srt")`.**
//! > That name matches the SubRip **subtitle** muxer, which exists in
//! > practically every FFmpeg build, so the probe returns `true` whether or
//! > not the SRT transport is present — it tells you nothing about SRT
//! > streaming support.
//!
//! ```rust,no_run
//! use ez_ffmpeg::{capabilities, FfmpegContext, FfmpegScheduler, Output};
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! if !(capabilities::is_output_protocol_available("srt")
//! && capabilities::is_muxer_available("mpegts"))
//! {
//! return Err("this FFmpeg build lacks the srt protocol \
//! (--enable-libsrt) or the mpegts muxer"
//! .into());
//! }
//!
//! let output = Output::from(
//! // ALL protocol options live in the URL query; latency is in MICROSECONDS.
//! "srt://127.0.0.1:9000?mode=caller&transtype=live&latency=120000&payload_size=1316",
//! )
//! .set_format("mpegts");
//!
//! FfmpegScheduler::new(FfmpegContext::builder().input("input.mp4").output(output).build()?)
//! .start()?
//! .wait()?;
//! Ok(())
//! }
//! ```
//!
//! Three warnings worth reading twice:
//!
//! - **Protocol options go in the URL query — only.** On the output side,
//! ez-ffmpeg opens the network connection without an options dictionary,
//! so [`Output::set_format_opt`](crate::Output::set_format_opt) feeds the
//! MPEG-TS **muxer**, never the SRT protocol. A `passphrase` set that way
//! only draws an "option not recognized" warning from the muxer and never
//! reaches the transport, so the stream goes out **unencrypted** with no
//! hard error. Encryption parameters (`passphrase`, `pbkeylen`) must go in
//! the URL query; percent-encode the passphrase if it contains characters
//! reserved in URLs.
//! - **`latency` is in microseconds, not milliseconds.** `latency=120000`
//! means 120 ms. libsrt truncates the value to whole milliseconds by
//! integer division, so a value of `120` collapses to 0 ms — an unusable
//! budget.
//! - **Redact stream URLs in logs.** With SRT, credentials (`passphrase`)
//! are part of the URL itself, so any log line printing the URL leaks
//! them.
//!
//! On the **input** side, `srt://` is a live network protocol with no seek
//! support, and — unlike the output side —
//! [`Input::set_format_opt`](crate::Input::set_format_opt) options do reach
//! the `avformat_open_input` call.
use ;
use ;
use ;
/// Returns whether the linked FFmpeg build contains a muxer (output format)
/// with this short name.
///
/// A `true` result only means the muxer is registered in the linked FFmpeg
/// build; it does not guarantee that the encoders, TLS/DTLS backends, or
/// network endpoints the format needs at runtime are also available. `name`
/// is the muxer short name (e.g. `"matroska"`, `"mpegts"`, `"whip"`), not a
/// file name — no file-extension guessing is applied. Names containing an
/// interior NUL byte return `false`.
///
/// Note: muxer names and protocol names are separate namespaces. The `srt`
/// *muxer* is the SubRip subtitle format, unrelated to the SRT streaming
/// protocol — use [`is_output_protocol_available`] for protocols.
///
/// ```rust,ignore
/// assert!(ez_ffmpeg::capabilities::is_muxer_available("matroska"));
/// let has_whip = ez_ffmpeg::capabilities::is_muxer_available("whip");
/// ```
/// Returns whether the linked FFmpeg build contains an I/O protocol with this
/// name that supports **output** (writing).
///
/// A `true` result only means the protocol is registered for output in the
/// linked FFmpeg build; it does not guarantee that the TLS backends, remote
/// endpoints, or network paths a stream needs at runtime are also available.
/// `name` is the protocol name as it appears before `://` in a URL (e.g.
/// `"file"`, `"srt"`, `"rtmp"`), not a URL or file name.
///
/// The probe is direction-aware: input-only protocols are not matched.
///
/// ```rust,ignore
/// assert!(ez_ffmpeg::capabilities::is_output_protocol_available("file"));
/// let has_srt = ez_ffmpeg::capabilities::is_output_protocol_available("srt");
/// ```