ezk_sdp_types/
media.rs

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    /// RTP over UDP
52    RtpAvp,
53
54    /// RTP with [RFC4585](https://www.rfc-editor.org/rfc/rfc4585.html)
55    RtpAvpf,
56
57    /// SRTP over UDP
58    RtpSavp,
59
60    /// SRTP with [RFC5124](https://www.rfc-editor.org/rfc/rfc5124.html)
61    RtpSavpf,
62
63    /// DTLS-SRTP
64    UdpTlsRtpSavp,
65
66    /// DTLS-SRTP with [RFC5124](https://www.rfc-editor.org/rfc/rfc5124.html)
67    UdpTlsRtpSavpf,
68
69    /// Other unknown
70    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/// Media field (`m=`)
112///
113/// [RFC8866](https://www.rfc-editor.org/rfc/rfc8866.html#section-5.14)
114#[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}