Skip to content
File

Blob: archive/str0m-probe/src/driver.rs

rust208 lines
1//! One owner drives networking, protocol timers, media, and channel writes.
2 
3use serde::Serialize;
4use std::{collections::HashMap, error::Error, net::UdpSocket, time::Instant};
5use str0m::{
6 Candidate, Event, Input, Output, Rtc,
7 change::{SdpAnswer, SdpPendingOffer},
8 channel::ChannelId,
9 crypto::dtls::DtlsCert,
10 media::{Direction, Frequency, MediaKind, MediaTime},
11 net::{Protocol, Receive},
12};
13 
14pub type Result<T> = std::result::Result<T, Box<dyn Error>>;
15 
16#[derive(Default, Debug, Serialize)]
17pub struct Stats {
18 pub audio_sent: u32,
19 pub robot_sent: u32,
20 pub spectrum_sent: u32,
21 pub commands_received: u32,
22 pub application_datagrams: u32,
23 pub deliberately_dropped: u32,
24 pub rtp_datagrams: u32,
25}
26 
27pub struct Driver {
28 rtc: Rtc,
29 socket: UdpSocket,
30 pending: Option<SdpPendingOffer>,
31 mid: str0m::media::Mid,
32 channels: HashMap<String, (u16, ChannelId)>,
33 pub events: Vec<serde_json::Value>,
34 pub stats: Stats,
35 pub drop_every: u32,
36}
37 
38impl Driver {
39 pub fn new(ip: &str, certificate: DtlsCert) -> Result<(Self, String)> {
40 let socket = UdpSocket::bind((ip, 0))?;
41 socket.set_nonblocking(true)?;
42 let mut rtc = crate::rtc(certificate);
43 rtc.add_local_candidate(Candidate::host(socket.local_addr()?, "udp")?);
44 while !matches!(rtc.poll_output()?, Output::Timeout(_)) {}
45 let mut change = rtc.sdp_api();
46 let mid = change.add_media(MediaKind::Audio, Direction::SendOnly, None, None, None);
47 let bootstrap = change.add_channel_with_config(crate::channel("bootstrap", 0, true));
48 let (offer, pending) = change.apply().ok_or("no initial offer")?;
49 let mut driver = Self {
50 rtc,
51 socket,
52 pending: Some(pending),
53 mid,
54 channels: HashMap::from([("bootstrap".to_owned(), (0, bootstrap))]),
55 events: Vec::new(),
56 stats: Stats::default(),
57 drop_every: 0,
58 };
59 driver.drain()?;
60 Ok((driver, offer.to_sdp_string()))
61 }
62 
63 pub fn mid(&self) -> String {
64 self.mid.to_string()
65 }
66 
67 pub fn answer(&mut self, sdp: &str) -> Result<()> {
68 let pending = self.pending.take().ok_or("answer already applied")?;
69 self.rtc
70 .sdp_api()
71 .accept_answer(pending, SdpAnswer::from_sdp_string(sdp)?)?;
72 self.drain()
73 }
74 
75 pub fn add_channel(&mut self, label: &str, id: u16, reliable: bool) -> Result<()> {
76 let cid = self
77 .rtc
78 .direct_api()
79 .create_data_channel(crate::channel(label, id, reliable));
80 self.channels.insert(label.to_owned(), (id, cid));
81 self.drain()
82 }
83 
84 pub fn pump(&mut self) -> Result<()> {
85 let mut buffer = [0u8; 2048];
86 for _ in 0..32 {
87 let (length, source) = match self.socket.recv_from(&mut buffer) {
88 Ok(value) => value,
89 Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => break,
90 Err(error) => return Err(error.into()),
91 };
92 let receive = Receive {
93 proto: Protocol::Udp,
94 source,
95 destination: self.socket.local_addr()?,
96 contents: buffer[..length].try_into()?,
97 };
98 self.rtc
99 .handle_input(Input::Receive(Instant::now(), receive))?;
100 self.drain()?;
101 }
102 self.rtc.handle_input(Input::Timeout(Instant::now()))?;
103 self.drain()
104 }
105 
106 pub fn audio(&mut self, _frame: u32) -> Result<()> {
107 let writer = self
108 .rtc
109 .writer(self.mid)
110 .ok_or("audio writer unavailable")?;
111 let pt = writer
112 .payload_params()
113 .next()
114 .ok_or("audio codec missing")?
115 .pt();
116 writer.write(
117 pt,
118 Instant::now(),
119 MediaTime::new(
120 u64::from(self.stats.audio_sent) * 960,
121 Frequency::FORTY_EIGHT_KHZ,
122 ),
123 [0xf8, 0xff, 0xfe],
124 )?;
125 self.stats.audio_sent += 1;
126 self.drain()
127 }
128 
129 pub fn data(&mut self, label: &str, sequence: u32) -> Result<()> {
130 let (_, cid) = *self.channels.get(label).ok_or("unknown channel")?;
131 let mut channel = self.rtc.channel(cid).ok_or("channel is not open")?;
132 let accepted = if label == "robot" {
133 let payload =
134 serde_json::json!({"kind": "telemetry", "sequence": sequence}).to_string();
135 channel.write(false, payload.as_bytes())?
136 } else {
137 channel.write(true, &sequence.to_le_bytes())?
138 };
139 if !accepted {
140 return Err("channel backpressure".into());
141 }
142 if label == "robot" {
143 self.stats.robot_sent += 1;
144 } else {
145 self.stats.spectrum_sent += 1;
146 }
147 self.drain()
148 }
149 
150 fn drain(&mut self) -> Result<()> {
151 loop {
152 match self.rtc.poll_output()? {
153 Output::Timeout(_) => break,
154 Output::Transmit(packet) => {
155 if packet.contents.len() >= 2
156 && packet.contents[0] & 0xc0 == 0x80
157 && !(192..=223).contains(&packet.contents[1])
158 {
159 self.stats.rtp_datagrams += 1;
160 }
161 // DTLS 1.2 application records; enabled only after setup.
162 let application = packet.contents.first() == Some(&23);
163 if application {
164 self.stats.application_datagrams += 1;
165 }
166 if application
167 && self.drop_every != 0
168 && self.stats.application_datagrams % self.drop_every == 0
169 {
170 self.stats.deliberately_dropped += 1;
171 } else {
172 self.socket.send_to(&packet.contents, packet.destination)?;
173 }
174 }
175 Output::Event(Event::Connected) => {
176 self.events.push(serde_json::json!({"event": "connected"}))
177 }
178 Output::Event(Event::ChannelOpen(cid, label)) => {
179 let id = self
180 .channels
181 .values()
182 .find(|(_, value)| *value == cid)
183 .map(|(id, _)| *id);
184 self.events.push(
185 serde_json::json!({"event": "channel_open", "label": label, "id": id}),
186 );
187 }
188 Output::Event(Event::ChannelData(data)) => {
189 let is_robot = self
190 .channels
191 .get("robot")
192 .is_some_and(|(_, cid)| *cid == data.id);
193 if is_robot {
194 self.stats.commands_received += 1;
195 let mut channel = self
196 .rtc
197 .channel(data.id)
198 .ok_or("reply channel unavailable")?;
199 channel.write(data.binary, &data.data)?;
200 }
201 }
202 Output::Event(_) => {}
203 }
204 }
205 Ok(())
206 }
207}