oddity_sdp_protocol/
sdp.rs

1use std::fmt;
2use std::net::IpAddr;
3
4use super::{
5    codec::{CodecInfo, MediaAttributes},
6    fmt::FMT_RTP_PAYLOAD_DYNAMIC,
7    ip::ip_addr_type,
8    time::unix_epoch_timestamp,
9    timing::TimeRange,
10};
11
12#[derive(Debug, Clone)]
13pub struct Sdp {
14    /* v= */
15    pub version: Version,
16    /* o= */
17    pub origin_username: String,
18    pub origin_session_id: String,
19    pub origin_session_version: String,
20    pub origin_network_type: NetworkType,
21    pub origin_address_type: AddressType,
22    pub origin_unicast_address: String,
23    /* s= */
24    pub session_name: String,
25    /* i= */
26    pub session_description: Option<String>,
27    /* c= */
28    pub connection_network_type: NetworkType,
29    pub connection_address_type: AddressType,
30    pub connection_address: String,
31    /* t= */
32    pub timing: (u64, u64),
33    /* a= */
34    pub tags: Vec<Tag>,
35    /* ... */
36    pub media: Vec<Media>,
37}
38
39impl Sdp {
40    pub fn new(origin: IpAddr, name: String, destination: IpAddr, time_range: TimeRange) -> Self {
41        Self {
42            version: Version::V0,
43            origin_username: "-".to_string(),
44            origin_session_id: unix_epoch_timestamp().to_string(),
45            origin_session_version: 0_u64.to_string(),
46            origin_network_type: NetworkType::Internet,
47            origin_address_type: ip_addr_type(&origin),
48            origin_unicast_address: origin.to_string(),
49            session_name: name,
50            session_description: None,
51            connection_network_type: NetworkType::Internet,
52            connection_address_type: ip_addr_type(&destination),
53            connection_address: destination.to_string(),
54            tags: Vec::new(),
55            timing: time_range.into(),
56            media: Vec::new(),
57        }
58    }
59
60    pub fn with_username(mut self, username: &str) -> Self {
61        self.origin_username = username.to_string();
62        self
63    }
64
65    pub fn with_session_version(mut self, version: usize) -> Self {
66        self.origin_session_version = version.to_string();
67        self
68    }
69
70    pub fn with_description(mut self, description: &str) -> Self {
71        self.session_description = Some(description.to_string());
72        self
73    }
74
75    pub fn with_tag(mut self, tag: Tag) -> Self {
76        self.tags.push(tag);
77        self
78    }
79
80    pub fn with_tags(mut self, tags: impl IntoIterator<Item = Tag>) -> Self {
81        self.tags.extend(tags);
82        self
83    }
84
85    pub fn with_media(
86        mut self,
87        kind: Kind,
88        port: u16,
89        protocol: Protocol,
90        codec_info: CodecInfo,
91        direction: Direction,
92    ) -> Self {
93        let mut tags = codec_info.media_attributes();
94        tags.push(Tag::Property(direction.to_string()));
95
96        self.media.push(Media {
97            kind,
98            port,
99            protocol,
100            format: FMT_RTP_PAYLOAD_DYNAMIC,
101            tags,
102        });
103        self
104    }
105}
106
107impl fmt::Display for Sdp {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        writeln!(f, "v={}", self.version)?;
110        writeln!(
111            f,
112            "o={} {} {} {} {} {}",
113            self.origin_username,
114            self.origin_session_id,
115            self.origin_session_version,
116            self.origin_network_type,
117            self.origin_address_type,
118            self.origin_unicast_address
119        )?;
120
121        writeln!(f, "s={}", self.session_name)?;
122        if let Some(session_description) = self.session_description.as_ref() {
123            writeln!(f, "i={}", session_description)?;
124        }
125
126        writeln!(
127            f,
128            "c={} {} {}",
129            self.connection_network_type, self.connection_address_type, self.connection_address
130        )?;
131
132        writeln!(f, "t={} {}", self.timing.0, self.timing.1)?;
133
134        for tag in &self.tags {
135            writeln!(f, "a={}", tag)?;
136        }
137
138        for media in &self.media {
139            write!(f, "{}", media)?;
140        }
141
142        Ok(())
143    }
144}
145
146#[derive(Debug, Clone)]
147pub struct Media {
148    /* m= */
149    pub kind: Kind,
150    pub port: u16,
151    pub protocol: Protocol,
152    pub format: usize,
153    /* a= */
154    pub tags: Vec<Tag>,
155}
156
157impl fmt::Display for Media {
158    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
159        writeln!(
160            f,
161            "m={} {} {} {}",
162            self.kind, self.port, self.protocol, self.format,
163        )?;
164
165        for tag in &self.tags {
166            writeln!(f, "a={}", tag)?;
167        }
168
169        Ok(())
170    }
171}
172
173#[derive(Debug, Clone)]
174pub struct Timing {
175    pub start: u64,
176    pub stop: u64,
177}
178
179impl fmt::Display for Timing {
180    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
181        write!(f, "{} {}", self.start, self.stop)
182    }
183}
184
185#[derive(Debug, Clone)]
186pub enum Version {
187    V0,
188}
189
190impl fmt::Display for Version {
191    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
192        match self {
193            Version::V0 => write!(f, "0"),
194        }
195    }
196}
197
198#[derive(Debug, Clone)]
199pub enum NetworkType {
200    Internet,
201}
202
203impl fmt::Display for NetworkType {
204    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
205        match self {
206            NetworkType::Internet => write!(f, "IN"),
207        }
208    }
209}
210
211#[derive(Debug, Clone)]
212pub enum AddressType {
213    IpV4,
214    IpV6,
215}
216
217impl fmt::Display for AddressType {
218    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
219        match self {
220            AddressType::IpV4 => write!(f, "IP4"),
221            AddressType::IpV6 => write!(f, "IP6"),
222        }
223    }
224}
225
226#[derive(Debug, Clone)]
227pub enum Tag {
228    Property(String),
229    Value(String, String),
230}
231
232impl fmt::Display for Tag {
233    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
234        match self {
235            Tag::Property(value) => write!(f, "{value}"),
236            Tag::Value(variable, value) => write!(f, "{variable}:{value}"),
237        }
238    }
239}
240
241#[derive(Debug, Clone)]
242pub enum Direction {
243    ReceiveOnly,
244    SendOnly,
245    SendAndReceive,
246}
247
248impl fmt::Display for Direction {
249    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
250        match self {
251            Direction::ReceiveOnly => write!(f, "recvonly"),
252            Direction::SendOnly => write!(f, "sendonly"),
253            Direction::SendAndReceive => write!(f, "sendrecv"),
254        }
255    }
256}
257
258#[derive(Debug, Clone)]
259pub enum Kind {
260    Video,
261    Audio,
262    Text,
263    Application,
264    Message,
265}
266
267impl fmt::Display for Kind {
268    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
269        match self {
270            Kind::Video => write!(f, "video"),
271            Kind::Audio => write!(f, "audio"),
272            Kind::Text => write!(f, "text"),
273            Kind::Application => write!(f, "application"),
274            Kind::Message => write!(f, "message"),
275        }
276    }
277}
278
279#[derive(Debug, Clone)]
280pub enum Protocol {
281    RtpAvp,
282    RtpSAvp,
283}
284
285impl fmt::Display for Protocol {
286    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
287        match self {
288            Protocol::RtpAvp => write!(f, "RTP/AVP"),
289            Protocol::RtpSAvp => write!(f, "RTP/SAVP"),
290        }
291    }
292}