xmpp_parsers/
jingle_rtp.rs

1// Copyright (c) 2019-2020 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7use xso::{AsXml, FromXml};
8
9use crate::jingle_rtcp_fb::RtcpFb;
10use crate::jingle_rtp_hdrext::RtpHdrext;
11use crate::jingle_ssma::{Group, Source};
12use crate::ns;
13
14/// Specifies the ability to multiplex RTP Data and Control Packets on a single port as
15/// described in RFC 5761.
16#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
17#[xml(namespace = ns::JINGLE_RTP, name = "rtcp-mux")]
18pub struct RtcpMux;
19
20/// Wrapper element describing an RTP session.
21#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
22#[xml(namespace = ns::JINGLE_RTP, name = "description")]
23pub struct Description {
24    /// Specifies the media type, such as "audio" or "video", where the media type SHOULD be as
25    /// registered at IANA MIME Media Types Registry.
26    #[xml(attribute)]
27    pub media: String,
28
29    /// 32-bit synchronization source for this media stream, as defined in RFC 3550.
30    #[xml(attribute(default))]
31    pub ssrc: Option<u32>,
32
33    /// List of encodings that can be used for this RTP stream.
34    #[xml(child(n = ..))]
35    pub payload_types: Vec<PayloadType>,
36
37    /// Specifies the ability to multiplex RTP Data and Control Packets on a single port as
38    /// described in RFC 5761.
39    #[xml(child(default))]
40    pub rtcp_mux: Option<RtcpMux>,
41
42    /// List of ssrc-group.
43    #[xml(child(n = ..))]
44    pub ssrc_groups: Vec<Group>,
45
46    /// List of ssrc.
47    #[xml(child(n = ..))]
48    pub ssrcs: Vec<Source>,
49
50    /// List of header extensions.
51    #[xml(child(n = ..))]
52    pub hdrexts: Vec<RtpHdrext>,
53    // TODO: Add support for <encryption/> and <bandwidth/>.
54}
55
56impl Description {
57    /// Create a new RTP description.
58    pub fn new(media: String) -> Description {
59        Description {
60            media,
61            ssrc: None,
62            payload_types: Vec::new(),
63            rtcp_mux: None,
64            ssrc_groups: Vec::new(),
65            ssrcs: Vec::new(),
66            hdrexts: Vec::new(),
67        }
68    }
69}
70
71generate_attribute!(
72    /// The number of channels.
73    Channels,
74    "channels",
75    u8,
76    Default = 1
77);
78
79/// An encoding that can be used for an RTP stream.
80#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
81#[xml(namespace = ns::JINGLE_RTP, name = "payload-type")]
82pub struct PayloadType {
83    /// The number of channels.
84    #[xml(attribute(default))]
85    pub channels: Channels,
86
87    /// The sampling frequency in Hertz.
88    #[xml(attribute(default))]
89    pub clockrate: Option<u32>,
90
91    /// The payload identifier.
92    #[xml(attribute)]
93    pub id: u8,
94
95    /// Maximum packet time as specified in RFC 4566.
96    #[xml(attribute(default))]
97    pub maxptime: Option<u32>,
98
99    /// The appropriate subtype of the MIME type.
100    #[xml(attribute(default))]
101    pub name: Option<String>,
102
103    /// Packet time as specified in RFC 4566.
104    #[xml(attribute(default))]
105    pub ptime: Option<u32>,
106
107    /// List of parameters specifying this payload-type.
108    ///
109    /// Their order MUST be ignored.
110    #[xml(child(n = ..))]
111    pub parameters: Vec<Parameter>,
112
113    /// List of rtcp-fb parameters from XEP-0293.
114    #[xml(child(n = ..))]
115    pub rtcp_fbs: Vec<RtcpFb>,
116}
117
118impl PayloadType {
119    /// Create a new RTP payload-type.
120    pub fn new(id: u8, name: String, clockrate: u32, channels: u8) -> PayloadType {
121        PayloadType {
122            channels: Channels(channels),
123            clockrate: Some(clockrate),
124            id,
125            maxptime: None,
126            name: Some(name),
127            ptime: None,
128            parameters: Vec::new(),
129            rtcp_fbs: Vec::new(),
130        }
131    }
132
133    /// Create a new RTP payload-type without a clockrate.  Warning: this is invalid as per
134    /// RFC 4566!
135    pub fn without_clockrate(id: u8, name: String) -> PayloadType {
136        PayloadType {
137            channels: Default::default(),
138            clockrate: None,
139            id,
140            maxptime: None,
141            name: Some(name),
142            ptime: None,
143            parameters: Vec::new(),
144            rtcp_fbs: Vec::new(),
145        }
146    }
147}
148
149/// Parameter related to a payload.
150#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
151#[xml(namespace = ns::JINGLE_RTP, name = "parameter")]
152pub struct Parameter {
153    /// The name of the parameter, from the list at
154    /// <https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml>
155    #[xml(attribute)]
156    pub name: String,
157
158    /// The value of this parameter.
159    #[xml(attribute)]
160    pub value: String,
161}
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use minidom::Element;
167
168    #[cfg(target_pointer_width = "32")]
169    #[test]
170    fn test_size() {
171        assert_size!(Description, 72);
172        assert_size!(Channels, 1);
173        assert_size!(PayloadType, 64);
174        assert_size!(Parameter, 24);
175    }
176
177    #[cfg(target_pointer_width = "64")]
178    #[test]
179    fn test_size() {
180        assert_size!(Description, 136);
181        assert_size!(Channels, 1);
182        assert_size!(PayloadType, 104);
183        assert_size!(Parameter, 48);
184    }
185
186    #[test]
187    fn test_simple() {
188        let elem: Element = "<description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>
189    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='2' clockrate='48000' id='96' name='OPUS'/>
190    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='32000' id='105' name='SPEEX'/>
191    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='8000' id='9' name='G722'/>
192    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='16000' id='106' name='SPEEX'/>
193    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='8000' id='8' name='PCMA'/>
194    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='8000' id='0' name='PCMU'/>
195    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='8000' id='107' name='SPEEX'/>
196    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='8000' id='99' name='AMR'>
197        <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='octet-align' value='1'/>
198        <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='crc' value='0'/>
199        <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='robust-sorting' value='0'/>
200        <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='interleaving' value='0'/>
201    </payload-type>
202    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='48000' id='100' name='telephone-event'>
203        <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='events' value='0-15'/>
204    </payload-type>
205    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='16000' id='101' name='telephone-event'>
206        <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='events' value='0-15'/>
207    </payload-type>
208    <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='8000' id='102' name='telephone-event'>
209        <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='events' value='0-15'/>
210    </payload-type>
211</description>"
212                .parse()
213                .unwrap();
214        let desc = Description::try_from(elem).unwrap();
215        assert_eq!(desc.media, "audio");
216        assert_eq!(desc.ssrc, None);
217    }
218}