1use crate::{not_whitespace, slash_num};
2use bytes::Bytes;
3use bytesstr::BytesStr;
4use internal::{ws, IResult};
5use nom::branch::alt;
6use nom::bytes::complete::{tag, take_while1};
7use nom::character::complete::digit1;
8use nom::combinator::{map, map_res, opt};
9use nom::error::context;
10use nom::multi::many0;
11use std::fmt;
12use std::str::FromStr;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum MediaType {
16 Audio,
17 Video,
18 Text,
19 App,
20}
21
22impl MediaType {
23 pub fn parse(i: &str) -> IResult<&str, Self> {
24 context(
25 "parsing media type",
26 alt((
27 map(tag("audio"), |_| MediaType::Audio),
28 map(tag("video"), |_| MediaType::Video),
29 map(tag("text"), |_| MediaType::Text),
30 map(tag("application"), |_| MediaType::App),
31 )),
32 )(i)
33 }
34}
35
36impl fmt::Display for MediaType {
37 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38 match self {
39 MediaType::Audio => f.write_str("audio"),
40 MediaType::Video => f.write_str("video"),
41 MediaType::Text => f.write_str("text"),
42 MediaType::App => f.write_str("application"),
43 }
44 }
45}
46
47#[derive(Debug, Clone, PartialEq, Eq)]
48pub enum TransportProtocol {
49 Unspecified,
50
51 RtpAvp,
53
54 RtpAvpf,
56
57 RtpSavp,
59
60 RtpSavpf,
62
63 UdpTlsRtpSavp,
65
66 UdpTlsRtpSavpf,
68
69 Other(BytesStr),
71}
72
73impl TransportProtocol {
74 pub fn parse(src: &Bytes) -> impl Fn(&str) -> IResult<&str, Self> + '_ {
75 move |i| {
76 alt((
77 map(tag("UDP/TLS/RTP/SAVPF"), |_| {
78 TransportProtocol::UdpTlsRtpSavpf
79 }),
80 map(tag("UDP/TLS/RTP/SAVP"), |_| {
81 TransportProtocol::UdpTlsRtpSavp
82 }),
83 map(tag("RTP/SAVPF"), |_| TransportProtocol::RtpSavpf),
84 map(tag("RTP/SAVP"), |_| TransportProtocol::RtpSavp),
85 map(tag("RTP/AVPF"), |_| TransportProtocol::RtpAvpf),
86 map(tag("RTP/AVP"), |_| TransportProtocol::RtpAvp),
87 map(tag("udp"), |_| TransportProtocol::Unspecified),
88 map(take_while1(not_whitespace), |tp| {
89 TransportProtocol::Other(BytesStr::from_parse(src, tp))
90 }),
91 ))(i)
92 }
93 }
94}
95
96impl fmt::Display for TransportProtocol {
97 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
98 match self {
99 TransportProtocol::Unspecified => f.write_str("udp"),
100 TransportProtocol::RtpAvp => f.write_str("RTP/AVP"),
101 TransportProtocol::RtpAvpf => f.write_str("RTP/AVPF"),
102 TransportProtocol::RtpSavp => f.write_str("RTP/SAVP"),
103 TransportProtocol::RtpSavpf => f.write_str("RTP/SAVPF"),
104 TransportProtocol::UdpTlsRtpSavp => f.write_str("UDP/TLS/RTP/SAVP"),
105 TransportProtocol::UdpTlsRtpSavpf => f.write_str("UDP/TLS/RTP/SAVPF"),
106 TransportProtocol::Other(str) => f.write_str(str),
107 }
108 }
109}
110
111#[derive(Debug, Clone)]
115pub struct Media {
116 pub media_type: MediaType,
117 pub port: u16,
118 pub ports_num: Option<u32>,
119 pub proto: TransportProtocol,
120 pub fmts: Vec<u8>,
121}
122
123impl Media {
124 pub fn parse<'i>(src: &Bytes, i: &'i str) -> IResult<&'i str, Self> {
125 context(
126 "parsing media field",
127 map(
128 ws((
129 MediaType::parse,
130 map_res(digit1, FromStr::from_str),
131 opt(slash_num),
132 TransportProtocol::parse(src),
133 many0(map(ws((map_res(digit1, FromStr::from_str),)), |t| t.0)),
134 )),
135 |(media, port, ports_num, proto, fmts)| Media {
136 media_type: media,
137 port,
138 ports_num,
139 proto,
140 fmts,
141 },
142 ),
143 )(i)
144 }
145}
146
147impl fmt::Display for Media {
148 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149 write!(f, "{}", self.media_type)?;
150
151 if let Some(ports_num) = &self.ports_num {
152 write!(f, " {}/{} ", self.port, ports_num)?;
153 } else {
154 write!(f, " {} ", self.port)?;
155 }
156
157 write!(f, "{}", self.proto)?;
158
159 for fmt in &self.fmts {
160 write!(f, " {}", fmt)?;
161 }
162
163 Ok(())
164 }
165}
166
167#[cfg(test)]
168mod test {
169 use super::*;
170 use bytesstr::BytesStr;
171
172 #[test]
173 fn media() {
174 let input = BytesStr::from_static("audio 54647 RTP/AVPF 96 97 98 0 8 18 101 99 100");
175
176 let (rem, media) = Media::parse(input.as_ref(), &input).unwrap();
177
178 assert_eq!(media.media_type, MediaType::Audio);
179 assert_eq!(media.port, 54647);
180 assert!(media.ports_num.is_none());
181 assert_eq!(media.proto, TransportProtocol::RtpAvpf);
182 assert_eq!(media.fmts, [96, 97, 98, 0, 8, 18, 101, 99, 100]);
183
184 assert!(rem.is_empty());
185 }
186}