use str0m::{
Candidate,
change::SdpAnswer,
format::Codec,
media::{Direction, MediaKind},
};
use url::Url;
use crate::{Error, Result, client::Client, egress::EgressSource, session};
pub(crate) async fn dial(
client: &Client,
url: Url,
origin: moq_net::origin::Consumer,
path: impl moq_net::AsPath,
) -> Result<()> {
let source = EgressSource::new(moq_mux::Source::new(origin, path)).await?;
let codecs = source.catalog_codecs();
if codecs.is_empty() {
return Err(Error::NoRenditions);
}
let (socket, candidates) = session::bind_udp(&client.config().ice_candidates).await?;
let mut rtc = session::rtc_with_codecs(&codecs);
for addr in &candidates {
let cand = Candidate::host(*addr, "udp").map_err(Error::rtc)?;
rtc.add_local_candidate(cand);
}
let mut api = rtc.sdp_api();
let mut mids = Vec::new();
if codecs.iter().any(|codec| matches!(codec, Codec::Opus)) {
mids.push(api.add_media(MediaKind::Audio, Direction::SendOnly, None, None, None));
}
if codecs
.iter()
.any(|codec| matches!(codec, Codec::H264 | Codec::H265 | Codec::Vp8 | Codec::Vp9 | Codec::Av1))
{
mids.push(api.add_media(MediaKind::Video, Direction::SendOnly, None, None, None));
}
let (offer, pending) = api.apply().ok_or(Error::NoSdpChanges)?;
let res = client
.http()
.post(url.clone())
.header(reqwest::header::CONTENT_TYPE, "application/sdp")
.header(reqwest::header::ACCEPT, "application/sdp")
.body(offer.to_sdp_string())
.send()
.await?;
if !res.status().is_success() {
return Err(Error::HttpStatus(res.status().as_u16()));
}
let body = res.text().await?;
let answer = SdpAnswer::from_sdp_string(&body).map_err(|err| Error::InvalidSdp(err.to_string()))?;
rtc.sdp_api().accept_answer(pending, answer).map_err(Error::rtc)?;
tracing::info!(%url, "whip client connected");
let inbound = session::spawn_socket_reader(socket.clone());
let mut session = session::Session::egress(rtc, socket, candidates, inbound, source);
for mid in mids {
session.open_media(mid)?;
}
tokio::spawn(async move {
let result = session.run().await;
session::log_session_end("whip client", &result);
});
Ok(())
}