1use std::{
8 fmt::{Display, Write},
9 net::IpAddr,
10 str::FromStr,
11};
12
13use crate::{builders, enums::*, Attribute};
14
15pub trait TypedAttribute: Display + FromStr<Err = AttributeError> {
17 const NAME: &'static str;
18}
19
20impl<T: TypedAttribute> From<T> for Attribute {
21 fn from(attr: T) -> Attribute {
22 Attribute {
23 attribute: T::NAME.to_string(),
24 value: Some(attr.to_string()),
25 }
26 }
27}
28
29#[derive(Debug, PartialEq, Eq, thiserror::Error)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub enum AttributeError {
33 #[error("Attribute {} not found", .0)]
35 NotFound(String),
36 #[error("Param {} not found in {}", .param, .attr)]
38 ParamNotFound { param: String, attr: String },
39 #[error("Invalid value {} for Param {} in {}", .val ,.param, .attr)]
41 InvalidParamValue {
42 param: String,
43 val: String,
44 attr: String,
45 },
46 #[error("Unsupported attribute format: {} for {}", .val, .attr)]
48 UnsupportedFormat { val: String, attr: String },
49 #[error("Unexpected trailing item {} for in {}", .val, .attr)]
51 UnexpectedTrailingItem { val: String, attr: String },
52 #[error("{}: {}", .attr, .error)]
54 Other { error: String, attr: String },
55}
56
57impl AttributeError {
58 pub fn is_attribute_not_found(&self) -> bool {
59 matches!(self, AttributeError::NotFound(_))
60 }
61
62 fn add_param_context(&mut self, param_context: &str) {
64 use AttributeError::*;
65 match self {
66 NotFound(_)
67 | Other { .. }
68 | UnsupportedFormat { .. }
69 | UnexpectedTrailingItem { .. } => (),
70 ParamNotFound { param, .. } | InvalidParamValue { param, .. } => {
71 param.push(')');
72 param.insert_str(0, " (");
73 param.insert_str(0, param_context);
74 }
75 }
76 }
77
78 pub(crate) fn set_attr(&mut self, new_attr: impl ToString) {
80 use AttributeError::*;
81 match self {
82 NotFound(attr)
83 | ParamNotFound { attr, .. }
84 | InvalidParamValue { attr, .. }
85 | UnsupportedFormat { attr, .. }
86 | UnexpectedTrailingItem { attr, .. }
87 | Other { attr, .. } => *attr = new_attr.to_string(),
88 }
89 }
90}
91
92pub(crate) trait ErrorContext {
93 fn with_param_context(self, param_context: &str) -> Self;
94 fn with_attr(self, new_attr: impl ToString) -> Self;
95}
96
97impl<T> ErrorContext for Result<T, AttributeError> {
98 fn with_param_context(self, param_context: &str) -> Self {
100 self.map_err(|mut err| {
101 err.add_param_context(param_context);
102 err
103 })
104 }
105
106 fn with_attr(self, new_attr: impl ToString) -> Self {
108 self.map_err(|mut err| {
109 err.set_attr(new_attr);
110 err
111 })
112 }
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
119#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
120pub struct RtpMap {
121 pub payload_type: u8,
123 pub encoding_name: String,
126 pub clock_rate: u32,
128 pub encoding_params: Option<String>,
132}
133
134impl RtpMap {
135 pub fn new(payload_type: u8, encoding_name: impl ToString, clock_rate: u32) -> Self {
136 RtpMap {
137 payload_type,
138 encoding_name: encoding_name.to_string(),
139 clock_rate,
140 encoding_params: None,
141 }
142 }
143
144 pub fn builder(
145 payload_type: u8,
146 encoding_name: impl ToString,
147 clock_rate: u32,
148 ) -> builders::RtpMap {
149 builders::RtpMap::new(payload_type, encoding_name, clock_rate)
150 }
151
152 pub fn with_encoding_params(
153 payload_type: u8,
154 encoding_name: impl ToString,
155 clock_rate: u32,
156 encoding_params: impl ToString,
157 ) -> Self {
158 RtpMap {
159 payload_type,
160 encoding_name: encoding_name.to_string(),
161 clock_rate,
162 encoding_params: Some(encoding_params.to_string()),
163 }
164 }
165
166 pub fn set_encoding_params(&mut self, encoding_params: impl ToString) {
167 self.encoding_params = Some(encoding_params.to_string());
168 }
169}
170
171impl FromStr for RtpMap {
172 type Err = AttributeError;
173
174 fn from_str(s: &str) -> Result<Self, Self::Err> {
175 let Some((pt, rest)) = s.split_once(' ') else {
176 return Err(AttributeError::UnsupportedFormat {
177 val: s.to_string(),
178 attr: <Self as TypedAttribute>::NAME.to_string(),
179 });
180 };
181
182 let Ok(pt) = pt.parse::<u8>() else {
183 return Err(AttributeError::InvalidParamValue {
184 param: "Payload type".to_string(),
185 val: pt.to_string(),
186 attr: <Self as TypedAttribute>::NAME.to_string(),
187 });
188 };
189
190 if pt > 127 {
191 return Err(AttributeError::InvalidParamValue {
192 param: "Payload type".to_string(),
193 val: format!("{pt}(expected 0-127)"),
194 attr: <Self as TypedAttribute>::NAME.to_string(),
195 });
196 }
197
198 let mut i = rest.splitn(3, '/');
199 let Some(encoding) = i.next() else {
200 return Err(AttributeError::ParamNotFound {
201 param: "Encoding name".to_string(),
202 attr: <Self as TypedAttribute>::NAME.to_string(),
203 });
204 };
205
206 let Some(clock_rate) = i.next() else {
207 return Err(AttributeError::ParamNotFound {
208 param: "Clock rate".to_string(),
209 attr: <Self as TypedAttribute>::NAME.to_string(),
210 });
211 };
212
213 let Ok(clock_rate) = clock_rate.parse::<u32>() else {
214 return Err(AttributeError::InvalidParamValue {
215 param: "Clock rate".to_string(),
216 val: clock_rate.to_string(),
217 attr: <Self as TypedAttribute>::NAME.to_string(),
218 });
219 };
220
221 let params = i.next().map(String::from);
222
223 Ok(Self {
224 payload_type: pt,
225 encoding_name: encoding.to_owned(),
226 clock_rate,
227 encoding_params: params,
228 })
229 }
230}
231
232impl Display for RtpMap {
233 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
234 write!(
235 f,
236 "{} {}/{}",
237 self.payload_type, self.encoding_name, self.clock_rate
238 )?;
239 if let Some(params) = &self.encoding_params {
240 f.write_char('/')?;
241 f.write_str(params)?;
242 }
243 Ok(())
244 }
245}
246
247impl TypedAttribute for RtpMap {
248 const NAME: &'static str = "rtpmap";
249}
250
251#[derive(Debug, Clone, PartialEq, Eq)]
253#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
254pub struct FmtpParam {
255 pub param: String,
256 pub val: Option<String>,
257}
258
259impl FmtpParam {
260 pub fn new(param: impl ToString) -> Self {
261 FmtpParam {
262 param: param.to_string(),
263 val: None,
264 }
265 }
266
267 pub fn builder(param: impl ToString) -> builders::FmtpParam {
268 builders::FmtpParam::new(param)
269 }
270
271 pub fn with_value(param: impl ToString, value: impl ToString) -> Self {
272 FmtpParam {
273 param: param.to_string(),
274 val: Some(value.to_string()),
275 }
276 }
277
278 pub fn set_value(&mut self, value: impl ToString) {
279 self.val = Some(value.to_string());
280 }
281}
282
283#[derive(Debug, Clone, PartialEq, Eq)]
287#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
288pub struct Fmtp {
289 pub fmt: u8,
291 pub format_specific_params: Vec<FmtpParam>,
295}
296
297impl Fmtp {
298 pub fn new(fmt: u8) -> Self {
299 Fmtp {
300 fmt,
301 format_specific_params: vec![],
302 }
303 }
304
305 pub fn builder(fmt: u8) -> builders::Fmtp {
306 builders::Fmtp::new(fmt)
307 }
308
309 pub fn add_format_specific_param(&mut self, format_specific_param: FmtpParam) {
310 self.format_specific_params.push(format_specific_param)
311 }
312
313 pub fn add_format_specific_params(
314 &mut self,
315 format_specific_params: impl IntoIterator<Item = FmtpParam>,
316 ) {
317 self.format_specific_params.extend(format_specific_params)
318 }
319}
320
321impl FromStr for Fmtp {
322 type Err = AttributeError;
323
324 fn from_str(s: &str) -> Result<Self, Self::Err> {
325 let Some((fmt, rest)) = s.split_once(' ') else {
326 return Err(AttributeError::UnsupportedFormat {
327 val: s.to_string(),
328 attr: <Self as TypedAttribute>::NAME.to_string(),
329 });
330 };
331
332 let Ok(fmt) = fmt.parse::<u8>() else {
333 return Err(AttributeError::InvalidParamValue {
334 param: "fmtp".to_string(),
335 val: fmt.to_string(),
336 attr: <Self as TypedAttribute>::NAME.to_string(),
337 });
338 };
339
340 let mut params: Vec<FmtpParam> = Vec::new();
341 for param in rest.split(';') {
342 if let Some((key, value)) = param.split_once('=') {
343 params.push(FmtpParam {
344 param: key.to_string(),
345 val: Some(value.to_string()),
346 });
347 } else {
348 params.push(FmtpParam {
349 param: param.to_string(),
350 val: None,
351 });
352 }
353 }
354
355 Ok(Self {
356 fmt,
357 format_specific_params: params,
358 })
359 }
360}
361
362impl Display for Fmtp {
363 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
364 write!(f, "{} ", self.fmt)?;
365 let mut iter = self.format_specific_params.iter().peekable();
366 while let Some(p) = iter.next() {
367 write!(f, "{}", p.param)?;
368 if let Some(val) = &p.val {
369 f.write_char('=')?;
370 f.write_str(val.as_str())?;
371 }
372 if iter.peek().is_some() {
373 f.write_char(';')?;
374 }
375 }
376 Ok(())
377 }
378}
379
380impl TypedAttribute for Fmtp {
381 const NAME: &'static str = "fmtp";
382}
383
384#[derive(Debug, Clone, PartialEq, Eq)]
391#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
392pub struct Rtcp {
393 pub port: u16,
395 pub nettype: NetType,
397 pub addrtype: AddrType,
399 pub connection_address: String,
402}
403
404impl Rtcp {
405 pub fn with_ip_addr(port: u16, connection_address: impl Into<IpAddr>) -> Self {
407 let connection_address = connection_address.into();
408 Rtcp {
409 port,
410 nettype: NetType::In,
411 addrtype: connection_address.into(),
412 connection_address: connection_address.to_string(),
413 }
414 }
415
416 pub fn new(
420 port: u16,
421 nettype: NetType,
422 addrtype: AddrType,
423 connection_address: impl ToString,
424 ) -> Self {
425 Rtcp {
426 port,
427 nettype,
428 addrtype,
429 connection_address: connection_address.to_string(),
430 }
431 }
432
433 pub fn try_parse_connection_ip_address(&self) -> Result<IpAddr, &str> {
438 self.connection_address
439 .parse::<IpAddr>()
440 .map_err(|_| self.connection_address.as_str())
441 }
442
443 pub fn set_connection_ip_address(&mut self, connection_address: impl Into<IpAddr>) {
445 let connection_address = connection_address.into();
446 self.addrtype = connection_address.into();
447 self.connection_address = connection_address.to_string();
448 }
449}
450
451impl FromStr for Rtcp {
452 type Err = AttributeError;
453
454 fn from_str(s: &str) -> Result<Self, Self::Err> {
455 let mut i = s.split(' ');
456 let Some(port) = i.next() else {
457 return Err(AttributeError::ParamNotFound {
458 param: "Port".to_string(),
459 attr: <Self as TypedAttribute>::NAME.to_string(),
460 });
461 };
462
463 let Ok(port) = port.parse::<u16>() else {
464 return Err(AttributeError::InvalidParamValue {
465 param: "Port".to_string(),
466 val: port.to_string(),
467 attr: <Self as TypedAttribute>::NAME.to_string(),
468 });
469 };
470
471 let Some(nettype) = i.next() else {
472 return Err(AttributeError::ParamNotFound {
473 param: "Network type".to_string(),
474 attr: <Self as TypedAttribute>::NAME.to_string(),
475 });
476 };
477
478 let Ok(nettype) = NetType::from_str(nettype) else {
479 return Err(AttributeError::InvalidParamValue {
480 param: "Network type".to_string(),
481 val: nettype.to_string(),
482 attr: <Self as TypedAttribute>::NAME.to_string(),
483 });
484 };
485
486 let Some(addrtype) = i.next() else {
487 return Err(AttributeError::ParamNotFound {
488 param: "Address type".to_string(),
489 attr: <Self as TypedAttribute>::NAME.to_string(),
490 });
491 };
492
493 let Ok(addrtype) = AddrType::from_str(addrtype) else {
494 return Err(AttributeError::InvalidParamValue {
495 param: "Address type".to_string(),
496 val: addrtype.to_string(),
497 attr: <Self as TypedAttribute>::NAME.to_string(),
498 });
499 };
500
501 let Some(connection_address) = i.next() else {
502 return Err(AttributeError::ParamNotFound {
503 param: "Connection address".to_string(),
504 attr: <Self as TypedAttribute>::NAME.to_string(),
505 });
506 };
507
508 if let Some(unexpected) = i.next() {
509 return Err(AttributeError::UnexpectedTrailingItem {
510 val: unexpected.to_string(),
511 attr: <Self as TypedAttribute>::NAME.to_string(),
512 });
513 }
514
515 Ok(Self {
516 port,
517 nettype,
518 addrtype,
519 connection_address: connection_address.to_string(),
520 })
521 }
522}
523
524impl Display for Rtcp {
525 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
526 write!(
527 f,
528 "{} {} {} {}",
529 self.port, self.nettype, self.addrtype, self.connection_address
530 )
531 }
532}
533
534impl TypedAttribute for Rtcp {
535 const NAME: &'static str = "rtcp";
536}
537
538#[derive(Debug, PartialEq, Eq, Clone)]
542#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
543pub struct RtcpFb {
544 pub pt: RtcpFbPt,
546 pub val: RtcpFbVal,
548}
549
550impl RtcpFb {
551 pub fn new(pt: impl Into<RtcpFbPt>, val: impl Into<RtcpFbVal>) -> Self {
552 RtcpFb {
553 pt: pt.into(),
554 val: val.into(),
555 }
556 }
557}
558
559impl FromStr for RtcpFb {
560 type Err = AttributeError;
561
562 fn from_str(s: &str) -> Result<Self, Self::Err> {
563 let mut i = s.split(' ');
564 let Some(pt) = i.next() else {
565 return Err(AttributeError::ParamNotFound {
566 param: "Payload format".to_string(),
567 attr: <Self as TypedAttribute>::NAME.to_string(),
568 });
569 };
570
571 let pt = if let Ok(pt) = pt.parse::<u8>() {
572 RtcpFbPt::Fmt(pt)
573 } else if pt == "*" {
574 RtcpFbPt::Wildcard
575 } else {
576 return Err(AttributeError::InvalidParamValue {
577 param: "Payload format".to_string(),
578 val: pt.to_string(),
579 attr: <Self as TypedAttribute>::NAME.to_string(),
580 });
581 };
582
583 let Some(val) = i.next() else {
584 return Err(AttributeError::ParamNotFound {
585 param: "Rtcp feedback value".to_string(),
586 attr: <Self as TypedAttribute>::NAME.to_string(),
587 });
588 };
589
590 let rtcp_fb_val = match val {
591 "ack" => {
592 if let Some(ack_val) = i.next() {
593 let ack_val = match ack_val {
594 "rpsi" => RtcpFbAck::Rpsi,
595 "app" => {
596 if let Some(app_param) = i.next() {
597 RtcpFbAck::App(Some(app_param.to_string()))
598 } else {
599 RtcpFbAck::App(None)
600 }
601 }
602 "ccfb" => {
603 if let RtcpFbPt::Fmt(pt) = pt {
606 return Err(AttributeError::InvalidParamValue {
607 param: "Payload type of Congestion control feedback (ccfb)"
608 .to_string(),
609 val: format!("{pt}(expected wildcard (*))"),
610 attr: <Self as TypedAttribute>::NAME.to_string(),
611 });
612 } else {
613 RtcpFbAck::Ccfb
614 }
615 }
616 other => RtcpFbAck::Other(other.to_string()),
617 };
618 RtcpFbVal::Ack(Some(ack_val))
619 } else {
620 RtcpFbVal::Ack(None)
621 }
622 }
623 "nack" => {
624 if let Some(nack_val) = i.next() {
625 let nack_val = match nack_val {
626 "pli" => RtcpFbNack::Pli,
627 "sli" => RtcpFbNack::Sli,
628 "rpsi" => RtcpFbNack::Rpsi,
629 "app" => {
630 if let Some(app_param) = i.next() {
631 RtcpFbNack::App(Some(app_param.to_string()))
632 } else {
633 RtcpFbNack::App(None)
634 }
635 }
636 "ecn" => RtcpFbNack::Ecn,
637 other => RtcpFbNack::Other(other.to_string()),
638 };
639 RtcpFbVal::Nack(Some(nack_val))
640 } else {
641 RtcpFbVal::Nack(None)
642 }
643 }
644 "trr-int" => {
645 if let Some(val) = i.next() {
646 let Ok(i) = val.parse::<u64>() else {
647 return Err(AttributeError::InvalidParamValue {
648 param: "Minimum interval between RTCP packets (trr-int)".to_string(),
649 val: val.to_string(),
650 attr: <Self as TypedAttribute>::NAME.to_string(),
651 });
652 };
653 RtcpFbVal::TrrInt(i)
654 } else {
655 return Err(AttributeError::Other {
656 error: "Minimum interval between RTCP packets (trr-int) not specified"
657 .to_string(),
658 attr: <Self as TypedAttribute>::NAME.to_string(),
659 });
660 }
661 }
662 "ccm" => {
663 if let Some(ccm_val) = i.next() {
664 let ccm_val = match ccm_val {
665 "fir" => RtcpFbCcm::Fir,
666 "tmmbr" => {
667 if let Some(tmmbr_val) = i.next() {
668 RtcpFbCcm::Tmmbr(Some(tmmbr_val.to_string()))
669 } else {
670 RtcpFbCcm::Tmmbr(None)
671 }
672 }
673 "tstr" => RtcpFbCcm::Tstr,
674 "vbcm" => {
675 let mut v = vec![];
676 for vbcm_val in i {
677 let Ok(p) = vbcm_val.parse::<u8>() else {
678 return Err(AttributeError::InvalidParamValue {
679 param: "Video backchannel messages (vbcm)".to_string(),
680 val: vbcm_val.to_string(),
681 attr: <Self as TypedAttribute>::NAME.to_string(),
682 });
683 };
684 v.push(p);
685 }
686 RtcpFbCcm::Vbcm(v)
687 }
688 other => RtcpFbCcm::Other(other.to_string()),
689 };
690 RtcpFbVal::Ccm(ccm_val)
691 } else {
692 return Err(AttributeError::ParamNotFound {
693 param: "Codec control messages (ccm)".to_string(),
694 attr: <Self as TypedAttribute>::NAME.to_string(),
695 });
696 }
697 }
698 "transport-cc" => RtcpFbVal::TransportCc,
699 other => RtcpFbVal::Other(other.to_string()),
700 };
701
702 Ok(Self {
703 pt,
704 val: rtcp_fb_val,
705 })
706 }
707}
708
709impl Display for RtcpFb {
710 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
711 match self.pt {
712 RtcpFbPt::Wildcard => f.write_char('*')?,
713 RtcpFbPt::Fmt(pt) => write!(f, "{pt}")?,
714 }
715
716 f.write_char(' ')?;
717 write!(f, "{}", self.val)
718 }
719}
720
721impl TypedAttribute for RtcpFb {
722 const NAME: &'static str = "rtcp-fb";
723}
724
725#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
729#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
730pub enum Direction {
731 #[default]
732 SendRecv,
733 SendOnly,
734 RecvOnly,
735 Inactive,
736}
737
738impl Direction {
739 pub fn as_str(&self) -> &'static str {
740 match self {
741 Self::SendOnly => "sendonly",
742 Self::RecvOnly => "recvonly",
743 Self::SendRecv => "sendrecv",
744 Self::Inactive => "inactive",
745 }
746 }
747
748 pub fn has_send(self) -> bool {
749 matches!(self, Self::SendRecv | Self::SendOnly)
750 }
751
752 pub fn has_recv(self) -> bool {
753 matches!(self, Self::SendRecv | Self::RecvOnly)
754 }
755
756 pub fn reverse(self) -> Self {
757 match self {
758 Self::SendRecv => Self::SendRecv,
759 Self::SendOnly => Self::RecvOnly,
760 Self::RecvOnly => Self::SendOnly,
761 Self::Inactive => Self::Inactive,
762 }
763 }
764
765 pub fn intersect_with_remote(self, remote: Self) -> Self {
766 match (self, remote) {
767 (Self::Inactive, _)
768 | (_, Self::Inactive)
769 | (Self::RecvOnly, Self::RecvOnly)
770 | (Self::SendOnly, Self::SendOnly) => Self::Inactive,
771 (Self::SendRecv, Self::SendRecv) => Self::SendRecv,
772 (Self::SendOnly, Self::RecvOnly | Self::SendRecv)
773 | (Self::SendRecv, Self::RecvOnly) => Self::SendOnly,
774 (Self::RecvOnly, Self::SendRecv | Self::SendOnly)
775 | (Self::SendRecv, Self::SendOnly) => Self::RecvOnly,
776 }
777 }
778}
779
780impl FromStr for Direction {
781 type Err = ParseEnumError;
782
783 fn from_str(s: &str) -> Result<Self, Self::Err> {
784 if "sendonly".eq_ignore_ascii_case(s) {
785 Ok(Direction::SendOnly)
786 } else if "recvonly".eq_ignore_ascii_case(s) {
787 Ok(Direction::RecvOnly)
788 } else if "sendrecv".eq_ignore_ascii_case(s) {
789 Ok(Direction::SendRecv)
790 } else if "inactive".eq_ignore_ascii_case(s) {
791 Ok(Direction::Inactive)
792 } else {
793 Err(ParseEnumError::Invalid(s.to_string()))
794 }
795 }
796}
797
798impl Display for Direction {
799 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
800 f.write_str(self.as_str())
801 }
802}
803
804impl From<Direction> for Attribute {
805 fn from(attr: Direction) -> Attribute {
806 Attribute {
807 attribute: attr.to_string(),
808 value: None,
809 }
810 }
811}
812
813#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
817#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
818pub struct ExtMap {
819 pub id: u8,
821 pub direction: Option<Direction>,
823 pub uri: String,
825 pub attributes: Option<String>,
827}
828
829impl ExtMap {
830 pub fn new(id: u8, uri: impl ToString) -> Self {
831 ExtMap {
832 id,
833 direction: None,
834 uri: uri.to_string(),
835 attributes: None,
836 }
837 }
838
839 pub fn builder(id: u8, uri: impl ToString) -> builders::ExtMap {
840 builders::ExtMap::new(id, uri)
841 }
842
843 pub fn with_direction(id: u8, direction: Direction, uri: impl ToString) -> Self {
844 ExtMap {
845 id,
846 direction: Some(direction),
847 uri: uri.to_string(),
848 attributes: None,
849 }
850 }
851
852 pub fn with_direction_and_attributes(
853 id: u8,
854 direction: Direction,
855 uri: impl ToString,
856 attributes: impl ToString,
857 ) -> Self {
858 ExtMap {
859 id,
860 direction: Some(direction),
861 uri: uri.to_string(),
862 attributes: Some(attributes.to_string()),
863 }
864 }
865
866 pub fn set_direction(&mut self, direction: Direction) {
867 self.direction = Some(direction);
868 }
869
870 pub fn set_attributes(&mut self, attributes: impl ToString) {
871 self.attributes = Some(attributes.to_string());
872 }
873}
874
875impl FromStr for ExtMap {
876 type Err = AttributeError;
877
878 fn from_str(s: &str) -> Result<Self, Self::Err> {
879 let mut i = s.splitn(3, ' ');
880
881 let Some(id_direction) = i.next() else {
882 return Err(AttributeError::ParamNotFound {
883 param: "id/direction".to_string(),
884 attr: <Self as TypedAttribute>::NAME.to_string(),
885 });
886 };
887
888 let mut d = id_direction.split('/');
889
890 let Some(id) = d.next() else {
891 return Err(AttributeError::ParamNotFound {
892 param: "id".to_string(),
893 attr: <Self as TypedAttribute>::NAME.to_string(),
894 });
895 };
896
897 let direction = if let Some(d) = d.next() {
898 let Ok(dir) = Direction::from_str(d) else {
899 return Err(AttributeError::InvalidParamValue {
900 param: "Direction".to_string(),
901 val: d.to_string(),
902 attr: <Self as TypedAttribute>::NAME.to_string(),
903 });
904 };
905 Some(dir)
906 } else {
907 None
908 };
909
910 let Ok(id) = id.parse::<u8>() else {
911 return Err(AttributeError::InvalidParamValue {
912 param: "Id".to_string(),
913 val: id.to_string(),
914 attr: <Self as TypedAttribute>::NAME.to_string(),
915 });
916 };
917
918 if id == 0 {
919 return Err(AttributeError::InvalidParamValue {
920 param: "Id".to_string(),
921 val: id.to_string(),
922 attr: <Self as TypedAttribute>::NAME.to_string(),
923 });
924 }
925
926 let Some(uri) = i.next() else {
927 return Err(AttributeError::ParamNotFound {
928 param: "URI".to_string(),
929 attr: <Self as TypedAttribute>::NAME.to_string(),
930 });
931 };
932
933 let attributes = i.next().map(|attr| attr.to_string());
934
935 Ok(Self {
936 id,
937 direction,
938 uri: uri.to_string(),
939 attributes,
940 })
941 }
942}
943
944impl Display for ExtMap {
945 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
946 write!(f, "{}", self.id)?;
947 if let Some(direction) = &self.direction {
948 f.write_char('/')?;
949 f.write_str(direction.as_str())?;
950 }
951
952 f.write_char(' ')?;
953 f.write_str(&self.uri)?;
954
955 if let Some(attr) = &self.attributes {
956 f.write_char(' ')?;
957 f.write_str(attr.as_str())?;
958 }
959 Ok(())
960 }
961}
962
963impl TypedAttribute for ExtMap {
964 const NAME: &'static str = "extmap";
965}
966
967#[derive(Debug, Clone, PartialEq, Eq)]
971#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
972pub struct Fingerprint {
973 pub hash_func: HashFunc,
975 pub fingerprint: Vec<u8>,
977}
978
979impl Fingerprint {
980 pub fn new(hash_func: HashFunc) -> Self {
981 Fingerprint {
982 hash_func,
983 fingerprint: vec![],
984 }
985 }
986
987 pub fn with_fingerprint(
988 hash_func: HashFunc,
989 fingerprint: impl IntoIterator<Item = u8>,
990 ) -> Self {
991 Fingerprint {
992 hash_func,
993 fingerprint: std::iter::FromIterator::from_iter(fingerprint),
994 }
995 }
996
997 pub fn set_fingerprint(&mut self, fingerprint: impl IntoIterator<Item = u8>) {
998 self.fingerprint.clear();
999 self.fingerprint.extend(fingerprint);
1000 }
1001}
1002
1003impl FromStr for Fingerprint {
1004 type Err = AttributeError;
1005
1006 fn from_str(s: &str) -> Result<Self, Self::Err> {
1007 let mut i = s.splitn(2, ' ');
1008
1009 let hash_func = if let Some(hash_func) = i.next() {
1010 HashFunc::new(hash_func)
1011 } else {
1012 return Err(AttributeError::ParamNotFound {
1013 param: "Hash function".to_string(),
1014 attr: <Self as TypedAttribute>::NAME.to_string(),
1015 });
1016 };
1017
1018 let mut fingerprint: Vec<u8> = vec![];
1019 if let Some(fp) = i.next() {
1020 for f in fp.split(':') {
1021 let Ok(mut f) = hex::decode(f) else {
1022 return Err(AttributeError::InvalidParamValue {
1023 param: "Fingerprint value".to_string(),
1024 val: f.to_string(),
1025 attr: <Self as TypedAttribute>::NAME.to_string(),
1026 });
1027 };
1028
1029 fingerprint.append(&mut f);
1030 }
1031 } else {
1032 return Err(AttributeError::ParamNotFound {
1033 param: "Hash value".to_string(),
1034 attr: <Self as TypedAttribute>::NAME.to_string(),
1035 });
1036 };
1037
1038 Ok(Self {
1039 hash_func,
1040 fingerprint,
1041 })
1042 }
1043}
1044
1045impl Display for Fingerprint {
1046 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1047 f.write_str(self.hash_func.as_str())?;
1048 let mut first = true;
1049 for v in &self.fingerprint {
1050 if first {
1051 f.write_char(' ')?;
1052 first = false;
1053 } else {
1054 f.write_char(':')?;
1055 }
1056 write!(f, "{v:02X}")?;
1057 }
1058 Ok(())
1059 }
1060}
1061
1062impl TypedAttribute for Fingerprint {
1063 const NAME: &'static str = "fingerprint";
1064}
1065
1066#[derive(Debug, Clone, PartialEq, Eq)]
1070#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1071pub struct Group {
1072 pub semantics: GroupSemantics,
1073 pub mid_tags: Vec<String>,
1074}
1075
1076impl Group {
1077 pub fn new(semantics: GroupSemantics) -> Self {
1078 Group {
1079 semantics,
1080 mid_tags: vec![],
1081 }
1082 }
1083
1084 pub fn add_mid_tag(&mut self, mid_tag: impl ToString) {
1085 self.mid_tags.push(mid_tag.to_string())
1086 }
1087
1088 pub fn add_mid_tags(&mut self, mid_tags: impl IntoIterator<Item = impl ToString>) {
1089 self.mid_tags
1090 .extend(mid_tags.into_iter().map(|i| i.to_string()))
1091 }
1092}
1093
1094impl FromStr for Group {
1095 type Err = AttributeError;
1096
1097 fn from_str(s: &str) -> Result<Self, Self::Err> {
1098 let mut i = s.split(' ');
1099
1100 let Some(semantics) = i.next() else {
1101 return Err(AttributeError::ParamNotFound {
1102 param: "Semantics".to_string(),
1103 attr: <Self as TypedAttribute>::NAME.to_string(),
1104 });
1105 };
1106
1107 let semantics = GroupSemantics::new(semantics);
1108
1109 let mut mid_tags = vec![];
1110 for mid in i {
1111 mid_tags.push(mid.to_string());
1112 }
1113
1114 if mid_tags.is_empty() {
1115 return Err(AttributeError::ParamNotFound {
1116 param: "Media identification tags".to_string(),
1117 attr: <Self as TypedAttribute>::NAME.to_string(),
1118 });
1119 }
1120
1121 Ok(Self {
1122 semantics,
1123 mid_tags,
1124 })
1125 }
1126}
1127
1128impl Display for Group {
1129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1130 f.write_str(self.semantics.as_str())?;
1131 for m in &self.mid_tags {
1132 f.write_char(' ')?;
1133 f.write_str(m)?;
1134 }
1135 Ok(())
1136 }
1137}
1138
1139impl TypedAttribute for Group {
1140 const NAME: &'static str = "group";
1141}
1142
1143#[derive(Debug, Clone, PartialEq, Eq)]
1147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1148pub enum Setup {
1149 Active,
1151 Passive,
1153 ActPass,
1155 HoldConn,
1157}
1158
1159impl FromStr for Setup {
1160 type Err = AttributeError;
1161
1162 fn from_str(s: &str) -> Result<Self, Self::Err> {
1163 if "active".eq_ignore_ascii_case(s) {
1164 Ok(Setup::Active)
1165 } else if "passive".eq_ignore_ascii_case(s) {
1166 Ok(Setup::Passive)
1167 } else if "actpass".eq_ignore_ascii_case(s) {
1168 Ok(Setup::ActPass)
1169 } else if "holdconn".eq_ignore_ascii_case(s) {
1170 Ok(Setup::HoldConn)
1171 } else {
1172 Err(AttributeError::Other {
1173 error: format!("Invalid Setup value {s}"),
1174 attr: <Self as TypedAttribute>::NAME.to_string(),
1175 })
1176 }
1177 }
1178}
1179
1180impl Display for Setup {
1181 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1182 let s = match self {
1183 Setup::Active => "active",
1184 Setup::Passive => "passive",
1185 Setup::ActPass => "actpass",
1186 Setup::HoldConn => "holdconn",
1187 };
1188 f.write_str(s)
1189 }
1190}
1191
1192impl TypedAttribute for Setup {
1193 const NAME: &'static str = "setup";
1194}
1195
1196#[derive(Debug, Clone, PartialEq, Eq)]
1200#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1201pub struct Ssrc {
1202 pub ssrc_id: u32,
1203 pub attribute: SsrcAttribute,
1204 pub value: Option<String>,
1205}
1206
1207impl Ssrc {
1208 pub fn new(ssrc_id: u32, attribute: SsrcAttribute) -> Self {
1209 Ssrc {
1210 ssrc_id,
1211 attribute,
1212 value: None,
1213 }
1214 }
1215
1216 pub fn with_typed_attribute(ssrc_id: u32, attribute: impl TypedAttribute) -> Self {
1217 let value = attribute.to_string();
1218 Ssrc {
1219 ssrc_id,
1220 attribute: SsrcAttribute::from(attribute),
1221 value: Some(value),
1222 }
1223 }
1224
1225 pub fn with_value(ssrc_id: u32, attribute: SsrcAttribute, value: impl ToString) -> Self {
1226 Ssrc {
1227 ssrc_id,
1228 attribute,
1229 value: Some(value.to_string()),
1230 }
1231 }
1232
1233 pub fn set_value(&mut self, value: impl ToString) {
1234 self.value = Some(value.to_string());
1235 }
1236
1237 pub fn get_typed<T: TypedAttribute>(&self) -> Result<T, AttributeError> {
1245 if !self.attribute.as_str().eq_ignore_ascii_case(T::NAME) {
1246 return Err(AttributeError::Other {
1247 error: format!("Attribute type mismatch (requested {})", T::NAME),
1248 attr: self.attribute.as_str().to_string(),
1249 });
1250 }
1251
1252 let Some(ref value) = self.value else {
1253 return Err(AttributeError::Other {
1254 error: "No value for the attribute".to_string(),
1255 attr: T::NAME.to_string(),
1256 });
1257 };
1258
1259 T::from_str(value)
1260 }
1261}
1262
1263impl FromStr for Ssrc {
1264 type Err = AttributeError;
1265
1266 fn from_str(s: &str) -> Result<Self, Self::Err> {
1267 let Some((ssrc_id_str, rest)) = s.split_once(' ') else {
1268 return Err(AttributeError::ParamNotFound {
1269 param: "Ssrc id".to_string(),
1270 attr: <Self as TypedAttribute>::NAME.to_string(),
1271 });
1272 };
1273
1274 let Ok(ssrc_id) = ssrc_id_str.parse::<u32>() else {
1275 return Err(AttributeError::InvalidParamValue {
1276 param: "Ssrc id".to_string(),
1277 val: ssrc_id_str.to_string(),
1278 attr: <Self as TypedAttribute>::NAME.to_string(),
1279 });
1280 };
1281
1282 let (attr, value) = if let Some((attr_str, value)) = rest.split_once(':') {
1283 (attr_str, Some(value.to_string()))
1284 } else {
1285 (rest, None)
1286 };
1287
1288 Ok(Self {
1289 ssrc_id,
1290 attribute: SsrcAttribute::new(attr),
1291 value,
1292 })
1293 }
1294}
1295
1296impl Display for Ssrc {
1297 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1298 use crate::{Fmtp, MediaClockSource, ReferenceClock, Rtcp};
1299
1300 let attr_str = match &self.attribute {
1301 SsrcAttribute::Cname => "cname",
1302 SsrcAttribute::PreviousSsrc => "previous-ssrc",
1303 SsrcAttribute::Fmtp => <Fmtp as TypedAttribute>::NAME,
1304 SsrcAttribute::Rtcp => <Rtcp as TypedAttribute>::NAME,
1305 SsrcAttribute::ReferenceClock => <ReferenceClock as TypedAttribute>::NAME,
1306 SsrcAttribute::MediaClockSource => <MediaClockSource as TypedAttribute>::NAME,
1307 SsrcAttribute::Other(other) => other.as_str(),
1308 };
1309 write!(f, "{} {attr_str}", self.ssrc_id)?;
1310
1311 if let Some(value) = &self.value {
1312 f.write_char(':')?;
1313 f.write_str(value)?;
1314 }
1315
1316 Ok(())
1317 }
1318}
1319
1320impl TypedAttribute for Ssrc {
1321 const NAME: &'static str = "ssrc";
1322}
1323
1324#[derive(Debug, Clone, PartialEq, Eq)]
1328#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1329pub struct SsrcGroup {
1330 pub semantics: GroupSemantics,
1331 pub ssrc_ids: Vec<u32>,
1332}
1333
1334impl SsrcGroup {
1335 pub fn new(semantics: GroupSemantics) -> Self {
1336 SsrcGroup {
1337 semantics,
1338 ssrc_ids: vec![],
1339 }
1340 }
1341
1342 pub fn add_ssrc_id(&mut self, ssrc_id: u32) {
1343 self.ssrc_ids.push(ssrc_id)
1344 }
1345
1346 pub fn add_ssrc_ids(&mut self, ssrc_ids: impl IntoIterator<Item = u32>) {
1347 self.ssrc_ids.extend(ssrc_ids)
1348 }
1349}
1350
1351impl FromStr for SsrcGroup {
1352 type Err = AttributeError;
1353
1354 fn from_str(s: &str) -> Result<Self, Self::Err> {
1355 let mut i = s.split(' ');
1356
1357 let Some(semantics) = i.next() else {
1358 return Err(AttributeError::ParamNotFound {
1359 param: "Semantics".to_string(),
1360 attr: <Self as TypedAttribute>::NAME.to_string(),
1361 });
1362 };
1363
1364 let semantics = if "FEC".eq_ignore_ascii_case(semantics) {
1365 GroupSemantics::FEC
1366 } else if "FID".eq_ignore_ascii_case(semantics) {
1367 GroupSemantics::FID
1368 } else {
1369 GroupSemantics::Other(semantics.to_string())
1373 };
1374
1375 let mut ssrc_ids = vec![];
1376 for ssrc_id in i {
1377 let Ok(ssrc_id) = ssrc_id.parse::<u32>() else {
1378 return Err(AttributeError::InvalidParamValue {
1379 param: "Ssrc id".to_string(),
1380 val: ssrc_id.to_string(),
1381 attr: <Self as TypedAttribute>::NAME.to_string(),
1382 });
1383 };
1384 ssrc_ids.push(ssrc_id);
1385 }
1386
1387 if ssrc_ids.is_empty() {
1388 return Err(AttributeError::ParamNotFound {
1389 param: "ssrc_id".to_string(),
1390 attr: <Self as TypedAttribute>::NAME.to_string(),
1391 });
1392 }
1393
1394 Ok(Self {
1395 semantics,
1396 ssrc_ids,
1397 })
1398 }
1399}
1400
1401impl Display for SsrcGroup {
1402 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1403 let sem = match &self.semantics {
1404 GroupSemantics::FEC => "FEC",
1405 GroupSemantics::FID => "FID",
1406 GroupSemantics::LS => "LS",
1409 GroupSemantics::SRF => "SRF",
1410 GroupSemantics::ANAT => "ANAT",
1411 GroupSemantics::DDP => "DDP",
1412 GroupSemantics::Other(s) => s.as_str(),
1413 };
1414
1415 f.write_str(sem)?;
1416 for ssrc_id in &self.ssrc_ids {
1417 f.write_char(' ')?;
1418 write!(f, "{ssrc_id}")?;
1419 }
1420
1421 Ok(())
1422 }
1423}
1424
1425impl TypedAttribute for SsrcGroup {
1426 const NAME: &'static str = "ssrc-group";
1427}
1428
1429#[derive(Debug, PartialEq, Eq, Clone)]
1433#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1434pub struct SrtpKeyParam {
1435 pub key_and_salt: String,
1437 pub lifetime: Option<u32>,
1439 pub mki_and_length: Option<(u32, u32)>,
1441}
1442
1443impl SrtpKeyParam {
1444 pub fn new(key_and_salt: impl ToString) -> Self {
1445 SrtpKeyParam {
1446 key_and_salt: key_and_salt.to_string(),
1447 lifetime: None,
1448 mki_and_length: None,
1449 }
1450 }
1451
1452 pub fn with_lifetime(key_and_salt: impl ToString, lifetime: u32) -> Self {
1453 SrtpKeyParam {
1454 key_and_salt: key_and_salt.to_string(),
1455 lifetime: Some(lifetime),
1456 mki_and_length: None,
1457 }
1458 }
1459
1460 pub fn with_lifetime_and_mki_and_length(
1461 key_and_salt: impl ToString,
1462 lifetime: u32,
1463 mki: u32,
1464 length: u32,
1465 ) -> Self {
1466 SrtpKeyParam {
1467 key_and_salt: key_and_salt.to_string(),
1468 lifetime: Some(lifetime),
1469 mki_and_length: Some((mki, length)),
1470 }
1471 }
1472
1473 pub fn set_lifetime(&mut self, lifetime: u32) {
1474 self.lifetime = Some(lifetime);
1475 }
1476
1477 pub fn set_mki_and_length(&mut self, mki: u32, length: u32) {
1478 self.mki_and_length = Some((mki, length));
1479 }
1480}
1481
1482impl FromStr for SrtpKeyParam {
1483 type Err = AttributeError;
1484 fn from_str(key_param: &str) -> Result<Self, Self::Err> {
1485 let mut k = key_param.split('|');
1486
1487 let Some(key_and_salt_with_method) = k.next() else {
1488 return Err(AttributeError::ParamNotFound {
1489 param: "Srtp Key and Salt".to_string(),
1490 attr: Crypto::NAME.to_string(),
1491 });
1492 };
1493
1494 let key_and_salt = if key_and_salt_with_method
1495 .get(..7)
1496 .is_some_and(|p| p.eq_ignore_ascii_case("inline:"))
1497 {
1498 &key_and_salt_with_method[7..]
1499 } else {
1500 return Err(AttributeError::InvalidParamValue {
1501 param: "Srtp Key and Salt".to_string(),
1502 val: key_and_salt_with_method.to_string(),
1503 attr: Crypto::NAME.to_string(),
1504 });
1505 };
1506
1507 let (lifetime, mki_and_length) = if let Some(next_param) = k.next() {
1508 match next_param.split_once(':') {
1509 Some(mki_and_length) => {
1510 let Ok(mki) = mki_and_length.0.parse::<u32>() else {
1512 return Err(AttributeError::InvalidParamValue {
1513 param: "MKI".to_string(),
1514 val: next_param.to_string(),
1515 attr: Crypto::NAME.to_string(),
1516 });
1517 };
1518
1519 let Ok(len) = mki_and_length.1.parse::<u32>() else {
1520 return Err(AttributeError::InvalidParamValue {
1521 param: "Length".to_string(),
1522 val: next_param.to_string(),
1523 attr: Crypto::NAME.to_string(),
1524 });
1525 };
1526 (None, Some((mki, len)))
1527 }
1528 None => {
1529 let lifetime = match next_param.strip_prefix("2^") {
1531 Some(exp) => {
1532 let Ok(exp) = exp.parse::<u32>() else {
1533 return Err(AttributeError::InvalidParamValue {
1534 param: "Lifetime".to_string(),
1535 val: next_param.to_string(),
1536 attr: Crypto::NAME.to_string(),
1537 });
1538 };
1539 if exp >= 32 {
1541 return Err(AttributeError::InvalidParamValue {
1542 param: "Lifetime".to_string(),
1543 val: format!("{exp}(expected 0-32)"),
1544 attr: Crypto::NAME.to_string(),
1545 });
1546 }
1547 Some(2u32.pow(exp))
1548 }
1549 None => {
1550 let Ok(lifetime) = next_param.parse::<u32>() else {
1551 return Err(AttributeError::InvalidParamValue {
1552 param: "Lifetime".to_string(),
1553 val: next_param.to_string(),
1554 attr: Crypto::NAME.to_string(),
1555 });
1556 };
1557 Some(lifetime)
1558 }
1559 };
1560
1561 let mki_and_length = if let Some(m) = k.next() {
1563 if let Some(p) = m.split_once(':') {
1564 let Ok(mki) = p.0.parse::<u32>() else {
1565 return Err(AttributeError::InvalidParamValue {
1566 param: "MKI".to_string(),
1567 val: m.to_string(),
1568 attr: Crypto::NAME.to_string(),
1569 });
1570 };
1571
1572 let Ok(len) = p.1.parse::<u32>() else {
1573 return Err(AttributeError::InvalidParamValue {
1574 param: "Length".to_string(),
1575 val: m.to_string(),
1576 attr: Crypto::NAME.to_string(),
1577 });
1578 };
1579 Some((mki, len))
1580 } else {
1581 return Err(AttributeError::ParamNotFound {
1582 param: "MKI and Length".to_string(),
1583 attr: Crypto::NAME.to_string(),
1584 });
1585 }
1586 } else {
1587 None
1588 };
1589
1590 (lifetime, mki_and_length)
1591 }
1592 }
1593 } else {
1594 (None, None)
1595 };
1596
1597 if let Some((_, len)) = mki_and_length {
1598 if !(1..=128).contains(&len) {
1599 return Err(AttributeError::InvalidParamValue {
1600 param: "MKI length".to_string(),
1601 val: len.to_string(),
1602 attr: Crypto::NAME.to_string(),
1603 });
1604 }
1605 }
1606
1607 if let Some(unexpected) = k.next() {
1608 return Err(AttributeError::UnexpectedTrailingItem {
1609 val: unexpected.to_string(),
1610 attr: Crypto::NAME.to_string(),
1611 });
1612 }
1613
1614 Ok(Self {
1615 key_and_salt: key_and_salt.to_string(),
1616 lifetime,
1617 mki_and_length,
1618 })
1619 }
1620}
1621
1622impl Display for SrtpKeyParam {
1623 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1624 write!(f, "inline:{}", self.key_and_salt)?;
1625 if let Some(lifetime) = self.lifetime {
1626 if lifetime.is_power_of_two() {
1627 write!(f, "|2^{}", lifetime.trailing_zeros())?;
1628 } else {
1629 write!(f, "|{lifetime}")?;
1630 }
1631 }
1632 if let Some((mki, length)) = self.mki_and_length {
1633 write!(f, "|{mki}:{length}")?;
1634 }
1635 Ok(())
1636 }
1637}
1638
1639#[derive(Debug, PartialEq, Eq, Clone)]
1643#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1644pub struct Crypto {
1645 pub tag: u32,
1646 pub crypto_suite: CryptoSuite,
1647 pub key_params: Vec<SrtpKeyParam>,
1648 pub session_params: Vec<SrtpSessionParam>,
1649}
1650
1651impl Crypto {
1652 pub fn new(tag: u32, crypto_suite: CryptoSuite) -> Self {
1653 Crypto {
1654 tag,
1655 crypto_suite,
1656 key_params: vec![],
1657 session_params: vec![],
1658 }
1659 }
1660
1661 pub fn add_key_param(&mut self, key_param: SrtpKeyParam) {
1662 self.key_params.push(key_param)
1663 }
1664
1665 pub fn add_key_params(&mut self, key_params: impl IntoIterator<Item = SrtpKeyParam>) {
1666 self.key_params.extend(key_params)
1667 }
1668
1669 pub fn add_session_param(&mut self, session_param: SrtpSessionParam) {
1670 self.session_params.push(session_param)
1671 }
1672
1673 pub fn add_session_params(
1674 &mut self,
1675 session_params: impl IntoIterator<Item = SrtpSessionParam>,
1676 ) {
1677 self.session_params.extend(session_params)
1678 }
1679}
1680
1681impl FromStr for Crypto {
1682 type Err = AttributeError;
1683
1684 fn from_str(s: &str) -> Result<Self, Self::Err> {
1685 let mut i = s.split(' ');
1686
1687 let Some(tag) = i.next() else {
1688 return Err(AttributeError::ParamNotFound {
1689 param: "Tag".to_string(),
1690 attr: <Self as TypedAttribute>::NAME.to_string(),
1691 });
1692 };
1693
1694 let Ok(tag) = tag.parse::<u32>() else {
1695 return Err(AttributeError::InvalidParamValue {
1696 param: "Tag".to_string(),
1697 val: tag.to_string(),
1698 attr: <Self as TypedAttribute>::NAME.to_string(),
1699 });
1700 };
1701
1702 let Some(crypto_suite) = i.next() else {
1703 return Err(AttributeError::ParamNotFound {
1704 param: "CryptoSuite".to_string(),
1705 attr: <Self as TypedAttribute>::NAME.to_string(),
1706 });
1707 };
1708
1709 let crypto_suite = CryptoSuite::new(crypto_suite);
1710
1711 let Some(key_params_str) = i.next() else {
1712 return Err(AttributeError::ParamNotFound {
1713 param: "Key params".to_string(),
1714 attr: <Self as TypedAttribute>::NAME.to_string(),
1715 });
1716 };
1717
1718 let mut key_params: Vec<SrtpKeyParam> = Vec::new();
1719
1720 for key_param in key_params_str.split(';') {
1721 let key_param = SrtpKeyParam::from_str(key_param)?;
1722 key_params.push(key_param);
1723 }
1724
1725 let mut session_params: Vec<SrtpSessionParam> = Vec::new();
1726 for s in &mut i {
1727 let param = if s.get(..4).is_some_and(|p| p.eq_ignore_ascii_case("KDR=")) {
1728 let kdr_val = &s[4..];
1729 let Ok(kdr_val) = kdr_val.parse::<u8>() else {
1730 return Err(AttributeError::InvalidParamValue {
1731 param: "KDR".to_string(),
1732 val: kdr_val.to_string(),
1733 attr: <Self as TypedAttribute>::NAME.to_string(),
1734 });
1735 };
1736
1737 if !(0..=24).contains(&kdr_val) {
1742 return Err(AttributeError::InvalidParamValue {
1743 param: "KDR".to_string(),
1744 val: format!("{kdr_val}(expected range 0..24)"),
1745 attr: <Self as TypedAttribute>::NAME.to_string(),
1746 });
1747 }
1748 SrtpSessionParam::Kdr(kdr_val)
1749 } else if s.eq_ignore_ascii_case("UNENCRYPTED_SRTCP") {
1750 SrtpSessionParam::UnencryptedSrtcp
1751 } else if s.eq_ignore_ascii_case("UNENCRYPTED_SRTP") {
1752 SrtpSessionParam::UnencryptedSrtp
1753 } else if s.eq_ignore_ascii_case("UNAUTHENTICATED_SRTP") {
1754 SrtpSessionParam::UnauthenticatedSrtp
1755 } else if s
1756 .get(..10)
1757 .is_some_and(|p| p.eq_ignore_ascii_case("FEC_ORDER="))
1758 {
1759 let fec_ord = &s[10..];
1760 if fec_ord.eq_ignore_ascii_case("FEC_SRTP") {
1761 SrtpSessionParam::FecOrder(FecOrder::FecSrtp)
1762 } else if fec_ord.eq_ignore_ascii_case("SRTP_FEC") {
1763 SrtpSessionParam::FecOrder(FecOrder::SrtpFec)
1764 } else {
1765 return Err(AttributeError::InvalidParamValue {
1766 param: "FEC order".to_string(),
1767 val: s.to_string(),
1768 attr: <Self as TypedAttribute>::NAME.to_string(),
1769 });
1770 }
1771 } else if s
1772 .get(..8)
1773 .is_some_and(|p| p.eq_ignore_ascii_case("FEC_KEY="))
1774 {
1775 let key_params_str = &s[8..];
1776 let mut key_params: Vec<SrtpKeyParam> = Vec::new();
1777
1778 for key_param in key_params_str.split(';') {
1779 let key_param = SrtpKeyParam::from_str(key_param)?;
1780 key_params.push(key_param);
1781 }
1782 SrtpSessionParam::FecKey(key_params)
1783 } else if s.get(..4).is_some_and(|p| p.eq_ignore_ascii_case("WSH=")) {
1784 let wsh_val = &s[4..];
1785 let Ok(wsh_val) = wsh_val.parse::<u8>() else {
1786 return Err(AttributeError::InvalidParamValue {
1787 param: "WSH".to_string(),
1788 val: wsh_val.to_string(),
1789 attr: <Self as TypedAttribute>::NAME.to_string(),
1790 });
1791 };
1792
1793 if wsh_val < 64 {
1794 return Err(AttributeError::InvalidParamValue {
1795 param: "WSH".to_string(),
1796 val: wsh_val.to_string(),
1797 attr: <Self as TypedAttribute>::NAME.to_string(),
1798 });
1799 }
1800 SrtpSessionParam::Wsh(wsh_val)
1801 } else {
1802 SrtpSessionParam::Extension(s.to_string())
1804 };
1805 session_params.push(param);
1806 }
1807
1808 Ok(Self {
1809 tag,
1810 key_params,
1811 crypto_suite,
1812 session_params,
1813 })
1814 }
1815}
1816
1817impl Display for Crypto {
1818 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1819 write!(f, "{} {}", self.tag, self.crypto_suite.as_str())?;
1820
1821 for (i, key_param) in self.key_params.iter().enumerate() {
1822 if i == 0 {
1823 f.write_char(' ')?;
1824 } else {
1825 f.write_char(';')?;
1826 }
1827
1828 write!(f, "{}", key_param)?;
1829 }
1830
1831 for session_param in &self.session_params {
1832 match session_param {
1833 SrtpSessionParam::Kdr(kdr) => write!(f, " KDR={kdr}")?,
1834 SrtpSessionParam::UnencryptedSrtp => write!(f, " UNENCRYPTED_SRTP")?,
1835 SrtpSessionParam::UnencryptedSrtcp => write!(f, " UNENCRYPTED_SRTCP")?,
1836 SrtpSessionParam::UnauthenticatedSrtp => write!(f, " UNAUTHENTICATED_SRTP")?,
1837 SrtpSessionParam::FecOrder(fec_order) => {
1838 let order = match fec_order {
1839 FecOrder::FecSrtp => "FEC_SRTP",
1840 FecOrder::SrtpFec => "SRTP_FEC",
1841 };
1842 write!(f, " FEC_ORDER={order}")?;
1843 }
1844 SrtpSessionParam::FecKey(srtp_key_params) => {
1845 write!(f, " FEC_KEY")?;
1846 for (i, key_param) in srtp_key_params.iter().enumerate() {
1847 if i == 0 {
1848 f.write_char('=')?;
1849 } else {
1850 f.write_char(';')?;
1851 }
1852
1853 write!(f, "{}", key_param)?;
1854 }
1855 }
1856 SrtpSessionParam::Wsh(wsh) => write!(f, " WSH={wsh}")?,
1857 SrtpSessionParam::Extension(extn) => {
1858 f.write_char(' ')?;
1859 f.write_str(extn)?;
1860 }
1861 }
1862 }
1863 Ok(())
1864 }
1865}
1866
1867impl TypedAttribute for Crypto {
1868 const NAME: &'static str = "crypto";
1869}
1870
1871#[derive(Debug, PartialEq, Eq, Clone)]
1875#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1876pub struct Candidate {
1877 pub foundation: String,
1880 pub component_id: u32,
1883 pub transport: String,
1885 pub priority: u64,
1887 pub address: CandidateAddress,
1890 pub port: u16,
1892 pub typ: CandidateType,
1894 pub rel_addr: Option<IpAddr>,
1897 pub rel_port: Option<u16>,
1900 pub extensions: Vec<(String, String)>,
1902}
1903
1904impl Candidate {
1905 pub fn new(
1906 foundation: impl ToString,
1907 component_id: u32,
1908 transport: impl ToString,
1909 priority: u64,
1910 address: CandidateAddress,
1911 port: u16,
1912 typ: CandidateType,
1913 ) -> Self {
1914 Candidate {
1915 foundation: foundation.to_string(),
1916 component_id,
1917 transport: transport.to_string(),
1918 priority,
1919 address,
1920 port,
1921 typ,
1922 rel_addr: None,
1923 rel_port: None,
1924 extensions: vec![],
1925 }
1926 }
1927
1928 #[allow(clippy::too_many_arguments)]
1929 pub fn with_rel_addr(
1930 foundation: impl ToString,
1931 component_id: u32,
1932 transport: impl ToString,
1933 priority: u64,
1934 address: CandidateAddress,
1935 port: u16,
1936 typ: CandidateType,
1937 rel_addr: impl Into<IpAddr>,
1938 ) -> Self {
1939 Candidate {
1940 foundation: foundation.to_string(),
1941 component_id,
1942 transport: transport.to_string(),
1943 priority,
1944 address,
1945 port,
1946 typ,
1947 rel_addr: Some(rel_addr.into()),
1948 rel_port: None,
1949 extensions: vec![],
1950 }
1951 }
1952
1953 #[allow(clippy::too_many_arguments)]
1954 pub fn with_rel_addr_and_port(
1955 foundation: impl ToString,
1956 component_id: u32,
1957 transport: impl ToString,
1958 priority: u64,
1959 address: CandidateAddress,
1960 port: u16,
1961 typ: CandidateType,
1962 rel_addr: impl Into<IpAddr>,
1963 rel_port: u16,
1964 ) -> Self {
1965 Candidate {
1966 foundation: foundation.to_string(),
1967 component_id,
1968 transport: transport.to_string(),
1969 priority,
1970 address,
1971 port,
1972 typ,
1973 rel_addr: Some(rel_addr.into()),
1974 rel_port: Some(rel_port),
1975 extensions: vec![],
1976 }
1977 }
1978
1979 pub fn set_rel_addr(&mut self, rel_addr: impl Into<IpAddr>) {
1980 self.rel_addr = Some(rel_addr.into());
1981 }
1982
1983 pub fn set_rel_port(&mut self, rel_port: u16) {
1984 self.rel_port = Some(rel_port);
1985 }
1986
1987 pub fn add_extension(&mut self, name: impl ToString, value: impl ToString) {
1988 self.extensions.push((name.to_string(), value.to_string()))
1989 }
1990}
1991
1992impl FromStr for Candidate {
1993 type Err = AttributeError;
1994
1995 fn from_str(s: &str) -> Result<Self, Self::Err> {
1996 let mut i = s.split(' ');
1997
1998 let Some(foundation) = i.next() else {
1999 return Err(AttributeError::ParamNotFound {
2000 param: "Foundation".to_string(),
2001 attr: <Self as TypedAttribute>::NAME.to_string(),
2002 });
2003 };
2004
2005 let Some(comp_id) = i.next() else {
2006 return Err(AttributeError::ParamNotFound {
2007 param: "Component id".to_string(),
2008 attr: <Self as TypedAttribute>::NAME.to_string(),
2009 });
2010 };
2011
2012 let Ok(comp_id) = comp_id.parse::<u32>() else {
2013 return Err(AttributeError::InvalidParamValue {
2014 param: "Component id".to_string(),
2015 val: comp_id.to_string(),
2016 attr: <Self as TypedAttribute>::NAME.to_string(),
2017 });
2018 };
2019
2020 let Some(transport) = i.next() else {
2021 return Err(AttributeError::ParamNotFound {
2022 param: "Transport".to_string(),
2023 attr: <Self as TypedAttribute>::NAME.to_string(),
2024 });
2025 };
2026
2027 let Some(priority) = i.next() else {
2028 return Err(AttributeError::ParamNotFound {
2029 param: "Priority".to_string(),
2030 attr: <Self as TypedAttribute>::NAME.to_string(),
2031 });
2032 };
2033
2034 let Ok(priority) = priority.parse::<u64>() else {
2035 return Err(AttributeError::InvalidParamValue {
2036 param: "Priority".to_string(),
2037 val: priority.to_string(),
2038 attr: <Self as TypedAttribute>::NAME.to_string(),
2039 });
2040 };
2041
2042 let Some(address) = i.next() else {
2043 return Err(AttributeError::ParamNotFound {
2044 param: "Address".to_string(),
2045 attr: <Self as TypedAttribute>::NAME.to_string(),
2046 });
2047 };
2048
2049 let address = match address.parse::<IpAddr>() {
2050 Ok(a) => CandidateAddress::IpAddr(a),
2051 Err(_) => CandidateAddress::FQDN(address.to_string()),
2052 };
2053
2054 let Some(port) = i.next() else {
2055 return Err(AttributeError::ParamNotFound {
2056 param: "Port".to_string(),
2057 attr: <Self as TypedAttribute>::NAME.to_string(),
2058 });
2059 };
2060
2061 let Ok(port) = port.parse::<u16>() else {
2062 return Err(AttributeError::InvalidParamValue {
2063 param: "Port".to_string(),
2064 val: port.to_string(),
2065 attr: <Self as TypedAttribute>::NAME.to_string(),
2066 });
2067 };
2068
2069 let Some(typ_str) = i.next() else {
2070 return Err(AttributeError::ParamNotFound {
2071 param: "'typ' string".to_string(),
2072 attr: <Self as TypedAttribute>::NAME.to_string(),
2073 });
2074 };
2075
2076 if !typ_str.eq_ignore_ascii_case("typ") {
2077 return Err(AttributeError::ParamNotFound {
2078 param: "'typ' string".to_string(),
2079 attr: <Self as TypedAttribute>::NAME.to_string(),
2080 });
2081 }
2082
2083 let Some(cand_type) = i.next() else {
2084 return Err(AttributeError::ParamNotFound {
2085 param: "Candidate type".to_string(),
2086 attr: <Self as TypedAttribute>::NAME.to_string(),
2087 });
2088 };
2089
2090 let cand_type = CandidateType::new(cand_type);
2091
2092 let mut rel_addr: Option<IpAddr> = None;
2093 let mut rel_port: Option<u16> = None;
2094 let mut exts: Vec<(String, String)> = Vec::new();
2095
2096 while let Some(key) = i.next() {
2097 if key.eq_ignore_ascii_case("raddr") {
2098 let Some(raddr) = i.next() else {
2099 return Err(AttributeError::ParamNotFound {
2100 param: "Relative address".to_string(),
2101 attr: <Self as TypedAttribute>::NAME.to_string(),
2102 });
2103 };
2104
2105 if let Ok(raddr) = raddr.parse::<IpAddr>() {
2106 rel_addr = Some(raddr);
2107 } else {
2108 return Err(AttributeError::InvalidParamValue {
2109 param: "Relative address".to_string(),
2110 val: raddr.to_string(),
2111 attr: <Self as TypedAttribute>::NAME.to_string(),
2112 });
2113 };
2114 } else if key.eq_ignore_ascii_case("rport") {
2115 let Some(rport) = i.next() else {
2116 return Err(AttributeError::ParamNotFound {
2117 param: "Relative port".to_string(),
2118 attr: <Self as TypedAttribute>::NAME.to_string(),
2119 });
2120 };
2121
2122 if let Ok(rport) = rport.parse::<u16>() {
2123 rel_port = Some(rport);
2124 } else {
2125 return Err(AttributeError::InvalidParamValue {
2126 param: "Relative port".to_string(),
2127 val: rport.to_string(),
2128 attr: <Self as TypedAttribute>::NAME.to_string(),
2129 });
2130 }
2131 } else {
2132 let Some(val) = i.next() else {
2133 return Err(AttributeError::Other {
2134 error: format!("No val for the extension {key}"),
2135 attr: <Self as TypedAttribute>::NAME.to_string(),
2136 });
2137 };
2138
2139 exts.push((key.to_string(), val.to_string()));
2140 }
2141 }
2142
2143 Ok(Self {
2144 foundation: foundation.to_string(),
2145 component_id: comp_id,
2146 transport: transport.to_string(),
2147 priority,
2148 address,
2149 port,
2150 typ: cand_type,
2151 rel_addr,
2152 rel_port,
2153 extensions: exts,
2154 })
2155 }
2156}
2157
2158impl Display for Candidate {
2159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2160 let candidate_addr = match &self.address {
2161 CandidateAddress::IpAddr(a) => a.to_string(),
2162 CandidateAddress::FQDN(d) => d.clone(),
2163 };
2164 write!(
2165 f,
2166 "{} {} {} {} {} {} typ {}",
2167 self.foundation,
2168 self.component_id,
2169 self.transport,
2170 self.priority,
2171 candidate_addr,
2172 self.port,
2173 self.typ.as_str(),
2174 )?;
2175 if let Some(rel_addr) = self.rel_addr {
2176 write!(f, " raddr {rel_addr}")?;
2177 }
2178 if let Some(rel_port) = self.rel_port {
2179 write!(f, " rport {rel_port}")?;
2180 }
2181 for (key, val) in &self.extensions {
2182 write!(f, " {key} {val}")?;
2183 }
2184 Ok(())
2185 }
2186}
2187
2188impl TypedAttribute for Candidate {
2189 const NAME: &'static str = "candidate";
2190}
2191
2192#[cfg(test)]
2193mod tests {
2194 use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
2195
2196 use super::*;
2197 use crate::*;
2198
2199 #[test]
2200 fn direction_parse() {
2201 assert_eq!("sendonly".parse::<Direction>(), Ok(Direction::SendOnly));
2202 assert_eq!("recvonly".parse::<Direction>(), Ok(Direction::RecvOnly));
2203 assert_eq!("sendrecv".parse::<Direction>(), Ok(Direction::SendRecv));
2204 assert_eq!("inactive".parse::<Direction>(), Ok(Direction::Inactive));
2205 assert!("invalid".parse::<Direction>().is_err());
2206 }
2207
2208 #[test]
2209 fn direction_display() {
2210 assert_eq!(Direction::SendOnly.to_string(), "sendonly");
2211 assert_eq!(Direction::RecvOnly.to_string(), "recvonly");
2212 assert_eq!(Direction::SendRecv.to_string(), "sendrecv");
2213 assert_eq!(Direction::Inactive.to_string(), "inactive");
2214 }
2215
2216 #[test]
2217 fn parse_rtcp_fb() {
2218 let sdp = "v=0\r
2219o=alice 3203093520 3203093520 IN IP4 host.example.com\r
2220s=Multicast video with feedback\r
2221t=3203130148 3203137348\r
2222m=audio 49170 RTP/AVP 0\r
2223c=IN IP4 224.2.1.183\r
2224a=rtpmap:0 PCMU/8000\r
2225m=video 51372 RTP/AVPF 98 99\r
2226c=IN IP4 224.2.1.184\r
2227a=rtpmap:98 H263-1998/90000\r
2228a=rtpmap:99 H261/90000\r
2229a=rtcp-fb:* nack\r
2230a=rtcp-fb:98 nack rpsi\r
2231a=rtcp-fb:* trr-int 1000\r
2232a=rtcp-fb:98 ccm vbcm 1 2\r
2233a=rtcp-fb:* ccm tmmbr smaxpr=120\r
2234";
2235
2236 let parsed = Session::parse(sdp.as_bytes()).unwrap();
2237 let mut written = vec![];
2238 parsed.write(&mut written).unwrap();
2239
2240 let v = fallible_iterator::convert(parsed.medias[1].attributes_typed::<RtcpFb>())
2241 .collect::<Vec<_>>()
2242 .expect("Valid vector of attributes");
2243 assert_eq!(v[0].pt, RtcpFbPt::Wildcard);
2244 assert_eq!(v[1].val, RtcpFbVal::Nack(Some(RtcpFbNack::Rpsi)));
2245 assert_eq!(v[2].val, RtcpFbVal::TrrInt(1000));
2246 assert_eq!(v[3].val, RtcpFbVal::Ccm(RtcpFbCcm::Vbcm(vec![1, 2])));
2247 assert_eq!(
2248 v[4].val,
2249 RtcpFbVal::Ccm(RtcpFbCcm::Tmmbr(Some("smaxpr=120".to_string())))
2250 );
2251 }
2252
2253 #[test]
2254 fn parse_group_attribute() {
2255 let sdp = "v=0\r
2256o=Laura 289083124 289083124 IN IP4 two.example.com\r
2257c=IN IP4 233.252.0.1/127\r
2258t=0 0\r
2259a=group:LS 1 2\r
2260m=audio 30000 RTP/AVP 0\r
2261a=mid:1\r
2262m=video 30002 RTP/AVP 31\r
2263a=mid:2\r
2264m=audio 30004 RTP/AVP 0\r
2265i=This media stream contains the Spanish translation\r
2266a=mid:3\r
2267";
2268 let parsed = Session::parse(sdp.as_bytes()).unwrap();
2269
2270 let g = parsed.attributes_typed::<Group>().collect::<Vec<_>>();
2271 assert_eq!(g.len(), 1);
2272 assert_eq!(g[0].as_ref().unwrap().semantics, GroupSemantics::LS);
2273 assert_eq!(
2274 g[0].as_ref().unwrap().mid_tags,
2275 vec!["1".to_string(), "2".to_string()]
2276 );
2277 }
2278
2279 #[test]
2280 fn parse_setup_attribute() {
2281 let sdp = "v=0\r
2282m=image 54111 TCP t38\r
2283c=IN IP4 192.0.2.2\r
2284a=setup:actpass\r
2285a=connection:new\r
2286";
2287 let media = Session::parse(sdp.as_bytes()).unwrap().medias;
2288
2289 let s = media[0].attributes_typed::<Setup>().collect::<Vec<_>>();
2290
2291 assert_eq!(s.len(), 1);
2292 assert_eq!(s[0].as_ref().unwrap().to_owned(), Setup::ActPass);
2293 }
2294
2295 #[test]
2296 fn parse_ssrc_attributes() {
2297 let sdp = "v=0\r
2298o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r
2299m=video 49174 RTP/AVPF 96 98\r
2300a=rtpmap:98 rtx/90000\r
2301a=fmtp:98 apt=96;rtx-time=3000\r
2302a=ssrc-group:FID 11111 22222\r
2303a=ssrc:11111 cname:user3@example.com\r
2304a=ssrc:22222 fmtp:0 0-15\r
2305a=ssrc-group:FID 33333 44444\r
2306a=ssrc:33333 cname:user3@example.com\r
2307a=ssrc:44444 cname:user3@example.com\r
2308a=ssrc:1698359993 rtcp:5003 IN IP4 127.0.0.1
2309";
2310
2311 let parsed = Session::parse(sdp.as_bytes()).unwrap();
2312 let m = &parsed.medias[0];
2313
2314 let ssrcs = m
2315 .attributes_typed::<Ssrc>()
2316 .filter(|s| {
2317 let Ok(ssrc) = s else { return false };
2318 ssrc.attribute == SsrcAttribute::Fmtp || ssrc.attribute == SsrcAttribute::Rtcp
2319 })
2320 .collect::<Vec<_>>();
2321
2322 let ssrc_id = ssrcs[0].as_ref().unwrap().ssrc_id;
2323
2324 let ssrc_groups = m
2325 .attributes_typed::<SsrcGroup>()
2326 .filter(|s| {
2327 let Ok(ssrc_group) = s else { return false };
2328
2329 ssrc_group.ssrc_ids[1] == ssrc_id
2330 })
2331 .collect::<Vec<_>>();
2332
2333 assert_eq!(
2334 ssrc_groups[0].as_ref().unwrap().semantics,
2335 GroupSemantics::FID
2336 );
2337
2338 assert_eq!(ssrcs[1].as_ref().unwrap().attribute, SsrcAttribute::Rtcp);
2339 }
2340
2341 #[test]
2342 fn parse_crypto_attributes() {
2343 let sdp = "v=0\r
2344o=sam 2890844526 2890842807 IN IP4 10.47.16.5\r
2345s=SRTP Discussion\r
2346i=A discussion of Secure RTP\r
2347u=http://www.example.com/seminars/srtp.pdf\r
2348e=marge@example.com (Marge Simpson)\r
2349c=IN IP4 168.2.17.12\r
2350t=2873397496 2873404696\r
2351m=audio 49170 RTP/SAVP 0\r
2352a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4 FEC_ORDER=SRTP_FEC\r
2353a=crypto:2 F8_128_HMAC_SHA1_80 inline:MTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5QUJjZGVm|2^20|1:4;inline:QUJjZGVmMTIzNDU2Nzg5QUJDREUwMTIzNDU2Nzg5|2^20|2:4 FEC_ORDER=FEC_SRTP\r
2354m=video 51372 RTP/SAVP 31\r
2355a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2|1066:4\r
2356";
2357
2358 let parsed = Session::parse(sdp.as_bytes()).unwrap();
2359 let a = &parsed.medias[0];
2360
2361 let audio_cryptos = a.attributes_typed::<Crypto>().collect::<Vec<_>>();
2362
2363 assert_eq!(
2364 audio_cryptos[0].as_ref().unwrap().crypto_suite,
2365 CryptoSuite::AesCm128HmacSha1_80
2366 );
2367 assert_eq!(audio_cryptos[1].as_ref().unwrap().key_params.len(), 2);
2368
2369 assert_eq!(
2370 audio_cryptos[1].as_ref().unwrap().key_params[1].mki_and_length,
2371 Some((2, 4))
2372 );
2373
2374 assert_eq!(
2375 audio_cryptos[1].as_ref().unwrap().session_params[0],
2376 SrtpSessionParam::FecOrder(FecOrder::FecSrtp)
2377 );
2378
2379 let v = &parsed.medias[1];
2380
2381 let video_cryptos = v
2382 .attributes_typed::<Crypto>()
2383 .filter(|c| {
2384 let Ok(crypto) = c else { return false };
2385
2386 crypto.tag == 1
2387 })
2388 .collect::<Vec<_>>();
2389
2390 let test_crypto = Crypto {
2391 tag: 1,
2392 crypto_suite: CryptoSuite::AesCm128HmacSha1_80,
2393 key_params: vec![SrtpKeyParam {
2394 key_and_salt: "YUJDZGVmZ2hpSktMbW9QUXJzVHVWd3l6MTIzNDU2".to_string(),
2395 lifetime: None,
2396 mki_and_length: Some((1066, 4)),
2397 }],
2398 session_params: Vec::new(),
2399 };
2400
2401 assert_eq!(&test_crypto, video_cryptos[0].as_ref().unwrap());
2402 }
2403
2404 #[test]
2405 fn write_crypto_attribute() {
2406 let crypto = Crypto {
2407 tag: 1,
2408 crypto_suite: CryptoSuite::AesCm128HmacSha1_80,
2409 key_params: vec![
2410 SrtpKeyParam {
2411 key_and_salt: "WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz".to_string(),
2412 lifetime: Some(1048576),
2413 mki_and_length: Some((1, 4)),
2414 },
2415 SrtpKeyParam {
2416 key_and_salt: "WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz".to_string(),
2417 lifetime: Some(1048576),
2418 mki_and_length: Some((1, 4)),
2419 },
2420 ],
2421 session_params: vec![SrtpSessionParam::FecOrder(FecOrder::SrtpFec)],
2422 };
2423
2424 assert_eq!(
2425 crypto.to_string(),
2426 "1 AES_CM_128_HMAC_SHA1_80 inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4;inline:WVNfX19zZW1jdGwgKCkgewkyMjA7fQp9CnVubGVz|2^20|1:4 FEC_ORDER=SRTP_FEC"
2427 );
2428 }
2429
2430 #[test]
2431 fn parse_candidate_attributes() {
2432 use std::net::{Ipv4Addr, Ipv6Addr};
2433
2434 let sdp = "v=0\r
2435o=- 2890844526 2890842807 IN IP4 192.168.1.1\r
2436s=-\r
2437c=IN IP4 192.168.1.1\r
2438t=0 0\r
2439m=audio 49152 RTP/AVP 0\r
2440a=candidate:1 1 UDP 2130706432 192.168.1.1 49152 typ host raddr 10.0.1.1 rport 49153 generation 0\r
2441a=candidate:2 1 UDP 1692467200 10.0.1.1 49152 typ srflx raddr 192.168.1.1 rport 49153\r
2442a=candidate:3 2 UDP 1692467184 192.168.1.1 49153 typ host\r
2443a=candidate:4 1 UDP 100 2001:db8::1 49152 typ host\r
2444a=candidate:5 1 UDP 50 192.168.1.1 49154 typ prflx\r
2445a=candidate:6 1 UDP 25 192.168.1.1 49155 typ relay raddr 10.0.0.1 rport 49156\r
2446a=candidate:7 1 UDP 10 192.168.1.1 49157 typ unknown_type\r
2447";
2448
2449 let session = Session::parse(sdp.as_bytes()).unwrap();
2450 let candidates: Vec<Candidate> =
2451 fallible_iterator::convert(session.medias[0].attributes_typed::<Candidate>())
2452 .collect::<Vec<_>>()
2453 .expect("Valid vector of candidates");
2454
2455 assert_eq!(candidates.len(), 7);
2456
2457 assert_eq!(candidates[0].foundation, "1");
2458 assert_eq!(candidates[0].component_id, 1);
2459 assert_eq!(
2460 candidates[0].address,
2461 CandidateAddress::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)))
2462 );
2463 assert_eq!(candidates[0].port, 49152);
2464 assert_eq!(candidates[0].typ, CandidateType::Host);
2465 assert_eq!(
2466 candidates[0].rel_addr,
2467 Some(IpAddr::V4(Ipv4Addr::new(10, 0, 1, 1)))
2468 );
2469 assert_eq!(candidates[0].rel_port, Some(49153));
2470 assert_eq!(
2471 candidates[0].extensions,
2472 vec![("generation".to_string(), "0".to_string())]
2473 );
2474
2475 assert_eq!(candidates[1].foundation, "2");
2476 assert_eq!(candidates[1].typ, CandidateType::Srflx);
2477 assert_eq!(
2478 candidates[1].rel_addr,
2479 Some(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)))
2480 );
2481 assert_eq!(candidates[1].rel_port, Some(49153));
2482
2483 assert_eq!(candidates[2].foundation, "3");
2484 assert_eq!(candidates[2].component_id, 2);
2485 assert_eq!(candidates[2].typ, CandidateType::Host);
2486
2487 assert_eq!(candidates[3].foundation, "4");
2488 assert_eq!(
2489 candidates[3].address,
2490 CandidateAddress::IpAddr(IpAddr::V6(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1)))
2491 );
2492 assert_eq!(candidates[3].typ, CandidateType::Host);
2493
2494 assert_eq!(candidates[4].foundation, "5");
2495 assert_eq!(candidates[4].typ, CandidateType::Prflx);
2496
2497 assert_eq!(candidates[5].foundation, "6");
2498 assert_eq!(candidates[5].typ, CandidateType::Relay);
2499
2500 assert_eq!(candidates[6].foundation, "7");
2501 assert_eq!(
2502 candidates[6].typ,
2503 CandidateType::Other("unknown_type".to_string())
2504 );
2505 }
2506
2507 #[test]
2508 fn write_candidate() {
2509 use std::net::Ipv4Addr;
2510
2511 let candidate = Candidate {
2512 foundation: "abcd/1234".into(),
2513 component_id: 1,
2514 transport: "UDP".into(),
2515 priority: 2130706432,
2516 address: CandidateAddress::IpAddr(IpAddr::V4(Ipv4Addr::new(192, 168, 0, 1))),
2517 port: 49152,
2518 typ: CandidateType::Srflx,
2519 rel_addr: Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1))),
2520 rel_port: Some(49153),
2521 extensions: vec![("tcptype".to_string(), "active".to_string())],
2522 };
2523
2524 assert_eq!(
2525 candidate.to_string(),
2526 "abcd/1234 1 UDP 2130706432 192.168.0.1 49152 typ srflx raddr 10.0.0.1 rport 49153 tcptype active"
2527 );
2528 }
2529
2530 #[test]
2531 fn test_attribute_errors() {
2532 assert_eq!(
2534 "99".parse::<RtpMap>().unwrap_err(),
2535 AttributeError::UnsupportedFormat {
2536 val: "99".to_string(),
2537 attr: "rtpmap".to_string()
2538 }
2539 );
2540 assert_eq!(
2541 "abc 90000".parse::<RtpMap>().unwrap_err(),
2542 AttributeError::InvalidParamValue {
2543 param: "Payload type".to_string(),
2544 val: "abc".to_string(),
2545 attr: "rtpmap".to_string()
2546 }
2547 );
2548 assert_eq!(
2549 "200 enc/90000".parse::<RtpMap>().unwrap_err(),
2550 AttributeError::InvalidParamValue {
2551 param: "Payload type".to_string(),
2552 val: "200(expected 0-127)".to_string(),
2553 attr: "rtpmap".to_string()
2554 }
2555 );
2556 assert_eq!(
2557 "99 ".parse::<RtpMap>().unwrap_err(),
2558 AttributeError::ParamNotFound {
2559 param: "Clock rate".to_string(),
2560 attr: "rtpmap".to_string()
2561 }
2562 );
2563 assert_eq!(
2564 "99 /".parse::<RtpMap>().unwrap_err(),
2565 AttributeError::InvalidParamValue {
2566 param: "Clock rate".to_string(),
2567 val: "".to_string(),
2568 attr: "rtpmap".to_string()
2569 }
2570 );
2571
2572 assert_eq!(
2573 "invalid".parse::<Fmtp>().unwrap_err(),
2574 AttributeError::UnsupportedFormat {
2575 val: "invalid".to_string(),
2576 attr: "fmtp".to_string()
2577 }
2578 );
2579 assert_eq!(
2580 "abc profile=1".parse::<Fmtp>().unwrap_err(),
2581 AttributeError::InvalidParamValue {
2582 param: "fmtp".to_string(),
2583 val: "abc".to_string(),
2584 attr: "fmtp".to_string()
2585 }
2586 );
2587
2588 assert_eq!(
2590 "".parse::<Rtcp>().unwrap_err(),
2591 AttributeError::InvalidParamValue {
2592 param: "Port".to_string(),
2593 val: "".to_string(),
2594 attr: "rtcp".to_string()
2595 }
2596 );
2597 assert_eq!(
2598 "abc IN IP4 127.0.0.1".parse::<Rtcp>().unwrap_err(),
2599 AttributeError::InvalidParamValue {
2600 param: "Port".to_string(),
2601 val: "abc".to_string(),
2602 attr: "rtcp".to_string()
2603 }
2604 );
2605
2606 assert_eq!(
2608 "".parse::<Fingerprint>().unwrap_err(),
2609 AttributeError::ParamNotFound {
2610 param: "Hash value".to_string(),
2611 attr: "fingerprint".to_string()
2612 }
2613 );
2614 assert_eq!(
2615 "SHA-1".parse::<Fingerprint>().unwrap_err(),
2616 AttributeError::ParamNotFound {
2617 param: "Hash value".to_string(),
2618 attr: "fingerprint".to_string()
2619 }
2620 );
2621
2622 assert_eq!(
2624 "".parse::<Candidate>().unwrap_err(),
2625 AttributeError::ParamNotFound {
2626 param: "Component id".to_string(),
2627 attr: "candidate".to_string()
2628 }
2629 );
2630 assert_eq!(
2631 "1 1 UDP 100".parse::<Candidate>().unwrap_err(),
2632 AttributeError::ParamNotFound {
2633 param: "Address".to_string(),
2634 attr: "candidate".to_string()
2635 }
2636 );
2637
2638 assert_eq!(
2640 "".parse::<ExtMap>().unwrap_err(),
2641 AttributeError::InvalidParamValue {
2642 param: "Id".to_string(),
2643 val: "".to_string(),
2644 attr: "extmap".to_string()
2645 }
2646 );
2647 assert_eq!(
2648 "999999 http://example.com".parse::<ExtMap>().unwrap_err(),
2649 AttributeError::InvalidParamValue {
2650 param: "Id".to_string(),
2651 val: "999999".to_string(),
2652 attr: "extmap".to_string()
2653 }
2654 );
2655
2656 assert_eq!(
2658 "".parse::<Group>().unwrap_err(),
2659 AttributeError::ParamNotFound {
2660 param: "Media identification tags".to_string(),
2661 attr: "group".to_string()
2662 }
2663 );
2664 assert_eq!(
2665 "LS".parse::<Group>().unwrap_err(),
2666 AttributeError::ParamNotFound {
2667 param: "Media identification tags".to_string(),
2668 attr: "group".to_string()
2669 }
2670 );
2671
2672 assert_eq!(
2674 "".parse::<Ssrc>().unwrap_err(),
2675 AttributeError::ParamNotFound {
2676 param: "Ssrc id".to_string(),
2677 attr: "ssrc".to_string()
2678 }
2679 );
2680 assert_eq!(
2681 "abc".parse::<Ssrc>().unwrap_err(),
2682 AttributeError::ParamNotFound {
2683 param: "Ssrc id".to_string(),
2684 attr: "ssrc".to_string()
2685 }
2686 );
2687
2688 let setup_err = "foo".parse::<Setup>().err().unwrap();
2690 assert!(matches!(setup_err, AttributeError::Other { .. }));
2691 assert_eq!(format!("{}", setup_err), "setup: Invalid Setup value foo");
2692
2693 assert_eq!(
2695 "".parse::<Crypto>().unwrap_err(),
2696 AttributeError::InvalidParamValue {
2697 param: "Tag".to_string(),
2698 val: "".to_string(),
2699 attr: "crypto".to_string()
2700 }
2701 );
2702 assert_eq!(
2703 "abc AES_CM_128_HMAC_SHA1_32 inline:key"
2704 .parse::<Crypto>()
2705 .unwrap_err(),
2706 AttributeError::InvalidParamValue {
2707 param: "Tag".to_string(),
2708 val: "abc".to_string(),
2709 attr: "crypto".to_string()
2710 }
2711 );
2712
2713 assert_eq!(
2715 "".parse::<RtcpFb>().unwrap_err(),
2716 AttributeError::InvalidParamValue {
2717 param: "Payload format".to_string(),
2718 val: "".to_string(),
2719 attr: "rtcp-fb".to_string()
2720 }
2721 );
2722 assert_eq!(
2723 "*".parse::<RtcpFb>().unwrap_err(),
2724 AttributeError::ParamNotFound {
2725 param: "Rtcp feedback value".to_string(),
2726 attr: "rtcp-fb".to_string()
2727 }
2728 );
2729 assert_eq!(
2730 "1 ack ccfb".parse::<RtcpFb>().unwrap_err(),
2731 AttributeError::InvalidParamValue {
2732 param: "Payload type of Congestion control feedback (ccfb)".to_string(),
2733 val: "1(expected wildcard (*))".to_string(),
2734 attr: "rtcp-fb".to_string()
2735 }
2736 );
2737
2738 let media = Media {
2740 media: "video".into(),
2741 port: 1234,
2742 num_ports: None,
2743 proto: "RTP/SAVPF".into(),
2744 fmt: "".into(),
2745 media_title: None,
2746 connections: vec![],
2747 bandwidths: vec![],
2748 key: None,
2749 attributes: vec![Attribute {
2750 attribute: "rtpmap".into(),
2751 value: None,
2752 }],
2753 };
2754 assert_eq!(
2755 media
2756 .attributes_typed::<RtpMap>()
2757 .collect::<Vec<Result<RtpMap, AttributeError>>>()
2758 .remove(0)
2759 .unwrap_err(),
2760 AttributeError::Other {
2761 error: "No value for the attribute".to_string(),
2762 attr: "rtpmap".to_string()
2763 }
2764 );
2765 }
2766
2767 #[test]
2768 fn parse_rtcp_address() {
2769 let sdp = "v=0\r
2770o=alice 3203093520 3203093520 IN IP4 host.example.com\r
2771s=parse rtcp attribute address test\r
2772a=rtcp:5000 IN IP4 127.0.0.1\r
2773a=rtcp:5000 IN IP4 127.0.0.0/24\r
2774a=rtcp:5000 IN IP6 ::1\r
2775a=rtcp:5000 IN NONIP non-IP\r
2776";
2777
2778 let parsed = Session::parse(sdp.as_bytes()).unwrap();
2779 let mut rtcp_attr_iter = parsed.attributes_typed::<Rtcp>();
2780
2781 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
2782 assert_eq!(rtcp.addrtype, AddrType::Ip4);
2783 assert_eq!(&rtcp.connection_address, "127.0.0.1");
2784 assert_eq!(
2785 rtcp.try_parse_connection_ip_address().unwrap(),
2786 IpAddr::V4(Ipv4Addr::LOCALHOST),
2787 );
2788
2789 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
2790 assert_eq!(rtcp.addrtype, AddrType::Ip4);
2791 assert_eq!(&rtcp.connection_address, "127.0.0.0/24");
2792 assert_eq!(
2793 &rtcp
2794 .try_parse_connection_ip_address()
2795 .expect_err("IPv4 with mask")
2796 .to_string(),
2797 "127.0.0.0/24"
2798 );
2799
2800 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
2801 assert_eq!(rtcp.addrtype, AddrType::Ip6);
2802 assert_eq!(&rtcp.connection_address, "::1");
2803 assert_eq!(
2804 rtcp.try_parse_connection_ip_address().unwrap(),
2805 IpAddr::V6(Ipv6Addr::LOCALHOST),
2806 );
2807
2808 let rtcp = rtcp_attr_iter.next().unwrap().unwrap();
2809 assert_eq!(rtcp.addrtype, AddrType::Other("NONIP".to_string()));
2810 assert_eq!(&rtcp.connection_address, "non-IP");
2811 assert_eq!(
2812 rtcp.try_parse_connection_ip_address().unwrap_err(),
2813 "non-IP",
2814 );
2815 }
2816}