1use std::borrow::Cow;
39
40use o_sfu_rfc::{rtp as rfc_rtp, webrtc as rfc_webrtc};
41pub use rfc_rtp::{HeaderExtensionId, Mid, PayloadType, Rid, Ssrc};
42
43use super::MediaKind;
44
45pub type MediaCodec = rfc_rtp::CodecName;
47
48pub type HeaderExtensionUri = rfc_webrtc::RtpHeaderExtensionUri;
50
51#[derive(Debug, Clone, PartialEq, Eq)]
56pub enum RtcpFeedbackKind {
57 Nack,
60 NackPli,
63 CcmFir,
66 GoogRemb,
68 TransportCc,
70 Other(String),
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct RtcpFeedback {
77 kind: RtcpFeedbackKind,
78 parameter: Option<String>,
79}
80
81impl RtcpFeedback {
82 #[must_use]
83 pub fn new(kind: RtcpFeedbackKind, parameter: Option<String>) -> Self {
84 Self { kind, parameter }
85 }
86
87 #[must_use]
88 pub fn kind(&self) -> &RtcpFeedbackKind {
89 &self.kind
90 }
91
92 #[must_use]
93 pub fn parameter(&self) -> Option<&str> {
94 self.parameter.as_deref()
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
103pub enum CodecSetting {
104 RtxAssociation(PayloadType),
107 H264PacketizationMode(rfc_rtp::h264::PacketizationMode),
109 H264ProfileLevelId(String),
111 Vp9ProfileId(rfc_rtp::Vp9ProfileId),
113 UseInBandFec(bool),
115 Other { key: String, value: String },
117}
118
119impl CodecSetting {
120 #[must_use]
121 pub fn key(&self) -> &str {
122 match self {
123 Self::RtxAssociation(_) => rfc_rtp::fmtp::RTX_ASSOCIATION,
124 Self::H264PacketizationMode(_) => rfc_rtp::fmtp::H264_PACKETIZATION_MODE,
125 Self::H264ProfileLevelId(_) => rfc_rtp::fmtp::H264_PROFILE_LEVEL_ID,
126 Self::Vp9ProfileId(_) => rfc_rtp::fmtp::VP9_PROFILE_ID,
127 Self::UseInBandFec(_) => rfc_rtp::fmtp::OPUS_USE_IN_BAND_FEC,
128 Self::Other { key, .. } => key.as_str(),
129 }
130 }
131
132 #[must_use]
133 pub fn wire_value(&self) -> Cow<'_, str> {
134 match self {
135 Self::RtxAssociation(payload_type) => Cow::Owned(payload_type.value().to_string()),
136 Self::H264PacketizationMode(mode) => Cow::Owned(mode.fmtp_value().to_string()),
137 Self::H264ProfileLevelId(profile_level_id) => Cow::Borrowed(profile_level_id.as_str()),
138 Self::Vp9ProfileId(profile_id) => Cow::Owned(profile_id.value().to_string()),
139 Self::UseInBandFec(enabled) => Cow::Borrowed(if *enabled {
140 rfc_rtp::fmtp::VALUE_ENABLED
141 } else {
142 rfc_rtp::fmtp::VALUE_DISABLED
143 }),
144 Self::Other { value, .. } => Cow::Borrowed(value.as_str()),
145 }
146 }
147}
148
149#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct HeaderExtension {
165 uri: HeaderExtensionUri,
166 id: HeaderExtensionId,
167 encrypt: bool,
168}
169
170impl HeaderExtension {
171 #[must_use]
172 pub fn new(uri: impl Into<HeaderExtensionUri>, id: impl Into<HeaderExtensionId>) -> Self {
173 Self {
174 uri: uri.into(),
175 id: id.into(),
176 encrypt: false,
177 }
178 }
179
180 #[must_use]
181 pub fn with_encryption(mut self, encrypt: bool) -> Self {
182 self.encrypt = encrypt;
183 self
184 }
185
186 #[must_use]
187 pub fn uri_kind(&self) -> &HeaderExtensionUri {
188 &self.uri
189 }
190
191 #[must_use]
192 pub fn id(&self) -> HeaderExtensionId {
193 self.id
194 }
195
196 #[must_use]
197 pub fn uri(&self) -> &str {
198 self.uri.as_str()
199 }
200
201 #[must_use]
202 pub fn encrypt(&self) -> bool {
203 self.encrypt
204 }
205}
206
207#[derive(Debug, Clone, PartialEq, Eq)]
214pub struct MediaCodecCapability {
215 media_kind: MediaKind,
216 codec: MediaCodec,
217 clock_rate: u32,
218 payload_type: Option<PayloadType>,
219 channels: Option<u16>,
220 settings: Vec<CodecSetting>,
221 rtcp_feedback: Vec<RtcpFeedback>,
222}
223
224impl MediaCodecCapability {
225 #[must_use]
226 pub fn new(media_kind: MediaKind, codec: impl Into<MediaCodec>, clock_rate: u32) -> Self {
227 Self {
228 media_kind,
229 codec: codec.into(),
230 clock_rate,
231 payload_type: None,
232 channels: None,
233 settings: Vec::new(),
234 rtcp_feedback: Vec::new(),
235 }
236 }
237
238 #[must_use]
239 pub fn with_payload_type(mut self, payload_type: PayloadType) -> Self {
240 self.payload_type = Some(payload_type);
241 self
242 }
243
244 #[must_use]
245 pub fn with_channels(mut self, channels: u16) -> Self {
246 self.channels = Some(channels);
247 self
248 }
249
250 #[must_use]
251 pub fn with_setting(mut self, setting: CodecSetting) -> Self {
252 self.settings.push(setting);
253 self
254 }
255
256 #[must_use]
259 pub fn with_parameter(self, name: impl Into<String>, value: impl Into<String>) -> Self {
260 let setting = codec_setting_from_wire(&self.codec, name.into(), value.into());
261 self.with_setting(setting)
262 }
263
264 #[must_use]
265 pub fn with_rtcp_feedback(mut self, feedback: RtcpFeedback) -> Self {
266 self.rtcp_feedback.push(feedback);
267 self
268 }
269
270 #[must_use]
271 pub fn media_kind(&self) -> MediaKind {
272 self.media_kind
273 }
274
275 #[must_use]
276 pub fn codec(&self) -> &MediaCodec {
277 &self.codec
278 }
279
280 #[must_use]
281 pub fn codec_name(&self) -> &str {
282 self.codec.as_str()
283 }
284
285 #[must_use]
286 pub fn clock_rate(&self) -> u32 {
287 self.clock_rate
288 }
289
290 #[must_use]
291 pub fn payload_type_id(&self) -> Option<PayloadType> {
292 self.payload_type
293 }
294
295 #[must_use]
296 pub fn payload_type(&self) -> Option<u8> {
297 self.payload_type.map(PayloadType::value)
298 }
299
300 #[must_use]
301 pub fn channels(&self) -> Option<u16> {
302 self.channels
303 }
304
305 pub fn settings(&self) -> impl Iterator<Item = &CodecSetting> {
306 self.settings.iter()
307 }
308
309 pub fn parameters(&self) -> impl Iterator<Item = (String, String)> + '_ {
310 self.settings
311 .iter()
312 .map(|setting| (setting.key().to_owned(), setting.wire_value().into_owned()))
313 }
314
315 pub fn rtcp_feedback(&self) -> impl Iterator<Item = &RtcpFeedback> {
316 self.rtcp_feedback.iter()
317 }
318
319 #[must_use]
320 pub fn rtx_associated_payload_type_id(&self) -> Option<PayloadType> {
321 self.settings.iter().find_map(|setting| match setting {
322 CodecSetting::RtxAssociation(payload_type) => Some(*payload_type),
323 _ => None,
324 })
325 }
326
327 #[must_use]
328 pub fn rtx_associated_payload_type(&self) -> Option<u8> {
329 self.rtx_associated_payload_type_id()
330 .map(PayloadType::value)
331 }
332}
333
334#[derive(Debug, Clone, Default, PartialEq, Eq)]
336pub struct MediaCapabilities {
337 codecs: Vec<MediaCodecCapability>,
338 header_extensions: Vec<HeaderExtension>,
339}
340
341impl MediaCapabilities {
342 #[must_use]
343 pub fn new(codecs: Vec<MediaCodecCapability>, header_extensions: Vec<HeaderExtension>) -> Self {
344 Self {
345 codecs,
346 header_extensions,
347 }
348 }
349
350 pub fn codecs(&self) -> impl Iterator<Item = &MediaCodecCapability> {
351 self.codecs.iter()
352 }
353
354 pub fn header_extensions(&self) -> impl Iterator<Item = &HeaderExtension> {
355 self.header_extensions.iter()
356 }
357}
358
359#[derive(Debug, Clone, PartialEq, Eq)]
364pub struct MediaFormat {
365 media_kind: MediaKind,
366 codec: MediaCodec,
367 payload_type: PayloadType,
368 clock_rate: u32,
369 channels: Option<u16>,
370 settings: Vec<CodecSetting>,
371 rtcp_feedback: Vec<RtcpFeedback>,
372}
373
374impl MediaFormat {
375 #[must_use]
376 pub fn new(
377 media_kind: MediaKind,
378 codec: impl Into<MediaCodec>,
379 payload_type: PayloadType,
380 clock_rate: u32,
381 ) -> Self {
382 Self {
383 media_kind,
384 codec: codec.into(),
385 payload_type,
386 clock_rate,
387 channels: None,
388 settings: Vec::new(),
389 rtcp_feedback: Vec::new(),
390 }
391 }
392
393 #[must_use]
394 pub fn with_channels(mut self, channels: u16) -> Self {
395 self.channels = Some(channels);
396 self
397 }
398
399 #[must_use]
400 pub fn with_setting(mut self, setting: CodecSetting) -> Self {
401 self.settings.push(setting);
402 self
403 }
404
405 #[must_use]
406 pub fn with_parameter(self, name: impl Into<String>, value: impl Into<String>) -> Self {
407 let setting = codec_setting_from_wire(&self.codec, name.into(), value.into());
408 self.with_setting(setting)
409 }
410
411 #[must_use]
412 pub fn with_rtcp_feedback(mut self, feedback: RtcpFeedback) -> Self {
413 self.rtcp_feedback.push(feedback);
414 self
415 }
416
417 #[must_use]
418 pub fn media_kind(&self) -> MediaKind {
419 self.media_kind
420 }
421
422 #[must_use]
423 pub fn codec(&self) -> &MediaCodec {
424 &self.codec
425 }
426
427 #[must_use]
428 pub fn codec_name(&self) -> &str {
429 self.codec.as_str()
430 }
431
432 #[must_use]
433 pub fn payload_type_id(&self) -> PayloadType {
434 self.payload_type
435 }
436
437 #[must_use]
438 pub fn payload_type(&self) -> u8 {
439 self.payload_type.value()
440 }
441
442 #[must_use]
443 pub fn clock_rate(&self) -> u32 {
444 self.clock_rate
445 }
446
447 #[must_use]
448 pub fn channels(&self) -> Option<u16> {
449 self.channels
450 }
451
452 pub fn settings(&self) -> impl Iterator<Item = &CodecSetting> {
453 self.settings.iter()
454 }
455
456 pub fn parameters(&self) -> impl Iterator<Item = (String, String)> + '_ {
457 self.settings
458 .iter()
459 .map(|setting| (setting.key().to_owned(), setting.wire_value().into_owned()))
460 }
461
462 pub fn rtcp_feedback(&self) -> impl Iterator<Item = &RtcpFeedback> {
463 self.rtcp_feedback.iter()
464 }
465
466 #[must_use]
467 pub fn rtx_associated_payload_type_id(&self) -> Option<PayloadType> {
468 self.settings.iter().find_map(|setting| match setting {
469 CodecSetting::RtxAssociation(payload_type) => Some(*payload_type),
470 _ => None,
471 })
472 }
473
474 #[must_use]
475 pub fn rtx_associated_payload_type(&self) -> Option<u8> {
476 self.rtx_associated_payload_type_id()
477 .map(PayloadType::value)
478 }
479}
480
481#[derive(Debug, Clone, Default, PartialEq, Eq)]
504pub struct StreamBinding {
505 ssrc: Option<Ssrc>,
506 repair_ssrc: Option<Ssrc>,
507 rid: Option<Rid>,
508 payload_type: Option<PayloadType>,
509 max_bitrate: Option<u64>,
510}
511
512impl StreamBinding {
513 #[must_use]
514 pub fn new() -> Self {
515 Self::default()
516 }
517
518 #[must_use]
519 pub fn with_ssrc(mut self, ssrc: impl Into<Ssrc>) -> Self {
520 self.ssrc = Some(ssrc.into());
521 self
522 }
523
524 #[must_use]
528 pub fn with_repair_ssrc(mut self, repair_ssrc: impl Into<Ssrc>) -> Self {
529 self.repair_ssrc = Some(repair_ssrc.into());
530 self
531 }
532
533 #[must_use]
534 pub fn with_rid(mut self, rid: impl Into<Rid>) -> Self {
535 self.rid = Some(rid.into());
536 self
537 }
538
539 #[must_use]
540 pub fn with_payload_type(mut self, payload_type: PayloadType) -> Self {
541 self.payload_type = Some(payload_type);
542 self
543 }
544
545 #[must_use]
546 pub fn with_max_bitrate(mut self, max_bitrate: u64) -> Self {
547 self.max_bitrate = Some(max_bitrate);
548 self
549 }
550
551 #[must_use]
552 pub fn ssrc(&self) -> Option<u32> {
553 self.ssrc.map(Ssrc::value)
554 }
555
556 #[must_use]
557 pub fn repair_ssrc(&self) -> Option<u32> {
558 self.repair_ssrc.map(Ssrc::value)
559 }
560
561 #[must_use]
562 pub fn rid(&self) -> Option<&str> {
563 self.rid.as_ref().map(Rid::as_str)
564 }
565
566 #[must_use]
567 pub(super) fn with_payload_type_mapping(
568 mut self,
569 payload_types: &[(PayloadType, PayloadType)],
570 ) -> Self {
571 if let Some(payload_type) = self.payload_type {
572 let mapped_payload_type = payload_types
573 .iter()
574 .find_map(|(original, mapped)| (*original == payload_type).then_some(*mapped))
575 .unwrap_or(payload_type);
576 self.payload_type = Some(mapped_payload_type);
577 }
578 self
579 }
580
581 #[must_use]
582 pub fn payload_type_id(&self) -> Option<PayloadType> {
583 self.payload_type
584 }
585
586 #[must_use]
587 pub fn payload_type(&self) -> Option<u8> {
588 self.payload_type.map(PayloadType::value)
589 }
590
591 #[must_use]
592 pub fn max_bitrate(&self) -> Option<u64> {
593 self.max_bitrate
594 }
595}
596
597#[derive(Debug, Clone, Default, PartialEq, Eq)]
602pub struct MediaStream {
603 formats: Vec<MediaFormat>,
604 header_extensions: Vec<HeaderExtension>,
605 bindings: Vec<StreamBinding>,
606 mid: Option<Mid>,
607}
608
609impl MediaStream {
610 #[must_use]
611 pub fn new(
612 formats: Vec<MediaFormat>,
613 header_extensions: Vec<HeaderExtension>,
614 bindings: Vec<StreamBinding>,
615 ) -> Self {
616 Self {
617 formats,
618 header_extensions,
619 bindings,
620 mid: None,
621 }
622 }
623
624 #[must_use]
625 pub fn with_mid(mut self, mid: impl Into<Mid>) -> Self {
626 self.mid = Some(mid.into());
627 self
628 }
629
630 pub fn formats(&self) -> impl Iterator<Item = &MediaFormat> {
631 self.formats.iter()
632 }
633
634 pub fn header_extensions(&self) -> impl Iterator<Item = &HeaderExtension> {
635 self.header_extensions.iter()
636 }
637
638 pub fn bindings(&self) -> impl Iterator<Item = &StreamBinding> {
639 self.bindings.iter()
640 }
641
642 #[must_use]
643 pub fn mid(&self) -> Option<&str> {
644 self.mid.as_ref().map(Mid::as_str)
645 }
646}
647
648fn codec_setting_from_wire(codec: &MediaCodec, key: String, value: String) -> CodecSetting {
649 match key.as_str() {
650 rfc_rtp::fmtp::RTX_ASSOCIATION => value
651 .parse::<u8>()
652 .ok()
653 .and_then(PayloadType::try_new)
654 .map_or(
655 CodecSetting::Other { key, value },
656 CodecSetting::RtxAssociation,
657 ),
658 rfc_rtp::fmtp::H264_PACKETIZATION_MODE => value
659 .parse::<u8>()
660 .ok()
661 .and_then(rfc_rtp::h264::PacketizationMode::from_fmtp_value)
662 .map_or_else(
663 || CodecSetting::Other { key, value },
664 CodecSetting::H264PacketizationMode,
665 ),
666 rfc_rtp::fmtp::H264_PROFILE_LEVEL_ID => CodecSetting::H264ProfileLevelId(value),
667 rfc_rtp::fmtp::VP9_PROFILE_ID if codec == &MediaCodec::Vp9 => value
668 .parse::<u8>()
669 .ok()
670 .and_then(rfc_rtp::Vp9ProfileId::try_new)
671 .map_or(
672 CodecSetting::Other { key, value },
673 CodecSetting::Vp9ProfileId,
674 ),
675 rfc_rtp::fmtp::OPUS_USE_IN_BAND_FEC => match value.as_str() {
676 rfc_rtp::fmtp::VALUE_ENABLED | rfc_rtp::fmtp::VALUE_TRUE => {
677 CodecSetting::UseInBandFec(true)
678 }
679 rfc_rtp::fmtp::VALUE_DISABLED | rfc_rtp::fmtp::VALUE_FALSE => {
680 CodecSetting::UseInBandFec(false)
681 }
682 _ => CodecSetting::Other { key, value },
683 },
684 _ => CodecSetting::Other { key, value },
685 }
686}