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//! A [`Component`] in an ICE [`Stream`](crate::stream::Stream)
10
11use sans_io_time::Instant;
12
13use crate::agent::{AgentError, AgentTransmit};
14use crate::candidate::{CandidatePair, TransportType};
15use crate::turn::TurnConfig;
16use crate::{Address, const_override, mut_override};
17
18/// A [`Component`] in an ICE [`Stream`](crate::stream::Stream)
19#[derive(Debug)]
20pub struct Component {
21    ffi: *mut crate::ffi::RiceComponent,
22    stream_id: usize,
23}
24
25unsafe impl Send for Component {}
26unsafe impl Sync for Component {}
27
28impl Clone for Component {
29    fn clone(&self) -> Self {
30        Self {
31            ffi: unsafe { crate::ffi::rice_component_ref(self.ffi) },
32            stream_id: self.stream_id,
33        }
34    }
35}
36
37impl Drop for Component {
38    fn drop(&mut self) {
39        unsafe { crate::ffi::rice_component_unref(self.ffi) }
40    }
41}
42
43impl Component {
44    pub(crate) fn from_c_full(component: *mut crate::ffi::RiceComponent, stream_id: usize) -> Self {
45        Self {
46            ffi: component,
47            stream_id,
48        }
49    }
50
51    /// The component identifier within a particular ICE [`Stream`](crate::stream::Stream)
52    pub fn id(&self) -> usize {
53        unsafe { crate::ffi::rice_component_get_id(self.ffi) }
54    }
55
56    /// Retrieve the [`Stream`](crate::stream::Stream) for this component.
57    pub fn stream(&self) -> crate::stream::Stream {
58        unsafe {
59            crate::stream::Stream::from_c_full(crate::ffi::rice_component_get_stream(self.ffi))
60        }
61    }
62
63    /// Retrieve the current state of a `Component`
64    pub fn state(&self) -> ComponentConnectionState {
65        unsafe { crate::ffi::rice_component_get_state(self.ffi).into() }
66    }
67
68    /// The [`CandidatePair`] this component has selected to send/receive data with.  This will not
69    /// be valid until the [`Component`] has reached [`ComponentConnectionState::Connected`]
70    pub fn selected_pair(&self) -> Option<CandidatePair> {
71        unsafe {
72            let mut local = crate::ffi::RiceCandidate::zeroed();
73            let mut remote = crate::ffi::RiceCandidate::zeroed();
74            crate::ffi::rice_component_selected_pair(self.ffi, &mut local, &mut remote);
75            if local.address.is_null() || remote.address.is_null() {
76                None
77            } else {
78                Some(crate::candidate::CandidatePair::new(
79                    crate::candidate::Candidate::from_c_full(local).to_owned(),
80                    crate::candidate::Candidate::from_c_full(remote).to_owned(),
81                ))
82            }
83        }
84    }
85
86    /// Start gathering candidates for this component.  The parent
87    /// [`Agent::poll`](crate::agent::Agent::poll) is used to progress
88    /// the gathering.
89    pub fn gather_candidates<'a, 'b>(
90        &self,
91        sockets: impl IntoIterator<Item = (TransportType, &'a Address)>,
92        turn_servers: impl IntoIterator<Item = (&'b Address, TurnConfig)>,
93    ) -> Result<(), AgentError> {
94        unsafe {
95            let mut transports = vec![];
96            let mut socket_addr = vec![];
97            let mut socket_addresses = vec![];
98            for (ttype, addr) in sockets.into_iter() {
99                transports.push(ttype.into());
100                socket_addresses.push(const_override(addr.ffi));
101                socket_addr.push(addr);
102            }
103            let mut turn_sockets = vec![];
104            let mut turn_configs = vec![];
105            for (turn_addr, config) in turn_servers.into_iter() {
106                turn_sockets.push(const_override(turn_addr.ffi));
107                turn_configs.push(config.into_c_full());
108            }
109            AgentError::from_c(crate::ffi::rice_component_gather_candidates(
110                self.ffi,
111                transports.len(),
112                socket_addresses.as_ptr(),
113                transports.as_ptr(),
114                turn_sockets.len(),
115                turn_sockets.as_ptr(),
116                turn_configs.as_ptr(),
117            ))
118        }
119    }
120
121    /// Set the pair that will be used to send/receive data.  This will override the ICE
122    /// negotiation chosen value.
123    pub fn set_selected_pair(&self, pair: CandidatePair) -> Result<(), AgentError> {
124        unsafe {
125            AgentError::from_c(crate::ffi::rice_component_set_selected_pair(
126                self.ffi,
127                pair.local.as_c(),
128                pair.remote.as_c(),
129            ))
130        }
131    }
132
133    /// Send data to the peer using the selected pair.  This will not succeed until the
134    /// [`Component`] has reached [`ComponentConnectionState::Connected`]
135    pub fn send(&self, data: &[u8], now: Instant) -> Result<AgentTransmit, AgentError> {
136        unsafe {
137            let mut transmit = crate::ffi::RiceTransmit {
138                stream_id: self.stream_id,
139                transport: TransportType::Udp.into(),
140                from: core::ptr::null(),
141                to: core::ptr::null(),
142                data: crate::ffi::RiceDataImpl {
143                    ptr: core::ptr::null_mut(),
144                    size: 0,
145                },
146            };
147            AgentError::from_c(crate::ffi::rice_component_send(
148                self.ffi,
149                mut_override(data.as_ptr()),
150                data.len(),
151                now.as_nanos(),
152                &mut transmit,
153            ))?;
154            Ok(AgentTransmit::from_c_full(transmit))
155        }
156    }
157}
158
159/// The state of a component
160#[repr(u32)]
161#[derive(Debug, Copy, Clone, PartialEq, Eq)]
162pub enum ComponentConnectionState {
163    /// Component is in initial state and no connectivity checks are in progress.
164    New = crate::ffi::RICE_COMPONENT_CONNECTION_STATE_NEW,
165    /// Connectivity checks are in progress for this candidate
166    Connecting = crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTING,
167    /// A [`CandidatePair`](crate::candidate::CandidatePair`) has been selected for this component
168    Connected = crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTED,
169    /// No connection could be found for this Component
170    Failed = crate::ffi::RICE_COMPONENT_CONNECTION_STATE_FAILED,
171}
172
173impl ComponentConnectionState {
174    pub(crate) fn from_c(ffi: crate::ffi::RiceComponentConnectionState) -> Self {
175        match ffi {
176            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_NEW => Self::New,
177            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTING => Self::Connecting,
178            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTED => Self::Connected,
179            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_FAILED => Self::Failed,
180            _ => panic!("Unknown RiceComponentConnectionState value {ffi:x?}"),
181        }
182    }
183}
184
185impl From<crate::ffi::RiceComponentConnectionState> for ComponentConnectionState {
186    fn from(value: crate::ffi::RiceComponentConnectionState) -> Self {
187        match value {
188            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_NEW => Self::New,
189            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTING => Self::Connecting,
190            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_CONNECTED => Self::Connected,
191            crate::ffi::RICE_COMPONENT_CONNECTION_STATE_FAILED => Self::Failed,
192            val => panic!("Unknown component connection state value {val:x?}"),
193        }
194    }
195}