Skip to main content

rice_c/
component.rs

1// Copyright (C) 2025 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8//
9// SPDX-License-Identifier: MIT OR Apache-2.0
10
11//! A [`Component`] in an ICE [`Stream`](crate::stream::Stream)
12
13use sans_io_time::Instant;
14
15use crate::agent::{AgentError, AgentTransmit};
16use crate::candidate::{CandidatePair, TransportType};
17use crate::turn::TurnConfig;
18use crate::{Address, const_override, mut_override};
19
20/// A [`Component`] in an ICE [`Stream`](crate::stream::Stream)
21#[derive(Debug)]
22pub struct Component {
23    ffi: *mut crate::ffi::RiceComponent,
24    stream_id: usize,
25}
26
27unsafe impl Send for Component {}
28unsafe impl Sync for Component {}
29
30impl Clone for Component {
31    fn clone(&self) -> Self {
32        Self {
33            ffi: unsafe { crate::ffi::rice_component_ref(self.ffi) },
34            stream_id: self.stream_id,
35        }
36    }
37}
38
39impl Drop for Component {
40    fn drop(&mut self) {
41        unsafe { crate::ffi::rice_component_unref(self.ffi) }
42    }
43}
44
45impl Component {
46    pub(crate) fn from_c_full(component: *mut crate::ffi::RiceComponent, stream_id: usize) -> Self {
47        Self {
48            ffi: component,
49            stream_id,
50        }
51    }
52
53    /// The component identifier within a particular ICE [`Stream`](crate::stream::Stream)
54    pub fn id(&self) -> usize {
55        unsafe { crate::ffi::rice_component_get_id(self.ffi) }
56    }
57
58    /// Retrieve the [`Stream`](crate::stream::Stream) for this component.
59    pub fn stream(&self) -> crate::stream::Stream {
60        unsafe {
61            crate::stream::Stream::from_c_full(crate::ffi::rice_component_get_stream(self.ffi))
62        }
63    }
64
65    /// Retrieve the current state of a `Component`
66    pub fn state(&self) -> ComponentConnectionState {
67        unsafe { crate::ffi::rice_component_get_state(self.ffi).into() }
68    }
69
70    /// The [`CandidatePair`] this component has selected to send/receive data with.  This will not
71    /// be valid until the [`Component`] has reached [`ComponentConnectionState::Connected`]
72    pub fn selected_pair(&self) -> Option<CandidatePair> {
73        unsafe {
74            let mut local = crate::ffi::RiceCandidate::zeroed();
75            let mut remote = crate::ffi::RiceCandidate::zeroed();
76            crate::ffi::rice_component_selected_pair(self.ffi, &mut local, &mut remote);
77            if local.address.is_null() || remote.address.is_null() {
78                None
79            } else {
80                Some(crate::candidate::CandidatePair::new(
81                    crate::candidate::Candidate::from_c_full(local).to_owned(),
82                    crate::candidate::Candidate::from_c_full(remote).to_owned(),
83                ))
84            }
85        }
86    }
87
88    /// Start gathering candidates for this component.  The parent
89    /// [`Agent::poll`](crate::agent::Agent::poll) is used to progress
90    /// the gathering.
91    ///
92    /// Candidates will be generated as follows (if they succeed):
93    ///
94    /// 1. A host candidate for each `sockets[i]`. If TCP, then both an active and passive host
95    ///    candidate will be generated.
96    /// 2. For each configured STUN server a reflexive candidate if different from any
97    ///    other candidate produced. The local address for each STUN server connection will be one
98    ///    of the entries provided in `sockets`.
99    /// 3. For each `turn_servers[i]` a TURN allocation will be attempted and a relayed candidate
100    ///    produced on success.  If you would like multiple options for relayed candidates,
101    ///    e.g. UDP, TCP, TCP/TLS, then provide each options as different entries in the provided
102    ///    Iterator. The `Address` for each TURN server is the local address to communicate with
103    ///    the TURN server and should be different than any value provided through `sockets`.
104    pub fn gather_candidates<'a, 'b>(
105        &self,
106        sockets: impl IntoIterator<Item = (TransportType, &'a Address)>,
107        turn_servers: impl IntoIterator<Item = (&'b Address, TurnConfig)>,
108    ) -> Result<(), AgentError> {
109        unsafe {
110            let mut transports = vec![];
111            let mut socket_addr = vec![];
112            let mut socket_addresses = vec![];
113            for (ttype, addr) in sockets.into_iter() {
114                transports.push(ttype.into());
115                socket_addresses.push(const_override(addr.ffi));
116                socket_addr.push(addr);
117            }
118            let mut turn_sockets = vec![];
119            let mut turn_configs = vec![];
120            for (turn_addr, config) in turn_servers.into_iter() {
121                turn_sockets.push(const_override(turn_addr.ffi));
122                turn_configs.push(config.into_c_full());
123            }
124            AgentError::from_c(crate::ffi::rice_component_gather_candidates(
125                self.ffi,
126                transports.len(),
127                socket_addresses.as_ptr(),
128                transports.as_ptr(),
129                turn_sockets.len(),
130                turn_sockets.as_ptr(),
131                turn_configs.as_ptr(),
132            ))
133        }
134    }
135
136    /// Set the pair that will be used to send/receive data.  This will override the ICE
137    /// negotiation chosen value.
138    pub fn set_selected_pair(&self, pair: CandidatePair) -> Result<(), AgentError> {
139        unsafe {
140            AgentError::from_c(crate::ffi::rice_component_set_selected_pair(
141                self.ffi,
142                pair.local.as_c(),
143                pair.remote.as_c(),
144            ))
145        }
146    }
147
148    /// Send data to the peer using the selected pair.  This will not succeed until the
149    /// [`Component`] has reached [`ComponentConnectionState::Connected`]
150    pub fn send(&self, data: &[u8], now: Instant) -> Result<AgentTransmit, AgentError> {
151        unsafe {
152            let mut transmit = crate::ffi::RiceTransmit {
153                stream_id: self.stream_id,
154                transport: TransportType::Udp.into(),
155                from: core::ptr::null(),
156                to: core::ptr::null(),
157                data: crate::ffi::RiceDataImpl {
158                    ptr: core::ptr::null_mut(),
159                    size: 0,
160                },
161            };
162            AgentError::from_c(crate::ffi::rice_component_send(
163                self.ffi,
164                mut_override(data.as_ptr()),
165                data.len(),
166                now.as_nanos(),
167                &mut transmit,
168            ))?;
169            Ok(AgentTransmit::from_c_full(transmit))
170        }
171    }
172}
173
174/// The state of a component
175#[repr(u32)]
176#[derive(Debug, Copy, Clone, PartialEq, Eq)]
177pub enum ComponentConnectionState {
178    /// Component is in initial state and no connectivity checks are in progress.
179    New = crate::ffi::RICE_COMPONENT_CONNECTION_STATE_NEW,
180    /// Connectivity checks are in progress for this candidate
181    Connecting = crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTING,
182    /// A [`CandidatePair`](crate::candidate::CandidatePair`) has been selected for this component
183    Connected = crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTED,
184    /// No connection could be found for this Component
185    Failed = crate::ffi::RICE_COMPONENT_CONNECTION_STATE_FAILED,
186}
187
188impl ComponentConnectionState {
189    pub(crate) fn from_c(ffi: crate::ffi::RiceComponentConnectionState) -> Self {
190        match ffi {
191            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_NEW => Self::New,
192            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTING => Self::Connecting,
193            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTED => Self::Connected,
194            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_FAILED => Self::Failed,
195            _ => panic!("Unknown RiceComponentConnectionState value {ffi:x?}"),
196        }
197    }
198}
199
200impl From<crate::ffi::RiceComponentConnectionState> for ComponentConnectionState {
201    fn from(value: crate::ffi::RiceComponentConnectionState) -> Self {
202        match value {
203            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_NEW => Self::New,
204            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTING => Self::Connecting,
205            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTED => Self::Connected,
206            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_FAILED => Self::Failed,
207            val => panic!("Unknown component connection state value {val:x?}"),
208        }
209    }
210}