1use std::sync::Arc;
11
12use o_sfu_rfc::webrtc::sdp;
13use o_sfu_router::rtp::MediaStream as RouterRtpParameters;
14use str0m::{
15 change::{SdpAnswer, SdpOffer},
16 media::{KeyframeRequestKind, MediaKind, Rid},
17};
18use tokio::sync::{mpsc, oneshot};
19
20use super::{
21 codec::{ParsedAnswerRids, RepairSummary, validate_answer_sdp},
22 relay_registry::{RelayPacketMailbox, RelayTargetId},
23 route_control::PacketLayerGate,
24};
25use crate::engine::{
26 media_transport::{
27 ActiveSpeakerSource, AppliedSessionAnswer, ConsumerRouteControl,
28 ConsumerRouteControlOutcome, ProducerRouteControl, ReceiverBweTargetUpdate, SessionOffer,
29 SessionUploadSlot, SourceActivityUpdate, TransportAdapterError, TransportConsumerRoute,
30 TransportMediaId, TransportResult, TransportSessionKey, TransportSourceDiagnosticsSnapshot,
31 TransportSourceKey,
32 },
33 metrics::{RtcMetricsRecorder, RtcRemoteControlDropKind, RtcRemotePacketGateConvergence},
34};
35
36#[derive(Debug, Clone)]
47pub struct RemoteSourceControl {
48 tx: mpsc::Sender<RtcWorkerCommand>,
49 target_id: RelayTargetId,
50 rtc_metrics: Arc<RtcMetricsRecorder>,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub(super) enum RemoteControlSendOutcome {
55 Forwarded,
56 Full,
57 Closed,
58}
59
60impl RemoteSourceControl {
61 pub(super) fn new(
63 tx: mpsc::Sender<RtcWorkerCommand>,
64 target_id: RelayTargetId,
65 rtc_metrics: Arc<RtcMetricsRecorder>,
66 ) -> Self {
67 Self {
68 tx,
69 target_id,
70 rtc_metrics,
71 }
72 }
73
74 pub(super) fn request_kf(
79 &self,
80 source: &TransportSourceKey,
81 rid: Option<Rid>,
82 kind: KeyframeRequestKind,
83 ) -> RemoteControlSendOutcome {
84 self.send_command(
85 RouteControlRequest::RequestRemoteKeyframe {
86 source: source.clone(),
87 target_id: self.target_id,
88 rid,
89 kind,
90 },
91 RtcRemoteControlDropKind::Keyframe,
92 )
93 }
94
95 pub(super) fn set_pkt_gate(
97 &self,
98 source: &TransportSourceKey,
99 packet_gate: PacketLayerGate,
100 ) -> bool {
101 self.send_command(
102 RouteControlRequest::SetRemoteSourcePacketGate {
103 source: source.clone(),
104 target_id: self.target_id,
105 packet_gate,
106 },
107 RtcRemoteControlDropKind::PacketGate,
108 ) == RemoteControlSendOutcome::Forwarded
109 }
110
111 pub(super) fn record_pkt_gate_retry(&self) {
112 self.rtc_metrics
113 .record_rtc_remote_packet_gate_convergence(RtcRemotePacketGateConvergence::Retry);
114 }
115
116 pub(super) fn record_pkt_gate_flushed(&self) {
117 self.rtc_metrics
118 .record_rtc_remote_packet_gate_convergence(RtcRemotePacketGateConvergence::Flushed);
119 }
120
121 #[inline]
122 fn send_command(
123 &self,
124 request: RouteControlRequest,
125 drop_kind: RtcRemoteControlDropKind,
126 ) -> RemoteControlSendOutcome {
127 match self.tx.try_send(RtcWorkerCommand::RouteControl {
128 request,
129 response: None,
130 }) {
131 Ok(()) => RemoteControlSendOutcome::Forwarded,
132 Err(mpsc::error::TrySendError::Full(_)) => {
133 self.rtc_metrics.record_rtc_remote_control_drop(drop_kind);
134 RemoteControlSendOutcome::Full
135 }
136 Err(mpsc::error::TrySendError::Closed(_)) => {
137 self.rtc_metrics.record_rtc_remote_control_drop(drop_kind);
138 RemoteControlSendOutcome::Closed
139 }
140 }
141 }
142}
143
144pub type RtcWorkerResponse<T> = oneshot::Sender<TransportResult<T>>;
149
150pub struct ParsedSessionAnswer {
152 pub(super) answer: SdpAnswer,
153 pub(super) rids: ParsedAnswerRids,
154 pub(super) repair: RepairSummary,
155}
156
157impl ParsedSessionAnswer {
158 pub(in crate::engine::media_transport) fn parse(answer_sdp: &str) -> TransportResult<Self> {
165 let repair = validate_answer_sdp(answer_sdp)?;
166 let answer = SdpAnswer::from_sdp_string(answer_sdp)
167 .map_err(|_error| TransportAdapterError::InvalidInput)?;
168 let rids = ParsedAnswerRids::parse(answer_sdp, &answer);
169 Ok(Self {
170 answer,
171 rids,
172 repair,
173 })
174 }
175}
176
177pub struct RtcSessionOffer {
178 offer: SdpOffer,
179 upload_slots: Vec<SessionUploadSlot>,
180}
181
182impl RtcSessionOffer {
183 pub(super) fn new(offer: SdpOffer, upload_slots: Vec<SessionUploadSlot>) -> Self {
184 Self {
185 offer,
186 upload_slots,
187 }
188 }
189
190 pub(in crate::engine::media_transport) fn into_session_offer(self) -> SessionOffer {
191 let mut sdp = self.offer.to_sdp_string();
192 let media_line_start = sdp.find("\r\nm=").map_or(sdp.len(), |index| index + 2);
193 sdp.insert_str(media_line_start, sdp::EOC_LINE);
194 SessionOffer::new(sdp).with_upload_slots(self.upload_slots)
195 }
196}
197
198#[derive(Debug)]
199pub enum WorkerMediaControlBatch {
200 ReceiverBwe(Vec<(usize, ReceiverBweTargetUpdate)>),
201 ProducerActivity(Vec<(usize, ProducerRouteControl)>),
202 ConsumerGates {
203 source: TransportSourceKey,
204 updates: Vec<(usize, TransportConsumerRoute, PacketLayerGate)>,
205 },
206 ConsumerFollowUp(Vec<(usize, ConsumerRouteControl)>),
207}
208
209#[derive(Debug)]
210pub enum WorkerMediaControlBatchOutcome {
211 Applied(Vec<TransportResult<()>>),
212 Consumers(Vec<ConsumerRouteControlOutcome>),
213}
214
215pub enum RouteControlRequest {
216 AddRelayTarget {
217 source: TransportSourceKey,
218 target_id: RelayTargetId,
219 target: RelayPacketMailbox,
220 },
221 RemoveRelayTarget {
222 source: TransportSourceKey,
223 target_id: RelayTargetId,
224 },
225 SetRelayTargetActive {
226 source: TransportSourceKey,
227 target_id: RelayTargetId,
228 active: bool,
229 },
230 SetRemoteSourceActivity {
231 source: TransportSourceKey,
232 update: SourceActivityUpdate,
233 },
234 RequestRemoteKeyframe {
235 source: TransportSourceKey,
236 target_id: RelayTargetId,
237 rid: Option<Rid>,
238 kind: KeyframeRequestKind,
239 },
240 SetRemoteSourcePacketGate {
241 source: TransportSourceKey,
242 target_id: RelayTargetId,
243 packet_gate: PacketLayerGate,
244 },
245}
246
247pub enum RtcWorkerCommand {
254 CreateInitialSessionOffer {
260 room_id: Arc<str>,
261 session_key: TransportSessionKey,
262 response: RtcWorkerResponse<RtcSessionOffer>,
263 },
264 CreateSessionRenegotiationOffer {
271 session_key: TransportSessionKey,
272 response: RtcWorkerResponse<RtcSessionOffer>,
273 },
274 ActiveSpeakerSourceSnapshot {
279 response: RtcWorkerResponse<Vec<ActiveSpeakerSource>>,
280 },
281 SourceDiagnosticsSnapshot {
283 transport_media_ids: Vec<TransportMediaId>,
284 response: RtcWorkerResponse<TransportSourceDiagnosticsSnapshot>,
285 },
286 ApplySessionAnswer {
292 session_key: TransportSessionKey,
293 answer: ParsedSessionAnswer,
294 response: RtcWorkerResponse<AppliedSessionAnswer>,
295 },
296 CloseSession {
301 session_key: TransportSessionKey,
302 response: RtcWorkerResponse<()>,
303 },
304 RemoveMedia {
311 session_key: TransportSessionKey,
312 transport_media_id: TransportMediaId,
313 response: RtcWorkerResponse<()>,
314 },
315 #[cfg(test)]
321 ResolveNegotiatedProducerParameters {
322 session_key: TransportSessionKey,
323 transport_media_id: TransportMediaId,
324 response: RtcWorkerResponse<RouterRtpParameters>,
325 },
326 ResolveMediaMid {
331 transport_media_id: TransportMediaId,
332 response: RtcWorkerResponse<Option<String>>,
333 },
334 AddRecvMedia {
342 session_key: TransportSessionKey,
343 media_kind: MediaKind,
344 rtp_parameters: RouterRtpParameters,
345 response: RtcWorkerResponse<TransportMediaId>,
346 },
347 AddSendMedia {
355 consumer_key: TransportSessionKey,
356 media_kind: MediaKind,
357 source: TransportSourceKey,
358 remote_source_control: Option<RemoteSourceControl>,
359 consumer_rtp_parameters: RouterRtpParameters,
360 active: bool,
361 response: RtcWorkerResponse<TransportMediaId>,
362 },
363 ApplyMediaControlBatch {
364 batch: WorkerMediaControlBatch,
365 response: RtcWorkerResponse<WorkerMediaControlBatchOutcome>,
366 },
367 RouteControl {
368 request: RouteControlRequest,
369 response: Option<RtcWorkerResponse<()>>,
370 },
371}