o_sfu_core/engine/media_transport/rtc/
bootstrap.rs1use std::{
12 io::ErrorKind,
13 net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket as StdUdpSocket},
14 sync::Arc,
15 time::{Duration, Instant},
16};
17
18use o_sfu_rfc::webrtc;
19use str0m::{Candidate, bwe::Bitrate as Str0mBitrate};
20use tracing::{info, warn};
21
22use super::{
23 RtpProfile,
24 bitrate::MediaBitrateCounter,
25 local_send_rewrite::{ConsumerStreamStore, RTX_CACHE_MAX_PACKETS},
26 packet_loop::{RtcUdpSocket, UdpIngress},
27 slots::SessionStore,
28 state::{RtcSessionState, SessionSdpNegotiationState, SharedRtcSocket},
29};
30use crate::{
31 Bitrate, RtcPortRange, RtcUdpIoBackend,
32 engine::media_transport::{TransportAdapterError, TransportSessionKey},
33};
34#[cfg(any(test, feature = "internal-benchmarks", fuzzing))]
35use crate::{CodecPreferences, MediaCodecFlags};
36
37pub(super) fn bind_shared_rtc_socket(
53 announced_ip: IpAddr,
54 rtc_port_range: RtcPortRange,
55 rtc_udp_io_backend: RtcUdpIoBackend,
56) -> Result<SharedRtcSocket, TransportAdapterError> {
57 let bind_ip = bind_ip_for_announced_ip(announced_ip);
58 for port in rtc_port_range.ports() {
59 let bind_addr = SocketAddr::new(bind_ip, port);
60 let socket = match StdUdpSocket::bind(bind_addr) {
61 Ok(socket) => socket,
62 Err(error) if error.kind() == ErrorKind::AddrInUse => continue,
63 Err(error) => {
64 warn!(%bind_addr, ?error, "failed to bind shared rtc UDP socket");
65 return Err(TransportAdapterError::TransportUnavailable);
66 }
67 };
68 socket.set_nonblocking(true).map_err(|error| {
69 warn!(%bind_addr, ?error, "failed to configure shared rtc UDP socket");
70 TransportAdapterError::TransportUnavailable
71 })?;
72 let socket = RtcUdpSocket::from_std(socket, rtc_udp_io_backend).map_err(|error| {
73 warn!(
74 %bind_addr,
75 backend = rtc_udp_io_backend.wire_name(),
76 ?error,
77 "failed to initialize shared rtc UDP socket"
78 );
79 TransportAdapterError::TransportUnavailable
80 })?;
81 let candidate_addr = SocketAddr::new(announced_ip, port);
82 let ingress = UdpIngress::new(socket.clone(), bind_addr, candidate_addr);
83 info!(
84 %bind_addr,
85 %candidate_addr,
86 "booted shared rtc UDP socket"
87 );
88 return Ok(SharedRtcSocket {
89 socket,
90 ingress,
91 candidate_addr,
92 });
93 }
94 warn!(
95 %bind_ip,
96 port_min = rtc_port_range.min(),
97 port_max = rtc_port_range.max(),
98 "rtc UDP port range is unavailable"
99 );
100 Err(TransportAdapterError::TransportUnavailable)
101}
102
103fn bind_ip_for_announced_ip(announced_ip: IpAddr) -> IpAddr {
108 match announced_ip {
109 IpAddr::V4(_) => IpAddr::V4(Ipv4Addr::UNSPECIFIED),
110 IpAddr::V6(_) => IpAddr::V6(Ipv6Addr::UNSPECIFIED),
111 }
112}
113
114#[cfg(any(test, feature = "internal-benchmarks", fuzzing))]
130pub(super) fn ensure_session_rtc_state(
131 users: &mut SessionStore,
132 session_key: &TransportSessionKey,
133 candidate_addr: SocketAddr,
134 max_bitrate_out: Bitrate,
135) -> Result<bool, TransportAdapterError> {
136 let profile = RtpProfile::compile(MediaCodecFlags::default(), CodecPreferences::default())?;
137 ensure_session_rtc_state_with_stats_interval(
138 users,
139 Arc::from("test-room"),
140 session_key,
141 candidate_addr,
142 max_bitrate_out,
143 &profile,
144 None,
145 )
146}
147
148pub(super) fn ensure_session_rtc_state_with_stats_interval(
149 users: &mut SessionStore,
150 room_id: Arc<str>,
151 session_key: &TransportSessionKey,
152 candidate_addr: SocketAddr,
153 max_bitrate_out: Bitrate,
154 profile: &RtpProfile,
155 stats_interval: Option<Duration>,
156) -> Result<bool, TransportAdapterError> {
157 if users.contains_key(session_key) {
158 return Ok(false);
159 }
160 let started_at = Instant::now();
161 let mut config = profile
162 .session_config()
163 .set_send_buffer_video(RTX_CACHE_MAX_PACKETS);
164 if let Some(stats_interval) = stats_interval {
165 config = config.set_stats_interval(Some(stats_interval));
166 }
167 let mut rtc = config
168 .enable_bwe(Some(Str0mBitrate::bps(max_bitrate_out.as_bps())))
169 .set_ice_lite(true)
170 .build(started_at);
171 let candidate = Candidate::host(candidate_addr, webrtc::IceTransport::Udp.as_str())
172 .map_err(|_error| TransportAdapterError::TransportUnavailable)?;
173 if rtc.add_local_candidate(candidate).is_none() {
174 return Err(TransportAdapterError::TransportUnavailable);
175 }
176 let local_ice_ufrag = rtc.direct_api().local_ice_credentials().ufrag;
177 users.insert(
178 session_key.clone(),
179 RtcSessionState {
180 room_id,
181 rtc,
182 started_at,
183 rtcp_ingress_budget: super::state::RtcpIngressBudget::new(started_at),
184 defer_rtx_expiry: false,
185 pending_rtp_input: None,
186 nack_totals: super::state::RtcNackTotals::default(),
187 egress_bitrate: Arc::new(MediaBitrateCounter::new(started_at)),
188 local_ice_ufrag,
189 #[cfg(test)]
190 max_bitrate_in: None,
191 #[cfg(test)]
192 max_bitrate_out: Some(max_bitrate_out),
193 receiver_bwe_target: None,
194 #[cfg(test)]
195 receiver_bwe_str0m_update_count: 0,
196 dtls_started: false,
197 packet_loop_dirty: false,
198 sdp_negotiation: SessionSdpNegotiationState::default(),
199 consumer_streams: ConsumerStreamStore::default(),
200 #[cfg(test)]
201 last_local_write: None,
202 },
203 );
204 Ok(true)
205}