#![allow(clippy::ptr_offset_with_cast)] #![allow(missing_docs)] use glib::glib_wrapper;
use glib::BoolError;
use glib::MainContext;
use glib::ObjectExt;
use glib::SignalHandlerId;
use std::borrow::Borrow;
use std::ffi::CStr;
use std::ffi::CString;
use std::io;
use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use std::net::SocketAddr;
use std::ops::DerefMut;
use std::os::raw::c_char;
use std::os::raw::c_uint;
use webrtc_sdp::address::Address;
use webrtc_sdp::attribute_type::SdpAttributeCandidate;
use webrtc_sdp::attribute_type::SdpAttributeCandidateTcpType;
use webrtc_sdp::attribute_type::SdpAttributeCandidateTransport;
use webrtc_sdp::attribute_type::SdpAttributeCandidateType;
use glib::translate::*;
use std::ptr;
use libnice_sys as sys;
glib_wrapper! {
pub struct NiceAgent(Object<sys::NiceAgent, NiceAgentClass>);
match fn {
get_type => || sys::nice_agent_get_type(),
}
}
unsafe impl Send for NiceAgent {}
unsafe impl Sync for NiceAgent {}
pub type BoolResult<T> = Result<T, BoolError>;
impl NiceAgent {
pub fn new_rfc5245(ctx: &MainContext) -> NiceAgent {
Self::new(ctx, NiceCompatibility::RFC5245)
}
pub fn new(ctx: &MainContext, compat: NiceCompatibility) -> NiceAgent {
let ptr = unsafe { sys::nice_agent_new(ctx.to_glib_none().0, compat as u32) };
if ptr.is_null() {
panic!("nice_agent_new failed");
}
unsafe { NiceAgent::from_glib_full(ptr) }
}
pub fn on_new_candidate<F: Fn(&NiceCandidate) + Send + Sync + 'static>(
&mut self,
f: F,
) -> BoolResult<SignalHandlerId> {
self.connect("new-candidate-full", false, move |values| {
f(&values[1].get().unwrap().unwrap());
None
})
}
pub fn on_candidate_gathering_done<F: Fn(c_uint) + Send + Sync + 'static>(
&mut self,
f: F,
) -> BoolResult<SignalHandlerId> {
self.connect("candidate-gathering-done", false, move |values| {
f(values[1].get().unwrap().unwrap());
None
})
}
pub fn on_component_state_changed<F>(&mut self, f: F) -> BoolResult<SignalHandlerId>
where
F: Fn(c_uint, c_uint, NiceComponentState) + Send + Sync + 'static,
{
self.connect("component-state-changed", false, move |values| {
let stream_id = values[1].get().unwrap().unwrap();
let component_id = values[2].get().unwrap().unwrap();
let state: c_uint = values[3].get().unwrap().unwrap();
f(stream_id, component_id, state.into());
None
})
}
pub fn set_software(&self, name: impl Borrow<CStr>) {
unsafe { sys::nice_agent_set_software(self.to_glib_none().0, name.borrow().as_ptr()) }
}
pub fn set_controlling_mode(&mut self, controlling: bool) {
self.set_property("controlling-mode", &controlling)
.expect("failed to set controlling-mode property");
}
pub fn add_stream(&self, components: c_uint) -> BoolResult<c_uint> {
let id = unsafe { sys::nice_agent_add_stream(self.to_glib_none().0, components) };
if id == 0 {
return Err(glib_bool_error!("add_stream failed"));
}
Ok(id)
}
pub fn gather_candidates(&self, stream_id: c_uint) -> BoolResult<()> {
glib_result_from_gboolean!(
unsafe { sys::nice_agent_gather_candidates(self.to_glib_none().0, stream_id) },
"gather_candidates failed",
)
}
pub fn set_remote_credentials(
&self,
stream_id: c_uint,
ufrag: &CStr,
pwd: &CStr,
) -> BoolResult<()> {
glib_result_from_gboolean!(
unsafe {
sys::nice_agent_set_remote_credentials(
self.to_glib_none().0,
stream_id,
ufrag.as_ptr(),
pwd.as_ptr(),
)
},
"set_remote_credentials failed",
)
}
pub fn get_local_credentials(&self, stream_id: c_uint) -> BoolResult<(CString, CString)> {
let mut ufrag_ptr: *mut c_char = ptr::null_mut();
let mut pwd_ptr: *mut c_char = ptr::null_mut();
if unsafe {
sys::nice_agent_get_local_credentials(
self.to_glib_none().0,
stream_id,
&mut ufrag_ptr,
&mut pwd_ptr,
)
} == 0
{
return Err(glib_bool_error!("set_remote_credentials failed"));
}
let ufrag = unsafe { CStr::from_ptr(ufrag_ptr) }.to_owned();
let pwd = unsafe { CStr::from_ptr(pwd_ptr) }.to_owned();
unsafe {
glib_sys::g_free(ufrag_ptr as glib_sys::gpointer);
glib_sys::g_free(pwd_ptr as glib_sys::gpointer);
}
Ok((ufrag, pwd))
}
pub fn add_remote_candidates<'a>(
&self,
stream_id: c_uint,
component_id: c_uint,
candidates: &'a [&'a NiceCandidate],
) -> BoolResult<usize> {
let res = unsafe {
let mut list = ptr::null_mut::<glib_sys::GSList>();
for candidate in candidates.iter().rev() {
list = glib_sys::g_slist_prepend(list, Ptr::to(candidate.to_glib_none().0));
}
let res = sys::nice_agent_set_remote_candidates(
self.to_glib_none().0,
stream_id,
component_id,
list,
);
glib_sys::g_slist_free(list);
res
};
if res < 0 {
return Err(glib_bool_error!("set_remote_candidates failed"));
}
Ok(res as usize)
}
pub fn set_port_range(
&self,
stream_id: c_uint,
component_id: c_uint,
min_port: u16,
max_port: u16,
) {
unsafe {
sys::nice_agent_set_port_range(
self.to_glib_none().0,
stream_id,
component_id,
min_port.into(),
max_port.into(),
)
}
}
pub fn send(&self, stream_id: c_uint, component_id: c_uint, buf: &[u8]) -> Option<usize> {
let res = unsafe {
sys::nice_agent_send(
self.to_glib_none().0,
stream_id,
component_id,
buf.len() as c_uint,
buf.as_ptr() as *const c_char,
)
};
if res < 0 {
return None;
}
Some(res as usize)
}
pub fn attach_recv<F: FnMut(&[u8]) + Send + 'static>(
&mut self,
stream_id: c_uint,
component_id: c_uint,
ctx: &MainContext,
f: F,
) -> BoolResult<AttachRecvHandle> {
extern "C" fn wrapper<F: FnMut(&[u8]) + Send + 'static>(
_agent: *mut sys::NiceAgent,
_stream_id: c_uint,
_component_id: c_uint,
len: c_uint,
buf: *mut c_char,
user_data: glib_sys::gpointer,
) {
let f_ptr = user_data as *mut F;
let f = unsafe { &mut *f_ptr };
let buf = unsafe { std::slice::from_raw_parts(buf as *mut u8, len as usize) };
f(buf)
}
let mut boxed_f = Box::new(f);
let res = unsafe {
sys::nice_agent_attach_recv(
self.to_glib_none().0,
stream_id,
component_id,
ctx.to_glib_none().0,
Some(wrapper::<F>),
boxed_f.deref_mut() as *mut F as glib_sys::gpointer,
)
};
if res < 0 {
return Err(glib_bool_error!("attach_recv failed"));
}
Ok(AttachRecvHandle(
self.clone(),
stream_id,
component_id,
ctx.clone(),
boxed_f,
))
}
pub fn detach_recv(
&mut self,
stream_id: c_uint,
component_id: c_uint,
ctx: &MainContext,
) -> BoolResult<()> {
let res = unsafe {
sys::nice_agent_attach_recv(
self.to_glib_none().0,
stream_id,
component_id,
ctx.to_glib_none().0,
None,
ptr::null_mut(),
)
};
if res < 0 {
return Err(glib_bool_error!("attach_recv failed"));
}
Ok(())
}
}
#[must_use = "when an AttachRecvHandle is dropped, it detaches the callback"]
pub struct AttachRecvHandle(
NiceAgent,
c_uint,
c_uint,
MainContext,
Box<dyn std::any::Any + Send>,
);
impl Drop for AttachRecvHandle {
fn drop(&mut self) {
self.0
.detach_recv(self.1, self.2, &self.3)
.expect("cannot continue safely when detach failed");
}
}
glib_wrapper! {
pub struct NiceCandidate(Boxed<sys::NiceCandidate>);
match fn {
copy => |ptr| sys::nice_candidate_copy(ptr),
free => |ptr| sys::nice_candidate_free(ptr),
get_type => || glib::Type::from_name("NiceCandidate").unwrap().to_glib(),
}
}
impl NiceCandidate {
pub fn new(type_: NiceCandidateType) -> Self {
unsafe { Self::from_glib_full(sys::nice_candidate_new(type_ as u32)) }
}
pub fn from_sdp_without_fqdn(sdp: &SdpAttributeCandidate) -> io::Result<Self> {
let mut raw = Self::new(match sdp.c_type {
SdpAttributeCandidateType::Host => NiceCandidateType::Host,
SdpAttributeCandidateType::Srflx => NiceCandidateType::ServerReflexive,
SdpAttributeCandidateType::Prflx => NiceCandidateType::PeerReflexive,
SdpAttributeCandidateType::Relay => NiceCandidateType::Relayed,
});
raw.set_transport(match sdp.transport {
SdpAttributeCandidateTransport::Udp => NiceCandidateTransport::Udp,
SdpAttributeCandidateTransport::Tcp => match sdp.tcp_type.as_ref().ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"transport is tcp but tcp_type is not set",
)
})? {
SdpAttributeCandidateTcpType::Active => NiceCandidateTransport::TcpActive,
SdpAttributeCandidateTcpType::Passive => NiceCandidateTransport::TcpPassive,
SdpAttributeCandidateTcpType::Simultaneous => NiceCandidateTransport::TcpSO,
},
});
raw.set_addr(SocketAddr::new(
match sdp.address {
Address::Ip(ip) => ip,
Address::Fqdn(_) => {
return Err(io::Error::new(
io::ErrorKind::Other,
"FQDN are not supported by from_sdp_without_fqdn",
))
}
},
sdp.port as u16,
));
raw.set_priority(sdp.priority as u32); raw.set_component_id(sdp.component as c_uint);
raw.set_foundation(&CString::new(sdp.foundation.clone()).unwrap());
Ok(raw)
}
pub fn stream_id(&self) -> c_uint {
let raw = unsafe { &*self.to_glib_none().0 };
raw.stream_id
}
pub fn type_(&self) -> NiceCandidateType {
let raw = unsafe { &*self.to_glib_none().0 };
raw.type_.into()
}
pub fn transport(&self) -> NiceCandidateTransport {
let raw = unsafe { &*self.to_glib_none().0 };
raw.transport.into()
}
pub fn set_transport(&mut self, transport: NiceCandidateTransport) {
let raw = unsafe { &mut *self.to_glib_none_mut().0 };
raw.transport = transport as u32;
}
pub fn addr(&self) -> SocketAddr {
let raw = unsafe { &*self.to_glib_none().0 };
from_nice_addr(&raw.addr)
}
pub fn set_addr(&mut self, addr: impl Borrow<SocketAddr>) {
let raw = unsafe { &mut *self.to_glib_none_mut().0 };
to_nice_addr(addr.borrow(), &mut raw.addr);
}
pub fn priority(&self) -> u32 {
let raw = unsafe { &*self.to_glib_none().0 };
raw.priority
}
pub fn set_priority(&mut self, priority: u32) {
let raw = unsafe { &mut *self.to_glib_none_mut().0 };
raw.priority = priority;
}
pub fn component_id(&self) -> c_uint {
let raw = unsafe { &*self.to_glib_none().0 };
raw.component_id
}
pub fn set_component_id(&mut self, component_id: c_uint) {
let raw = unsafe { &mut *self.to_glib_none_mut().0 };
raw.component_id = component_id;
}
pub fn foundation(&self) -> &CStr {
let raw = unsafe { &*self.to_glib_none().0 };
unsafe { CStr::from_ptr((&raw.foundation) as *const c_char) }
}
pub fn set_foundation(&mut self, foundation: &CStr) {
let self_foundation = unsafe { &mut (*self.to_glib_none_mut().0).foundation };
let foundation = foundation.to_bytes_with_nul();
if foundation.len() > self_foundation.len() {
panic!("foundation too long (>{} bytes)", self_foundation.len());
}
for i in 0..foundation.len() {
self_foundation[i] = foundation[i] as c_char;
}
}
pub fn to_sdp(&self) -> SdpAttributeCandidate {
let address = self.addr();
SdpAttributeCandidate {
foundation: self
.foundation()
.to_owned()
.into_string()
.expect("foundation is ascii"),
component: self.component_id() as u32,
transport: match self.transport() {
NiceCandidateTransport::Udp => SdpAttributeCandidateTransport::Udp,
_ => SdpAttributeCandidateTransport::Tcp,
},
priority: u64::from(self.priority()),
address: Address::Ip(address.ip()),
port: u32::from(address.port()),
c_type: match self.type_() {
NiceCandidateType::Host => SdpAttributeCandidateType::Host,
NiceCandidateType::ServerReflexive => SdpAttributeCandidateType::Srflx,
NiceCandidateType::PeerReflexive => SdpAttributeCandidateType::Prflx,
NiceCandidateType::Relayed => SdpAttributeCandidateType::Relay,
},
raddr: None,
rport: None,
tcp_type: match self.transport() {
NiceCandidateTransport::Udp => None,
NiceCandidateTransport::TcpActive => Some(SdpAttributeCandidateTcpType::Active),
NiceCandidateTransport::TcpPassive => Some(SdpAttributeCandidateTcpType::Passive),
NiceCandidateTransport::TcpSO => Some(SdpAttributeCandidateTcpType::Simultaneous),
},
generation: None,
ufrag: None,
networkcost: None,
unknown_extensions: Vec::new(),
}
}
}
#[allow(missing_docs)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NiceCandidateType {
Host = sys::NiceCandidateType_NICE_CANDIDATE_TYPE_HOST as isize,
ServerReflexive = sys::NiceCandidateType_NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE as isize,
PeerReflexive = sys::NiceCandidateType_NICE_CANDIDATE_TYPE_PEER_REFLEXIVE as isize,
Relayed = sys::NiceCandidateType_NICE_CANDIDATE_TYPE_RELAYED as isize,
}
impl From<sys::NiceCandidateType> for NiceCandidateType {
fn from(raw: sys::NiceCandidateType) -> Self {
match raw {
sys::NiceCandidateType_NICE_CANDIDATE_TYPE_HOST => NiceCandidateType::Host,
sys::NiceCandidateType_NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE => {
NiceCandidateType::ServerReflexive
}
sys::NiceCandidateType_NICE_CANDIDATE_TYPE_PEER_REFLEXIVE => {
NiceCandidateType::PeerReflexive
}
sys::NiceCandidateType_NICE_CANDIDATE_TYPE_RELAYED => NiceCandidateType::Relayed,
_ => panic!("unknown NiceCandidateType: {}", raw),
}
}
}
#[allow(missing_docs)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NiceCandidateTransport {
Udp = sys::NiceCandidateTransport_NICE_CANDIDATE_TRANSPORT_UDP as isize,
TcpActive = sys::NiceCandidateTransport_NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE as isize,
TcpPassive = sys::NiceCandidateTransport_NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE as isize,
TcpSO = sys::NiceCandidateTransport_NICE_CANDIDATE_TRANSPORT_TCP_SO as isize,
}
impl From<sys::NiceCandidateTransport> for NiceCandidateTransport {
fn from(raw: sys::NiceCandidateTransport) -> Self {
match raw {
sys::NiceCandidateTransport_NICE_CANDIDATE_TRANSPORT_UDP => NiceCandidateTransport::Udp,
sys::NiceCandidateTransport_NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE => {
NiceCandidateTransport::TcpActive
}
sys::NiceCandidateTransport_NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE => {
NiceCandidateTransport::TcpPassive
}
sys::NiceCandidateTransport_NICE_CANDIDATE_TRANSPORT_TCP_SO => {
NiceCandidateTransport::TcpSO
}
_ => panic!("unknown NiceCandidateTransport: {}", raw),
}
}
}
#[allow(missing_docs)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NiceRelayType {
TurnUdp = sys::NiceRelayType_NICE_RELAY_TYPE_TURN_UDP as isize,
TurnTcp = sys::NiceRelayType_NICE_RELAY_TYPE_TURN_TCP as isize,
TurnTls = sys::NiceRelayType_NICE_RELAY_TYPE_TURN_TLS as isize,
}
impl From<sys::NiceRelayType> for NiceRelayType {
fn from(raw: sys::NiceRelayType) -> Self {
match raw {
sys::NiceRelayType_NICE_RELAY_TYPE_TURN_UDP => NiceRelayType::TurnUdp,
sys::NiceRelayType_NICE_RELAY_TYPE_TURN_TCP => NiceRelayType::TurnTcp,
sys::NiceRelayType_NICE_RELAY_TYPE_TURN_TLS => NiceRelayType::TurnTls,
_ => panic!("unknown NiceRelayType: {}", raw),
}
}
}
#[allow(missing_docs)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NiceComponentState {
Disconnected = sys::NiceComponentState_NICE_COMPONENT_STATE_DISCONNECTED as isize,
Gathering = sys::NiceComponentState_NICE_COMPONENT_STATE_GATHERING as isize,
Connecting = sys::NiceComponentState_NICE_COMPONENT_STATE_CONNECTING as isize,
Connected = sys::NiceComponentState_NICE_COMPONENT_STATE_CONNECTED as isize,
Ready = sys::NiceComponentState_NICE_COMPONENT_STATE_READY as isize,
Failed = sys::NiceComponentState_NICE_COMPONENT_STATE_FAILED as isize,
}
impl From<sys::NiceComponentState> for NiceComponentState {
fn from(raw: sys::NiceComponentState) -> Self {
use NiceComponentState::*;
match raw {
sys::NiceComponentState_NICE_COMPONENT_STATE_DISCONNECTED => Disconnected,
sys::NiceComponentState_NICE_COMPONENT_STATE_GATHERING => Gathering,
sys::NiceComponentState_NICE_COMPONENT_STATE_CONNECTING => Connecting,
sys::NiceComponentState_NICE_COMPONENT_STATE_CONNECTED => Connected,
sys::NiceComponentState_NICE_COMPONENT_STATE_READY => Ready,
sys::NiceComponentState_NICE_COMPONENT_STATE_FAILED => Failed,
_ => panic!("unknown NiceComponentState: {}", raw),
}
}
}
#[allow(missing_docs)] #[repr(C)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NiceCompatibility {
RFC5245 = sys::NiceCompatibility_NICE_COMPATIBILITY_RFC5245 as isize,
GOOGLE = sys::NiceCompatibility_NICE_COMPATIBILITY_GOOGLE as isize,
MSN = sys::NiceCompatibility_NICE_COMPATIBILITY_MSN as isize,
WLM2009 = sys::NiceCompatibility_NICE_COMPATIBILITY_WLM2009 as isize,
OC2007 = sys::NiceCompatibility_NICE_COMPATIBILITY_OC2007 as isize,
OC2007R2 = sys::NiceCompatibility_NICE_COMPATIBILITY_OC2007R2 as isize,
}
fn from_nice_addr(raw: &sys::NiceAddress) -> SocketAddr {
unsafe {
match i32::from(raw.s.addr.as_ref().sa_family) {
libc::AF_INET => (
Ipv4Addr::from(u32::from_be(raw.s.ip4.as_ref().sin_addr.s_addr)),
u16::from_be(raw.s.ip4.as_ref().sin_port),
)
.into(),
libc::AF_INET6 => (
Ipv6Addr::from(raw.s.ip6.as_ref().sin6_addr.s6_addr),
u16::from_be(raw.s.ip6.as_ref().sin6_port),
)
.into(),
other => panic!("unknown AF type: {}", other),
}
}
}
fn to_nice_addr(addr: &SocketAddr, raw: &mut sys::NiceAddress) {
match addr {
SocketAddr::V4(addr) => {
let raw_addr = unsafe { raw.s.ip4.as_mut() };
raw_addr.sin_family = libc::AF_INET as libc::sa_family_t;
raw_addr.sin_port = addr.port().to_be();
raw_addr.sin_addr.s_addr = u32::from(*addr.ip()).to_be();
}
SocketAddr::V6(addr) => {
let raw_addr = unsafe { raw.s.ip6.as_mut() };
raw_addr.sin6_family = libc::AF_INET6 as libc::sa_family_t;
raw_addr.sin6_port = addr.port().to_be();
raw_addr.sin6_addr.s6_addr = addr.ip().octets();
}
}
}