o_sfu_core/engine/media_transport/
build.rs1use std::{io, net::TcpListener, sync::Arc};
4
5use thiserror::Error;
6use tracing::info;
7
8use super::{
9 MediaTransport, SourcePolicySignal,
10 config::{MediaTransportConfig, MediaTransportDeps},
11 rtc::{RtcWorker, RtpProfile, TcpAcceptor},
12};
13use crate::{
14 MediaWorkerId, RtcUdpIoBackend,
15 engine::media_transport::rtc::{UfragWorkerMap, WorkerAssignment},
16};
17
18const MEDIA_ID_STRIDE: u64 = 1_000_000_000;
24
25impl MediaTransport {
26 pub fn build(
38 mut config: MediaTransportConfig,
39 deps: MediaTransportDeps,
40 ) -> Result<Self, MediaTransportBuildError> {
41 if config.rtc_udp_io_backend != RtcUdpIoBackend::Tokio && !cfg!(target_os = "linux") {
42 return Err(MediaTransportBuildError::UnsupportedUdpIoBackend {
43 backend: config.rtc_udp_io_backend,
44 });
45 }
46 if config.worker_count == 0 {
47 return Err(MediaTransportBuildError::InvalidWorkerCount);
48 }
49 if config
51 .media_quality_interval
52 .is_some_and(|interval| interval > MediaTransportConfig::MAX_MEDIA_QUALITY_INTERVAL)
53 {
54 return Err(MediaTransportBuildError::InvalidMediaQualityInterval);
55 }
56 config.media_quality_interval = config
57 .media_quality_interval
58 .filter(|value| !value.is_zero());
59 let worker_ranges = config
60 .rtc_port_range
61 .split_for_workers(config.worker_count)
62 .ok_or(MediaTransportBuildError::InvalidPortSplit {
63 worker_count: config.worker_count,
64 port_count: config.rtc_port_range.port_count(),
65 })?;
66 let profile = Arc::new(
67 RtpProfile::compile(config.codec_flags, config.codec_preferences)
68 .map_err(|_error| MediaTransportBuildError::InvalidRtpProfile)?,
69 );
70 let source_policy_signal = SourcePolicySignal::default();
71 let ufrag_worker_map = UfragWorkerMap::default();
72 let workers: Arc<[_]> = (0_u16..u16::MAX)
73 .zip(worker_ranges)
74 .map(|(worker_index, range)| {
75 RtcWorker::start(
78 &config,
79 Arc::clone(&profile),
80 WorkerAssignment {
81 rtc_port_range: range,
82 media_id_base: u64::from(worker_index) * MEDIA_ID_STRIDE,
83 media_worker_id: MediaWorkerId::from_raw(usize::from(worker_index)),
84 },
85 &deps,
86 source_policy_signal.clone(),
87 ufrag_worker_map.clone(),
88 )
89 .map_err(|_error| MediaTransportBuildError::WorkerStartup {
90 worker_index: usize::from(worker_index),
91 })
92 })
93 .collect::<Result<_, _>>()?;
94 let tcp_acceptor = config
95 .rtc_tcp_config
96 .map(|c| {
97 let addr = c.bind_addr;
98 let listener = TcpListener::bind(addr)?;
99 listener.set_nonblocking(true)?;
100 let acceptor: Result<Arc<TcpAcceptor>, io::Error> = TcpAcceptor::spawn(
101 listener,
102 c.announced_addr,
103 ufrag_worker_map,
104 Arc::clone(&workers),
105 c.max_conn,
106 )
107 .map(Arc::new);
108 info!(
109 "booted rtc TCP listener bind_addr={} candidate_addr={}",
110 addr, c.announced_addr,
111 );
112 acceptor
113 })
114 .transpose()
115 .map_err(|error| MediaTransportBuildError::TcpAcceptorStartup { kind: error.kind() })?;
116 Ok(Self {
117 workers,
118 profile,
119 metrics: deps.metrics,
120 #[cfg(test)]
121 media_control_batches: Arc::default(),
122 #[cfg(any(test, feature = "testing-transport"))]
123 source_diagnostics_requests: Arc::default(),
124 source_policy_signal,
125 _tcp_acceptor: tcp_acceptor,
126 })
127 }
128}
129
130#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
132pub enum MediaTransportBuildError {
133 #[error("media transport worker count must be at least one")]
135 InvalidWorkerCount,
136 #[error(
138 "media transport cannot split {port_count} UDP ports across {worker_count} media workers"
139 )]
140 InvalidPortSplit {
141 worker_count: usize,
142 port_count: u16,
143 },
144 #[error("media-quality interval must not exceed 24 hours")]
146 InvalidMediaQualityInterval,
147 #[error("rtc UDP I/O backend `{backend}` is not supported on this target")]
149 UnsupportedUdpIoBackend { backend: RtcUdpIoBackend },
150 #[error("media transport RTP profile is invalid")]
152 InvalidRtpProfile,
153 #[error("media transport worker {worker_index} failed to start")]
155 WorkerStartup { worker_index: usize },
156 #[error("media transport failed to start the TCP acceptor: {kind}")]
158 TcpAcceptorStartup { kind: io::ErrorKind },
159}