File
Blob: firmware/vendor/str0m/src/media/event.rs
| 1 | use std::fmt; |
| 2 | use std::ops::RangeInclusive; |
| 3 | use std::sync::Arc; |
| 4 | use std::time::Instant; |
| 5 | |
| 6 | use crate::packet::MediaKind; |
| 7 | use crate::rtp_::{Direction, ExtensionValues, MediaTime, Mid, Pt, Rid, SenderInfo, SeqNo, Ssrc}; |
| 8 | use crate::sdp::SimulcastLayer as SdpSimulcastLayer; |
| 9 | use crate::sdp::{RestrictionId, Simulcast as SdpSimulcast, SimulcastGroups as SdpSimulcastGroups}; |
| 10 | |
| 11 | use super::PayloadParams; |
| 12 | use crate::format::CodecExtra; |
| 13 | |
| 14 | impl From<&SdpSimulcastLayer> for SimulcastLayer { |
| 15 | fn from(layer: &SdpSimulcastLayer) -> Self { |
| 16 | SimulcastLayer { |
| 17 | rid: Rid::from(layer.restriction_id.0.as_ref()), |
| 18 | attributes: layer.attributes.clone(), |
| 19 | } |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | impl From<&SimulcastLayer> for SdpSimulcastLayer { |
| 24 | fn from(layer: &SimulcastLayer) -> Self { |
| 25 | SdpSimulcastLayer { |
| 26 | restriction_id: RestrictionId::new_active(layer.rid.to_string()), |
| 27 | attributes: layer.attributes.clone(), |
| 28 | } |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | impl From<SdpSimulcast> for Simulcast { |
| 33 | fn from(s: SdpSimulcast) -> Self { |
| 34 | let send = s.send.iter().map(Into::into).collect(); |
| 35 | let recv = s.recv.iter().map(Into::into).collect(); |
| 36 | |
| 37 | Simulcast { send, recv } |
| 38 | } |
| 39 | } |
| 40 | |
| 41 | /// A new media appeared in an Rtc session. |
| 42 | /// |
| 43 | /// This event fires both for negotiations triggered by a remote or local offer. |
| 44 | /// |
| 45 | /// Does not fire for application media (data channel). |
| 46 | #[derive(Debug, PartialEq, Eq)] |
| 47 | pub struct MediaAdded { |
| 48 | /// Identifier of the new media. |
| 49 | pub mid: Mid, |
| 50 | |
| 51 | /// The kind of media carried. |
| 52 | pub kind: MediaKind, |
| 53 | |
| 54 | /// Current direction. |
| 55 | pub direction: Direction, |
| 56 | |
| 57 | /// If simulcast is configured, this holds the Rids. |
| 58 | /// |
| 59 | /// `a=simulcast:send h;l` |
| 60 | pub simulcast: Option<Simulcast>, |
| 61 | } |
| 62 | |
| 63 | /// A change happening during an SDP re-negotiation. |
| 64 | /// |
| 65 | /// This event fires both for re-negotiations triggered by a remote or local offer. |
| 66 | /// |
| 67 | /// Does not fire for application media (data channel). |
| 68 | #[derive(Debug, PartialEq, Eq)] |
| 69 | pub struct MediaChanged { |
| 70 | /// Identifier of the media. |
| 71 | pub mid: Mid, |
| 72 | |
| 73 | /// Current direction. |
| 74 | pub direction: Direction, |
| 75 | } |
| 76 | |
| 77 | /// Simplified information about the simulcast config from SDP. |
| 78 | /// |
| 79 | /// The [full spec][1] covers many cases that are not used by simple simulcast. |
| 80 | /// |
| 81 | /// [1]: https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-sdp-simulcast-14 |
| 82 | #[derive(Debug, PartialEq, Eq, Clone)] |
| 83 | pub struct Simulcast { |
| 84 | /// The layers used for sending simulcast. |
| 85 | pub send: Vec<SimulcastLayer>, |
| 86 | /// The layers used for receiving simulcast. |
| 87 | pub recv: Vec<SimulcastLayer>, |
| 88 | } |
| 89 | |
| 90 | impl Simulcast { |
| 91 | /// Create a new struct with no layers |
| 92 | pub fn new() -> Simulcast { |
| 93 | Simulcast { |
| 94 | send: vec![], |
| 95 | recv: vec![], |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | impl Simulcast { |
| 101 | /// Add a send layer |
| 102 | pub fn add_send_layer(&mut self, layer: SimulcastLayer) { |
| 103 | self.send.push(layer); |
| 104 | } |
| 105 | |
| 106 | /// Add a receive layer |
| 107 | pub fn add_recv_layer(&mut self, layer: SimulcastLayer) { |
| 108 | self.recv.push(layer); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | /// A simulcast layer which has a RID and optional attributes |
| 113 | #[derive(Debug, PartialEq, Eq, Clone)] |
| 114 | pub struct SimulcastLayer { |
| 115 | /// The layer's rid |
| 116 | pub rid: Rid, |
| 117 | /// The layer's attributes per RFC 8851 |
| 118 | pub attributes: Option<Vec<(String, String)>>, |
| 119 | } |
| 120 | |
| 121 | impl SimulcastLayer { |
| 122 | /// Creates a new layer with the rid |
| 123 | pub fn new(rid: &str) -> SimulcastLayer { |
| 124 | SimulcastLayer { |
| 125 | rid: Rid::from(rid), |
| 126 | attributes: None, |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | /// Create a new layer builder for setting attributes |
| 131 | pub fn new_with_attributes(rid: &str) -> SimulcastLayerBuilder { |
| 132 | SimulcastLayerBuilder { |
| 133 | rid: Rid::from(rid), |
| 134 | attributes: vec![], |
| 135 | } |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | /// A builder which is used to populate layer attributes |
| 140 | pub struct SimulcastLayerBuilder { |
| 141 | rid: Rid, |
| 142 | // We could use a HashMap but that doesn't preserve order - which we need for tests (to validate the |
| 143 | // resulting SDP). BTreeMap seems like overkill for 4-6 keys, so does HashMap, actually. A simple |
| 144 | // vector of pairs is perfect: preserves order and is efficient enough. |
| 145 | attributes: Vec<(String, String)>, |
| 146 | } |
| 147 | |
| 148 | impl SimulcastLayerBuilder { |
| 149 | /// Maximum video width |
| 150 | pub fn max_width(mut self, max_width: u32) -> Self { |
| 151 | self.update_or_insert("max-width", max_width.to_string()); |
| 152 | self |
| 153 | } |
| 154 | |
| 155 | /// Maximum video height |
| 156 | pub fn max_height(mut self, max_height: u32) -> Self { |
| 157 | self.update_or_insert("max-height", max_height.to_string()); |
| 158 | self |
| 159 | } |
| 160 | |
| 161 | /// Maximum bitrate, in bits per second (not kilobits) |
| 162 | pub fn max_br(mut self, max_br: u32) -> Self { |
| 163 | self.update_or_insert("max-br", max_br.to_string()); |
| 164 | self |
| 165 | } |
| 166 | |
| 167 | /// Maximum frame rate, in frames per second, or 0 if none |
| 168 | pub fn max_fps(mut self, max_fps: u32) -> Self { |
| 169 | self.update_or_insert("max-fps", max_fps.to_string()); |
| 170 | self |
| 171 | } |
| 172 | |
| 173 | /// A custom attribute |
| 174 | pub fn custom(mut self, key: &str, value: &str) -> Self { |
| 175 | self.update_or_insert(key, value.to_string()); |
| 176 | self |
| 177 | } |
| 178 | |
| 179 | /// Build the layer |
| 180 | pub fn build(self) -> SimulcastLayer { |
| 181 | SimulcastLayer { |
| 182 | rid: self.rid, |
| 183 | attributes: if self.attributes.is_empty() { |
| 184 | None |
| 185 | } else { |
| 186 | Some(self.attributes) |
| 187 | }, |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | fn update_or_insert(&mut self, key: &str, value: String) { |
| 192 | for (k, v) in &mut self.attributes { |
| 193 | if k == key { |
| 194 | *v = value; |
| 195 | return; |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | self.attributes.push((key.to_string(), value)); |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | impl Simulcast { |
| 204 | pub(crate) fn into_sdp(self) -> SdpSimulcast { |
| 205 | SdpSimulcast { |
| 206 | send: SdpSimulcastGroups(self.send.iter().map(Into::into).collect()), |
| 207 | recv: SdpSimulcastGroups(self.recv.iter().map(Into::into).collect()), |
| 208 | is_munged: false, |
| 209 | } |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | /// Video or audio data from the remote peer. |
| 214 | /// |
| 215 | /// This is obtained via [`Event::MediaData`][crate::Event::MediaData]. |
| 216 | #[derive(PartialEq, Eq)] |
| 217 | pub struct MediaData { |
| 218 | /// Identifier of the media in the session this media belongs to. |
| 219 | pub mid: Mid, |
| 220 | |
| 221 | /// Payload type (PT) tells which negotiated codec is being used. Each media |
| 222 | /// can carry different codecs, the payload type can theoretically change |
| 223 | /// from one packet to the next. |
| 224 | pub pt: Pt, |
| 225 | |
| 226 | /// Rtp Stream Id (RID) identifies an RTP stream without referring to its |
| 227 | /// Synchronization Source (SSRC). |
| 228 | /// |
| 229 | /// This is a newer standard that is sometimes used in WebRTC to identify |
| 230 | /// a stream. Specifically when using Simulcast in Chrome. |
| 231 | pub rid: Option<Rid>, |
| 232 | |
| 233 | /// Parameters for the codec. This is used to match incoming PT to outgoing PT. |
| 234 | pub params: PayloadParams, |
| 235 | |
| 236 | /// The RTP media time of this packet. Media time is described as a numerator/denominator |
| 237 | /// quantity. The numerator is the timestamp field from the RTP header, the denominator |
| 238 | /// depends on whether this is an audio or video packet. |
| 239 | /// |
| 240 | /// For audio the timebase is often 48kHz for video it is 90kHz. |
| 241 | pub time: MediaTime, |
| 242 | |
| 243 | /// The time of the [`Input::Receive`][crate::Input::Receive] of the first packet |
| 244 | /// that caused this MediaData. |
| 245 | /// |
| 246 | /// In simple SFU setups this can be used as wallclock for [`Writer::write`][crate::media::Writer]. |
| 247 | pub network_time: Instant, |
| 248 | |
| 249 | /// The (RTP) sequence numbers that made up this data. |
| 250 | pub seq_range: RangeInclusive<SeqNo>, |
| 251 | |
| 252 | /// Whether the data is contiguous from the one just previously emitted. If this is false, |
| 253 | /// we got an interruption in RTP packets, and the data may or may not be usable in a decoder |
| 254 | /// without requesting a new keyframe. |
| 255 | /// |
| 256 | /// For audio this flag most likely doesn't matter. |
| 257 | pub contiguous: bool, |
| 258 | |
| 259 | /// The actual packet data a.k.a Frame. |
| 260 | /// |
| 261 | /// Bigger frames don't fit in one UDP packet, thus WebRTC RTP is chopping up codec |
| 262 | /// transmission units into smaller parts. |
| 263 | /// |
| 264 | /// This data is a full depayloaded Frame. |
| 265 | pub data: Arc<[u8]>, |
| 266 | |
| 267 | /// RTP header extensions for this media data. This is taken from the |
| 268 | /// first RTP header. |
| 269 | pub ext_vals: ExtensionValues, |
| 270 | |
| 271 | /// Additional codec specific information |
| 272 | pub codec_extra: CodecExtra, |
| 273 | |
| 274 | /// Sender information from the most recent Sender Report(SR). |
| 275 | /// |
| 276 | /// If no Sender Report(SR) has been received this is [`None`]. |
| 277 | pub last_sender_info: Option<SenderInfo>, |
| 278 | |
| 279 | /// First packet of a talkspurt, that is the first packet after a silence period during |
| 280 | /// which packets have not been transmitted contiguously. |
| 281 | /// |
| 282 | /// For audio only when dtx or silence suppression is enabled. |
| 283 | pub audio_start_of_talk_spurt: bool, |
| 284 | } |
| 285 | |
| 286 | impl MediaData { |
| 287 | /// Return true if MediaData is keyframe independently of Codec |
| 288 | pub fn is_keyframe(&self) -> bool { |
| 289 | match self.codec_extra { |
| 290 | CodecExtra::None => false, |
| 291 | CodecExtra::H264(h264_extra) => h264_extra.is_keyframe, |
| 292 | CodecExtra::H265(h265_extra) => h265_extra.is_keyframe, |
| 293 | CodecExtra::H266(h266_extra) => h266_extra.is_keyframe, |
| 294 | CodecExtra::Vp8(vp8_extra) => vp8_extra.is_keyframe, |
| 295 | CodecExtra::Vp9(vp9_extra) => vp9_extra.is_keyframe, |
| 296 | CodecExtra::Av1(av1_extra) => av1_extra.is_keyframe, |
| 297 | } |
| 298 | } |
| 299 | } |
| 300 | |
| 301 | /// Details for an incoming a keyframe request (PLI or FIR). |
| 302 | /// |
| 303 | /// This is obtained via the [`Event::KeyframeRequest`][crate::Event::KeyframeRequest]. |
| 304 | /// |
| 305 | /// Sending a keyframe request is done via [`Media::request_keyframe()`][crate::media::Media]. |
| 306 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 307 | pub struct KeyframeRequest { |
| 308 | /// The media identifier this keyframe request is for. |
| 309 | pub mid: Mid, |
| 310 | |
| 311 | /// Rid the keyframe request is for. Relevant when doing simulcast. |
| 312 | pub rid: Option<Rid>, |
| 313 | |
| 314 | /// The kind of keyframe request (PLI or FIR). |
| 315 | pub kind: KeyframeRequestKind, |
| 316 | } |
| 317 | |
| 318 | /// Type of keyframe request. |
| 319 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 320 | pub enum KeyframeRequestKind { |
| 321 | /// Picture Loss Indication (PLI) is a less severe keyframe request that can be |
| 322 | /// automatically generated by an SFU or by the end peer. |
| 323 | Pli, |
| 324 | |
| 325 | /// Full Intra Request (FIR) is a more severe keyframe request that should only |
| 326 | /// be used when it's impossible for an end peer to show a video stream. |
| 327 | Fir, |
| 328 | } |
| 329 | |
| 330 | /// Incoming application-specific Payload-Specific Feedback (PSFB FMT=15, PT=206). |
| 331 | /// |
| 332 | /// Emitted when a non-REMB FMT=15 RTCP PSFB message is received. The payload |
| 333 | /// is opaque and application-defined (RFC 4585 Section 6.4). |
| 334 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 335 | pub struct AppSpecificFeedback { |
| 336 | /// SSRC of the sender of this feedback message. |
| 337 | pub sender_ssrc: Ssrc, |
| 338 | /// SSRC of the media source this feedback relates to. |
| 339 | pub media_ssrc: Ssrc, |
| 340 | /// Application-dependent payload. |
| 341 | pub payload: Arc<[u8]>, |
| 342 | } |
| 343 | |
| 344 | /// Incoming feedback from a sender. |
| 345 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 346 | pub struct SenderFeedback { |
| 347 | /// The media identifier this feedback is for. |
| 348 | pub mid: Mid, |
| 349 | |
| 350 | /// Rid the feedback is for. Relevant when doing simulcast. |
| 351 | pub rid: Option<Rid>, |
| 352 | |
| 353 | /// When the RTCP packet containing the included [`SenderInfo`] was received. |
| 354 | pub received_at: Instant, |
| 355 | |
| 356 | /// Information about the sender of this report. |
| 357 | pub sender_info: SenderInfo, |
| 358 | } |
| 359 | |
| 360 | impl fmt::Debug for MediaData { |
| 361 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 362 | f.debug_struct("MediaData") |
| 363 | .field("mid", &self.mid) |
| 364 | .field("pt", &self.pt) |
| 365 | .field("rid", &self.rid) |
| 366 | .field("time", &self.time) |
| 367 | .field("len", &self.data.len()) |
| 368 | .finish() |
| 369 | } |
| 370 | } |