added TURN support

This commit is contained in:
Jasper Hugo 2021-08-13 23:09:17 +07:00
parent bed8d156dc
commit 7190086673
8 changed files with 82 additions and 51 deletions

View File

@ -116,3 +116,7 @@ The dependency `gstreamer` is licensed under the GNU Lesser General Public Licen
Any kinds of contributions are welcome as a pull request.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in these crates by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
## Acknowledgements
`gst-meet` development is sponsored by [AVStack](https://www.avstack.io/). We provide globally-distributed, scalable, managed Jitsi Meet backends.

View File

@ -21,12 +21,7 @@ use xmpp_parsers::{
BareJid, Element, FullJid, Jid,
};
use crate::{
jingle::JingleSession,
source::MediaType,
stanza_filter::StanzaFilter,
xmpp,
};
use crate::{jingle::JingleSession, source::MediaType, stanza_filter::StanzaFilter, xmpp};
static DISCO_INFO: Lazy<DiscoInfoResult> = Lazy::new(|| DiscoInfoResult {
node: None,
@ -36,7 +31,6 @@ static DISCO_INFO: Lazy<DiscoInfoResult> = Lazy::new(|| DiscoInfoResult {
Feature::new(ns::JINGLE_RTP_VIDEO),
Feature::new(ns::JINGLE_ICE_UDP),
Feature::new(ns::JINGLE_DTLS),
// not supported yet: rtx
// Feature::new("urn:ietf:rfc:4588"),

View File

@ -280,7 +280,9 @@ impl JitsiConnection {
}
let iq = Iq::from_get(generate_id(), xmpp::extdisco::ServicesQuery {})
.with_from(Jid::Full(locked_inner.jid.as_ref().context("missing jid")?.clone()))
.with_from(Jid::Full(
locked_inner.jid.as_ref().context("missing jid")?.clone(),
))
.with_to(Jid::Bare(locked_inner.xmpp_domain.clone()));
tx.send(iq.into()).await?;
locked_inner.state = DiscoveringExternalServices;

View File

@ -34,6 +34,7 @@ use crate::{
};
const DEFAULT_STUN_PORT: u16 = 3478;
const DEFAULT_TURNS_PORT: u16 = 5349;
pub(crate) struct JingleSession {
pipeline: gstreamer::Pipeline,
@ -198,7 +199,10 @@ impl JingleSession {
}
if let Some(remote_fingerprint) = dtls_fingerprint {
warn!("Remote DTLS fingerprint (verification not implemented yet): {:?}", remote_fingerprint);
warn!(
"Remote DTLS fingerprint (verification not implemented yet): {:?}",
remote_fingerprint
);
}
let mut dtls_cert_params = CertificateParams::new(vec!["gst-meet".to_owned()]);
@ -225,13 +229,23 @@ impl JingleSession {
debug!("audio SSRC: {}", audio_ssrc);
debug!("video SSRC: {}", video_ssrc);
let ice_agent = nice::Agent::new(&conference.glib_main_context, nice::Compatibility::Rfc5245);
ice_agent.set_ice_tcp(false);
ice_agent.set_upnp(false);
let ice_stream_id = ice_agent.add_stream(1);
let ice_component_id = 1;
let maybe_stun = conference
.external_services
.iter()
.find(|svc| svc.r#type == "stun");
let stun_addr = if let Some(stun) = maybe_stun {
lookup_host(format!("{}:{}", stun.host, stun.port.unwrap_or(DEFAULT_STUN_PORT)))
lookup_host(format!(
"{}:{}",
stun.host,
stun.port.unwrap_or(DEFAULT_STUN_PORT)
))
.await?
.next()
}
@ -240,21 +254,38 @@ impl JingleSession {
};
debug!("STUN address: {:?}", stun_addr);
let ice_agent = nice::Agent::new(&conference.glib_main_context, nice::Compatibility::Rfc5245);
ice_agent.set_ice_tcp(false);
if let Some((stun_addr, stun_port)) = stun_addr.map(|sa| (sa.ip().to_string(), sa.port())) {
ice_agent.set_stun_server(Some(&stun_addr));
ice_agent.set_stun_server_port(stun_port as u32);
}
ice_agent.set_upnp(false);
ice_agent.connect_component_state_changed(|_, a, b, c| {
debug!("ICE component-state-changed {} {} {}", a, b, c);
});
ice_agent.connect_new_selected_pair(|_, a, b, c, d| {
debug!("ICE new-selected-pair {} {} {} {}", a, b, c, d);
});
let ice_stream_id = ice_agent.add_stream(1);
let ice_component_id = 1;
let maybe_turn = conference
.external_services
.iter()
.find(|svc| svc.r#type == "turns");
if let Some(turn_server) = maybe_turn {
let maybe_addr = lookup_host(format!(
"{}:{}",
turn_server.host,
turn_server.port.unwrap_or(DEFAULT_TURNS_PORT)
))
.await?
.next();
if let Some(addr) = maybe_addr {
debug!("TURN address: {:?}", addr);
ice_agent.set_relay_info(
ice_stream_id,
ice_component_id,
&addr.ip().to_string(),
addr.port() as u32,
turn_server.username.as_deref().unwrap_or_default(),
turn_server.password.as_deref().unwrap_or_default(),
nice::RelayType::Tls,
);
}
}
if !ice_agent.attach_recv(
ice_stream_id,
@ -716,8 +747,7 @@ impl JingleSession {
});
description.ssrcs = vec![ssrc];
let mut transport = IceUdpTransport::new()
.with_fingerprint(Fingerprint {
let mut transport = IceUdpTransport::new().with_fingerprint(Fingerprint {
hash: Algo::Sha_256,
setup: Some(Setup::Active),
value: fingerprint.clone(),

View File

@ -1,11 +1,11 @@
pub mod conference;
pub mod connection;
mod jingle;
mod xmpp;
mod pinger;
pub mod source;
mod stanza_filter;
mod util;
mod xmpp;
pub use crate::{
conference::{JitsiConference, JitsiConferenceConfig, Participant},

View File

@ -1,10 +1,7 @@
use std::convert::TryFrom;
use anyhow::{bail, Context, Result};
use xmpp_parsers::{
Element,
iq::IqGetPayload,
};
use xmpp_parsers::{iq::IqGetPayload, Element};
use crate::xmpp::ns;
@ -56,17 +53,21 @@ impl TryFrom<Element> for ServicesResult {
Ok(ServicesResult {
services: elem
.children()
.map(|child| Ok(Service {
.map(|child| {
Ok(Service {
r#type: child.attr("type").context("missing type attr")?.to_owned(),
name: child.attr("name").map(ToOwned::to_owned),
host: child.attr("host").context("missing host attr")?.to_owned(),
port: child.attr("port").map(|p| p.parse()).transpose()?,
transport: child.attr("transport").map(ToOwned::to_owned),
restricted: child.attr("restricted").map(|b| b.to_lowercase() == "parse" || b == "1"),
restricted: child
.attr("restricted")
.map(|b| b.to_lowercase() == "parse" || b == "1"),
username: child.attr("username").map(ToOwned::to_owned),
password: child.attr("password").map(ToOwned::to_owned),
expires: child.attr("expires").map(ToOwned::to_owned),
}))
})
})
.collect::<Result<_>>()?,
})
}