mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-12-23 10:30:40 +00:00
webrtchttp: whipsink: Miscellaneous clean up
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/949>
This commit is contained in:
parent
b427cb6a3d
commit
b992596236
1 changed files with 68 additions and 110 deletions
|
@ -7,7 +7,7 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use crate::utils::{build_reqwest_client, parse_redirect_location, wait};
|
use crate::utils::{build_reqwest_client, parse_redirect_location, set_ice_servers, wait};
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use gst::glib;
|
use gst::glib;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
|
@ -16,7 +16,6 @@ use gst::ErrorMessage;
|
||||||
use gst_sdp::*;
|
use gst_sdp::*;
|
||||||
use gst_webrtc::*;
|
use gst_webrtc::*;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use parse_link_header;
|
|
||||||
use reqwest::header::HeaderMap;
|
use reqwest::header::HeaderMap;
|
||||||
use reqwest::header::HeaderValue;
|
use reqwest::header::HeaderValue;
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
|
@ -135,7 +134,36 @@ impl ElementImpl for WhipSink {
|
||||||
&self,
|
&self,
|
||||||
transition: gst::StateChange,
|
transition: gst::StateChange,
|
||||||
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
||||||
let ret = self.parent_change_state(transition);
|
if transition == gst::StateChange::NullToReady {
|
||||||
|
/*
|
||||||
|
* Fail the state change if WHIP endpoint has not been set by the
|
||||||
|
* time ReadyToPaused transition happens. This prevents us from
|
||||||
|
* having to check this everywhere else.
|
||||||
|
*/
|
||||||
|
let settings = self.settings.lock().unwrap();
|
||||||
|
|
||||||
|
if settings.whip_endpoint.is_none() {
|
||||||
|
gst::error!(CAT, imp: self, "WHIP endpoint URL must be set");
|
||||||
|
return Err(gst::StateChangeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if we have a valid URL. We can be assured any further URL
|
||||||
|
* handling won't fail due to invalid URLs.
|
||||||
|
*/
|
||||||
|
if let Err(e) = reqwest::Url::parse(settings.whip_endpoint.as_ref().unwrap().as_str()) {
|
||||||
|
gst::error!(
|
||||||
|
CAT,
|
||||||
|
imp: self,
|
||||||
|
"WHIP endpoint URL could not be parsed: {}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(gst::StateChangeError);
|
||||||
|
}
|
||||||
|
drop(settings);
|
||||||
|
}
|
||||||
|
|
||||||
if transition == gst::StateChange::PausedToReady {
|
if transition == gst::StateChange::PausedToReady {
|
||||||
// Interrupt requests in progress, if any
|
// Interrupt requests in progress, if any
|
||||||
if let Some(canceller) = &*self.canceller.lock().unwrap() {
|
if let Some(canceller) = &*self.canceller.lock().unwrap() {
|
||||||
|
@ -150,6 +178,8 @@ impl ElementImpl for WhipSink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ret = self.parent_change_state(transition);
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -389,16 +419,11 @@ impl WhipSink {
|
||||||
.headers(headermap)
|
.headers(headermap)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
let resp = match wait(&self.canceller, future) {
|
let resp = wait(&self.canceller, future)?;
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match resp.status() {
|
match resp.status() {
|
||||||
StatusCode::NO_CONTENT => {
|
StatusCode::NO_CONTENT => {
|
||||||
self.set_ice_servers(resp.headers());
|
set_ice_servers(&self.webrtcbin, resp.headers())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
status if status.is_redirection() => {
|
status if status.is_redirection() => {
|
||||||
|
@ -439,93 +464,6 @@ impl WhipSink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_ice_servers(&self, headermap: &HeaderMap) {
|
|
||||||
for link in headermap.get_all("link").iter() {
|
|
||||||
let link = link
|
|
||||||
.to_str()
|
|
||||||
.expect("Header value should contain only visible ASCII strings");
|
|
||||||
// FIXME: The Demo WHIP Server appends an extra ; at the end of each link
|
|
||||||
// but not needed as per https://datatracker.ietf.org/doc/html/rfc8288#section-3.5
|
|
||||||
let link = link.trim_matches(';');
|
|
||||||
|
|
||||||
let item_map = parse_link_header::parse_with_rel(link);
|
|
||||||
if let Err(e) = item_map {
|
|
||||||
gst::error!(CAT, imp: self, "set_ice_servers {} - Error {:?}", link, e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let item_map = item_map.unwrap();
|
|
||||||
if !item_map.contains_key("ice-server") {
|
|
||||||
// Not a link header we care about
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let link = item_map.get("ice-server").unwrap();
|
|
||||||
|
|
||||||
// Note: webrtcbin needs ice servers to be in the below format
|
|
||||||
// <scheme>://<user:pass>@<url>
|
|
||||||
// and the ice-servers (link headers) received from the whip server might be
|
|
||||||
// in the format <scheme>:<host> with username and password as separate params.
|
|
||||||
// Constructing these with 'url' crate also require a format/parse
|
|
||||||
// for changing <scheme>:<host> to <scheme>://<user>:<password>@<host>.
|
|
||||||
// So preferred to use the String rather
|
|
||||||
|
|
||||||
let mut ice_server_url;
|
|
||||||
|
|
||||||
// check if uri has ://
|
|
||||||
if link.uri.has_authority() {
|
|
||||||
// use raw_uri as is
|
|
||||||
// username and password in the link.uri.params ignored
|
|
||||||
ice_server_url = link.raw_uri.as_str().to_string();
|
|
||||||
} else {
|
|
||||||
// construct url as '<scheme>://<user:pass>@<url>'
|
|
||||||
ice_server_url = format!("{}://", link.uri.scheme());
|
|
||||||
if let Some(user) = link.params.get("username") {
|
|
||||||
ice_server_url += user.as_str();
|
|
||||||
if let Some(pass) = link.params.get("credential") {
|
|
||||||
ice_server_url = ice_server_url + ":" + pass.as_str();
|
|
||||||
}
|
|
||||||
ice_server_url += "@";
|
|
||||||
}
|
|
||||||
|
|
||||||
// the raw_uri contains the ice-server in the form <scheme>:<url>
|
|
||||||
// so strip the scheme and the ':' from the beginning of raw_uri and use
|
|
||||||
// the rest of raw_uri to append it the url which will be in the form
|
|
||||||
// <scheme>://<user:pass>@<url> as expected
|
|
||||||
ice_server_url += link
|
|
||||||
.raw_uri
|
|
||||||
.strip_prefix((link.uri.scheme().to_owned() + ":").as_str())
|
|
||||||
.expect("strip 'scheme:' from raw uri");
|
|
||||||
}
|
|
||||||
|
|
||||||
gst::debug!(
|
|
||||||
CAT,
|
|
||||||
imp: self,
|
|
||||||
"Setting STUN/TURN server {}",
|
|
||||||
ice_server_url
|
|
||||||
);
|
|
||||||
|
|
||||||
// It's icer to not collapse the `else if` and its inner `if`
|
|
||||||
#[allow(clippy::collapsible_if)]
|
|
||||||
if link.uri.scheme() == "stun" {
|
|
||||||
self.webrtcbin
|
|
||||||
.set_property_from_str("stun-server", ice_server_url.as_str());
|
|
||||||
} else if link.uri.scheme().starts_with("turn") {
|
|
||||||
if !self
|
|
||||||
.webrtcbin
|
|
||||||
.emit_by_name::<bool>("add-turn-server", &[&ice_server_url.as_str()])
|
|
||||||
{
|
|
||||||
gst::error!(
|
|
||||||
CAT,
|
|
||||||
imp: self,
|
|
||||||
"Falied to set turn server {}",
|
|
||||||
ice_server_url
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn send_offer(
|
fn send_offer(
|
||||||
&self,
|
&self,
|
||||||
offer: gst_webrtc::WebRTCSessionDescription,
|
offer: gst_webrtc::WebRTCSessionDescription,
|
||||||
|
@ -613,12 +551,7 @@ impl WhipSink {
|
||||||
.body(body)
|
.body(body)
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
let resp = match wait(&self.canceller, future) {
|
let resp = wait(&self.canceller, future)?;
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = match resp.status() {
|
let res = match resp.status() {
|
||||||
StatusCode::OK | StatusCode::CREATED => {
|
StatusCode::OK | StatusCode::CREATED => {
|
||||||
|
@ -633,20 +566,45 @@ impl WhipSink {
|
||||||
// So we want to construct the full url of the resource
|
// So we want to construct the full url of the resource
|
||||||
// using the endpoint url i.e., replace the end point path with
|
// using the endpoint url i.e., replace the end point path with
|
||||||
// resource path
|
// resource path
|
||||||
let location = resp.headers().get(reqwest::header::LOCATION).unwrap();
|
let location = match resp.headers().get(reqwest::header::LOCATION) {
|
||||||
let mut url = reqwest::Url::parse(endpoint.as_str()).unwrap();
|
Some(location) => location,
|
||||||
|
None => {
|
||||||
|
return Err(gst::error_msg!(
|
||||||
|
gst::ResourceError::Failed,
|
||||||
|
["Location header field should be present for WHIP resource URL"]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let location = match location.to_str() {
|
||||||
|
Ok(loc) => loc,
|
||||||
|
Err(e) => {
|
||||||
|
return Err(gst::error_msg!(
|
||||||
|
gst::ResourceError::Failed,
|
||||||
|
["Failed to convert location to string {}", e]
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = reqwest::Url::parse(endpoint.as_str()).unwrap();
|
||||||
|
|
||||||
|
gst::debug!(CAT, imp: self, "WHIP resource: {:?}", location);
|
||||||
|
|
||||||
|
let url = url.join(location).map_err(|err| {
|
||||||
|
gst::error_msg!(
|
||||||
|
gst::ResourceError::Failed,
|
||||||
|
["URL join operation failed: {:?}", err]
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
url.set_path(location.to_str().unwrap());
|
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
*state = State::Running {
|
*state = State::Running {
|
||||||
whip_resource_url: url.to_string(),
|
whip_resource_url: url.to_string(),
|
||||||
};
|
};
|
||||||
drop(state);
|
drop(state);
|
||||||
|
|
||||||
let ans_bytes = match wait(&self.canceller, resp.bytes()) {
|
let ans_bytes = wait(&self.canceller, resp.bytes())?;
|
||||||
Ok(ans) => ans.to_vec(),
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
};
|
|
||||||
match sdp_message::SDPMessage::parse_buffer(&ans_bytes) {
|
match sdp_message::SDPMessage::parse_buffer(&ans_bytes) {
|
||||||
Ok(ans_sdp) => {
|
Ok(ans_sdp) => {
|
||||||
let answer = gst_webrtc::WebRTCSessionDescription::new(
|
let answer = gst_webrtc::WebRTCSessionDescription::new(
|
||||||
|
|
Loading…
Reference in a new issue