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 crate::jingle_rtcp_fb::RtcpFb;
8use crate::jingle_rtp_hdrext::RtpHdrext;
9use crate::jingle_ssma::{Group, Source};
10
11generate_empty_element!(
12 /// Specifies the ability to multiplex RTP Data and Control Packets on a single port as
13 /// described in RFC 5761.
14 RtcpMux,
15 "rtcp-mux",
16 JINGLE_RTP
17);
18
19generate_element!(
20 /// Wrapper element describing an RTP session.
21 Description, "description", JINGLE_RTP,
22 attributes: [
23 /// Namespace of the encryption scheme used.
24 media: Required<String> = "media",
25
26 /// User-friendly name for the encryption scheme, should be `None` for OTR,
27 /// legacy OpenPGP and OX.
28 // XXX: is this a String or an u32?! Refer to RFC 3550.
29 ssrc: Option<String> = "ssrc",
30 ],
31 children: [
32 /// List of encodings that can be used for this RTP stream.
33 payload_types: Vec<PayloadType> = ("payload-type", JINGLE_RTP) => PayloadType,
34
35 /// Specifies the ability to multiplex RTP Data and Control Packets on a single port as
36 /// described in RFC 5761.
37 rtcp_mux: Option<RtcpMux> = ("rtcp-mux", JINGLE_RTP) => RtcpMux,
38
39 /// List of ssrc-group.
40 ssrc_groups: Vec<Group> = ("ssrc-group", JINGLE_SSMA) => Group,
41
42 /// List of ssrc.
43 ssrcs: Vec<Source> = ("source", JINGLE_SSMA) => Source,
44
45 /// List of header extensions.
46 hdrexts: Vec<RtpHdrext> = ("rtp-hdrext", JINGLE_RTP_HDREXT) => RtpHdrext
47
48 // TODO: Add support for <encryption/> and <bandwidth/>.
49 ]
50);
51
52impl Description {
53 /// Create a new RTP description.
54 pub fn new(media: String) -> Description {
55 Description {
56 media,
57 ssrc: None,
58 payload_types: Vec::new(),
59 rtcp_mux: None,
60 ssrc_groups: Vec::new(),
61 ssrcs: Vec::new(),
62 hdrexts: Vec::new(),
63 }
64 }
65}
66
67generate_attribute!(
68 /// The number of channels.
69 Channels,
70 "channels",
71 u8,
72 Default = 1
73);
74
75generate_element!(
76 /// An encoding that can be used for an RTP stream.
77 PayloadType, "payload-type", JINGLE_RTP,
78 attributes: [
79 /// The number of channels.
80 channels: Default<Channels> = "channels",
81
82 /// The sampling frequency in Hertz.
83 clockrate: Option<u32> = "clockrate",
84
85 /// The payload identifier.
86 id: Required<u8> = "id",
87
88 /// Maximum packet time as specified in RFC 4566.
89 maxptime: Option<u32> = "maxptime",
90
91 /// The appropriate subtype of the MIME type.
92 name: Option<String> = "name",
93
94 /// Packet time as specified in RFC 4566.
95 ptime: Option<u32> = "ptime",
96 ],
97 children: [
98 /// List of parameters specifying this payload-type.
99 ///
100 /// Their order MUST be ignored.
101 parameters: Vec<Parameter> = ("parameter", JINGLE_RTP) => Parameter,
102
103 /// List of rtcp-fb parameters from XEP-0293.
104 rtcp_fbs: Vec<RtcpFb> = ("rtcp-fb", JINGLE_RTCP_FB) => RtcpFb
105 ]
106);
107
108impl PayloadType {
109 /// Create a new RTP payload-type.
110 pub fn new(id: u8, name: String, clockrate: u32, channels: u8) -> PayloadType {
111 PayloadType {
112 channels: Channels(channels),
113 clockrate: Some(clockrate),
114 id,
115 maxptime: None,
116 name: Some(name),
117 ptime: None,
118 parameters: Vec::new(),
119 rtcp_fbs: Vec::new(),
120 }
121 }
122
123 /// Create a new RTP payload-type without a clockrate. Warning: this is invalid as per
124 /// RFC 4566!
125 pub fn without_clockrate(id: u8, name: String) -> PayloadType {
126 PayloadType {
127 channels: Default::default(),
128 clockrate: None,
129 id,
130 maxptime: None,
131 name: Some(name),
132 ptime: None,
133 parameters: Vec::new(),
134 rtcp_fbs: Vec::new(),
135 }
136 }
137}
138
139generate_element!(
140 /// Parameter related to a payload.
141 Parameter, "parameter", JINGLE_RTP,
142 attributes: [
143 /// The name of the parameter, from the list at
144 /// <https://www.iana.org/assignments/sdp-parameters/sdp-parameters.xhtml>
145 name: Required<String> = "name",
146
147 /// The value of this parameter.
148 value: Required<String> = "value",
149 ]
150);
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use crate::Element;
156 use std::convert::TryFrom;
157
158 #[cfg(target_pointer_width = "32")]
159 #[test]
160 fn test_size() {
161 assert_size!(Description, 76);
162 assert_size!(Channels, 1);
163 assert_size!(PayloadType, 64);
164 assert_size!(Parameter, 24);
165 }
166
167 #[cfg(target_pointer_width = "64")]
168 #[test]
169 fn test_size() {
170 assert_size!(Description, 152);
171 assert_size!(Channels, 1);
172 assert_size!(PayloadType, 104);
173 assert_size!(Parameter, 48);
174 }
175
176 #[test]
177 fn test_simple() {
178 let elem: Element = "<description xmlns='urn:xmpp:jingle:apps:rtp:1' media='audio'>
179 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='2' clockrate='48000' id='96' name='OPUS'/>
180 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='32000' id='105' name='SPEEX'/>
181 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='8000' id='9' name='G722'/>
182 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='16000' id='106' name='SPEEX'/>
183 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='8000' id='8' name='PCMA'/>
184 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='8000' id='0' name='PCMU'/>
185 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='8000' id='107' name='SPEEX'/>
186 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' channels='1' clockrate='8000' id='99' name='AMR'>
187 <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='octet-align' value='1'/>
188 <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='crc' value='0'/>
189 <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='robust-sorting' value='0'/>
190 <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='interleaving' value='0'/>
191 </payload-type>
192 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='48000' id='100' name='telephone-event'>
193 <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='events' value='0-15'/>
194 </payload-type>
195 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='16000' id='101' name='telephone-event'>
196 <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='events' value='0-15'/>
197 </payload-type>
198 <payload-type xmlns='urn:xmpp:jingle:apps:rtp:1' clockrate='8000' id='102' name='telephone-event'>
199 <parameter xmlns='urn:xmpp:jingle:apps:rtp:1' name='events' value='0-15'/>
200 </payload-type>
201</description>"
202 .parse()
203 .unwrap();
204 let desc = Description::try_from(elem).unwrap();
205 assert_eq!(desc.media, "audio");
206 assert_eq!(desc.ssrc, None);
207 }
208}