Skip to main content

o_sfu_core/engine/media_transport/
build.rs

1//! media transport construction and startup validation
2
3use std::sync::Arc;
4
5use thiserror::Error;
6
7use super::{
8    MediaTransport, SourcePolicySignal,
9    config::{MediaTransportConfig, MediaTransportDeps},
10    rtc::{RtcWorker, RtpProfile},
11};
12use crate::{MediaWorkerId, RtcUdpIoBackend};
13
14/// Per-worker [`TransportMediaId`](crate::engine::media_transport::TransportMediaId)
15/// allocation stride.
16///
17/// IDs have no worker namespace. Each `RtcWorker` may allocate at most this
18/// many IDs during its lifetime within one `MediaTransport`.
19const MEDIA_ID_STRIDE: u64 = 1_000_000_000;
20
21impl MediaTransport {
22    /// builds the runtime media transport from owner configuration and process services
23    ///
24    /// validation completes before worker startup and every worker has a bound
25    /// socket when this function returns
26    ///
27    /// # Errors
28    ///
29    /// returns [`MediaTransportBuildError`] when worker topology or the
30    /// code-controlled RTP profile is invalid, the selected UDP backend is
31    /// unavailable or a worker cannot start
32    pub fn build(
33        config: MediaTransportConfig,
34        deps: MediaTransportDeps,
35    ) -> Result<Self, MediaTransportBuildError> {
36        if config.rtc_udp_io_backend == RtcUdpIoBackend::IoUring && !cfg!(target_os = "linux") {
37            return Err(MediaTransportBuildError::UnsupportedUdpIoBackend {
38                backend: config.rtc_udp_io_backend,
39            });
40        }
41        if config.worker_count == 0 {
42            return Err(MediaTransportBuildError::InvalidWorkerCount);
43        }
44        let worker_ranges = config
45            .rtc_port_range
46            .split_for_workers(config.worker_count)
47            .ok_or(MediaTransportBuildError::InvalidPortSplit {
48                worker_count: config.worker_count,
49                port_count: config.rtc_port_range.port_count(),
50            })?;
51        let profile = Arc::new(
52            RtpProfile::compile(config.codec_flags, config.codec_preferences)
53                .map_err(|_error| MediaTransportBuildError::InvalidRtpProfile)?,
54        );
55        let source_policy_signal = SourcePolicySignal::default();
56        let workers: Arc<[_]> = (0_u16..u16::MAX)
57            .zip(worker_ranges)
58            .map(|(worker_index, range)| {
59                // `start` returns only after socket binding, so the completed
60                // transport cannot publish a worker whose first command races I/O setup.
61                RtcWorker::start(
62                    &config,
63                    Arc::clone(&profile),
64                    range,
65                    &deps,
66                    source_policy_signal.clone(),
67                    u64::from(worker_index) * MEDIA_ID_STRIDE,
68                    MediaWorkerId::from_raw(usize::from(worker_index)),
69                )
70                .map_err(|_error| MediaTransportBuildError::WorkerStartup {
71                    worker_index: usize::from(worker_index),
72                })
73            })
74            .collect::<Result<_, _>>()?;
75        Ok(Self {
76            workers,
77            profile,
78            metrics: deps.metrics,
79            #[cfg(test)]
80            media_control_batches: Arc::default(),
81            #[cfg(any(test, feature = "testing-transport"))]
82            source_diagnostics_requests: Arc::default(),
83            source_policy_signal,
84        })
85    }
86}
87
88/// invalid construction inputs for the media transport
89#[derive(Debug, Error, Clone, Copy, PartialEq, Eq)]
90pub enum MediaTransportBuildError {
91    /// a transport cannot be built without at least one RTC worker
92    #[error("media transport worker count must be at least one")]
93    InvalidWorkerCount,
94    /// the configured UDP range cannot provide one port to every worker
95    #[error(
96        "media transport cannot split {port_count} UDP ports across {worker_count} media workers"
97    )]
98    InvalidPortSplit {
99        worker_count: usize,
100        port_count: u16,
101    },
102    /// the selected UDP I/O backend is not available on this build target
103    #[error("rtc UDP I/O backend `{backend}` is not supported on this target")]
104    UnsupportedUdpIoBackend { backend: RtcUdpIoBackend },
105    /// the code-controlled RTC profile cannot be projected for router policy
106    #[error("media transport RTP profile is invalid")]
107    InvalidRtpProfile,
108    /// one worker could not create its runtime or bind its assigned UDP range
109    #[error("media transport worker {worker_index} failed to start")]
110    WorkerStartup { worker_index: usize },
111}