Skip to main content

moq_rtc/client/
mod.rs

1//! HTTP-client side: dial a remote WHIP/WHEP endpoint over an SDP exchange.
2//!
3//! Counterpart to [`crate::server`]. Whereas the server accepts POSTed
4//! offers, the client mints the offer with `str0m::Rtc::sdp_api` and POSTs
5//! it to the remote URL. Once the answer arrives the same internal session
6//! driver takes over, so the per-codec bridges and UDP socket loop are shared.
7
8mod whep;
9mod whip;
10
11use std::net::SocketAddr;
12use std::time::Duration;
13
14use url::Url;
15
16/// Configuration shared by both `client publish` and `client subscribe`.
17#[derive(Clone, Debug, Default)]
18#[non_exhaustive]
19pub struct Config {
20	/// Public UDP socket addresses to advertise as ICE host candidates in
21	/// our outbound offer. Same semantics as [`crate::server::Config::ice_candidates`].
22	pub ice_candidates: Vec<SocketAddr>,
23
24	/// How long relays keep a non-latest group of an ingested media track fetchable.
25	/// Same semantics as [`crate::server::Config::max_age`].
26	///
27	/// Ingest only ([`subscribe`](Client::subscribe) / WHEP): a WHIP
28	/// [`publish`](Client::publish) reads a broadcast someone else declared, so it
29	/// ignores this.
30	pub max_age: Option<Duration>,
31
32	/// Connection allocator each ingested track claims its peak-hold bitrate on.
33	/// Ingest only ([`subscribe`](Client::subscribe) / WHEP); a WHIP
34	/// [`publish`](Client::publish) ignores this.
35	pub bandwidth: moq_net::bandwidth::Allocator,
36}
37
38/// Outbound WHIP/WHEP dialer.
39///
40/// Owns a [`reqwest::Client`] reused across calls so connection pooling and
41/// rustls config survive between resources.
42#[derive(Clone)]
43pub struct Client {
44	config: Config,
45	http: reqwest::Client,
46}
47
48impl Client {
49	/// Build a dialer from the shared client [`Config`]. The underlying
50	/// [`reqwest::Client`] (with its connection pool and rustls config) is created
51	/// once here and reused across every [`subscribe`](Self::subscribe) /
52	/// [`publish`](Self::publish) call.
53	pub fn new(config: Config) -> Self {
54		Self {
55			config,
56			http: reqwest::Client::new(),
57		}
58	}
59
60	pub(crate) fn config(&self) -> &Config {
61		&self.config
62	}
63
64	pub(crate) fn http(&self) -> &reqwest::Client {
65		&self.http
66	}
67
68	/// `client subscribe`: pull a remote WHEP feed and publish it as
69	/// `broadcast` on the local origin. Returns once the session is
70	/// running in the background.
71	pub async fn subscribe(&self, url: Url, broadcast: moq_net::broadcast::Producer) -> crate::Result<()> {
72		whep::dial(self, url, broadcast).await
73	}
74
75	/// `client publish`: pull the broadcast at `path` from `origin` and push it to a
76	/// remote WHIP endpoint. Gated on the per-codec re-packetizer.
77	///
78	/// Taking the origin plus path (rather than a resolved [`moq_net::broadcast::Consumer`])
79	/// lets the egress resolve a rendition whose catalog `broadcast` field references a
80	/// sibling broadcast, against the same origin.
81	pub async fn publish(
82		&self,
83		url: Url,
84		origin: moq_net::origin::Consumer,
85		path: impl moq_net::AsPath,
86	) -> crate::Result<()> {
87		whip::dial(self, url, origin, path).await
88	}
89}