1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
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 file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "signaling/src/sdp/SipccSdpMediaSection.h"
8
9 #include <ostream>
10 #include "signaling/src/sdp/SdpErrorHolder.h"
11
12 #ifdef CRLF
13 #undef CRLF
14 #endif
15 #define CRLF "\r\n"
16
17 namespace mozilla
18 {
19
20 unsigned int
GetPort() const21 SipccSdpMediaSection::GetPort() const
22 {
23 return mPort;
24 }
25
26 void
SetPort(unsigned int port)27 SipccSdpMediaSection::SetPort(unsigned int port)
28 {
29 mPort = port;
30 }
31
32 unsigned int
GetPortCount() const33 SipccSdpMediaSection::GetPortCount() const
34 {
35 return mPortCount;
36 }
37
38 SdpMediaSection::Protocol
GetProtocol() const39 SipccSdpMediaSection::GetProtocol() const
40 {
41 return mProtocol;
42 }
43
44 const SdpConnection&
GetConnection() const45 SipccSdpMediaSection::GetConnection() const
46 {
47 return *mConnection;
48 }
49
50 SdpConnection&
GetConnection()51 SipccSdpMediaSection::GetConnection()
52 {
53 return *mConnection;
54 }
55
56 uint32_t
GetBandwidth(const std::string & type) const57 SipccSdpMediaSection::GetBandwidth(const std::string& type) const
58 {
59 auto found = mBandwidths.find(type);
60 if (found == mBandwidths.end()) {
61 return 0;
62 }
63 return found->second;
64 }
65
66 const std::vector<std::string>&
GetFormats() const67 SipccSdpMediaSection::GetFormats() const
68 {
69 return mFormats;
70 }
71
72 const SdpAttributeList&
GetAttributeList() const73 SipccSdpMediaSection::GetAttributeList() const
74 {
75 return mAttributeList;
76 }
77
78 SdpAttributeList&
GetAttributeList()79 SipccSdpMediaSection::GetAttributeList()
80 {
81 return mAttributeList;
82 }
83
84 SdpDirectionAttribute
GetDirectionAttribute() const85 SipccSdpMediaSection::GetDirectionAttribute() const
86 {
87 return SdpDirectionAttribute(mAttributeList.GetDirection());
88 }
89
90 bool
Load(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder)91 SipccSdpMediaSection::Load(sdp_t* sdp, uint16_t level,
92 SdpErrorHolder& errorHolder)
93 {
94 switch (sdp_get_media_type(sdp, level)) {
95 case SDP_MEDIA_AUDIO:
96 mMediaType = kAudio;
97 break;
98 case SDP_MEDIA_VIDEO:
99 mMediaType = kVideo;
100 break;
101 case SDP_MEDIA_APPLICATION:
102 mMediaType = kApplication;
103 break;
104 case SDP_MEDIA_TEXT:
105 mMediaType = kText;
106 break;
107
108 default:
109 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
110 "Unsupported media section type");
111 return false;
112 }
113
114 mPort = sdp_get_media_portnum(sdp, level);
115 int32_t pc = sdp_get_media_portcount(sdp, level);
116 if (pc == SDP_INVALID_VALUE) {
117 // SDP_INVALID_VALUE (ie; -2) is used when there is no port count. :(
118 mPortCount = 0;
119 } else if (pc > static_cast<int32_t>(UINT16_MAX) || pc < 0) {
120 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
121 "Invalid port count");
122 return false;
123 } else {
124 mPortCount = pc;
125 }
126
127 if (!LoadProtocol(sdp, level, errorHolder)) {
128 return false;
129 }
130
131 if (!LoadFormats(sdp, level, errorHolder)) {
132 return false;
133 }
134
135 if (!mAttributeList.Load(sdp, level, errorHolder)) {
136 return false;
137 }
138
139 if (!ValidateSimulcast(sdp, level, errorHolder)) {
140 return false;
141 }
142
143 if (!mBandwidths.Load(sdp, level, errorHolder)) {
144 return false;
145 }
146
147 return LoadConnection(sdp, level, errorHolder);
148 }
149
150 bool
LoadProtocol(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder)151 SipccSdpMediaSection::LoadProtocol(sdp_t* sdp, uint16_t level,
152 SdpErrorHolder& errorHolder)
153 {
154 switch (sdp_get_media_transport(sdp, level)) {
155 case SDP_TRANSPORT_RTPAVP:
156 mProtocol = kRtpAvp;
157 break;
158 case SDP_TRANSPORT_RTPSAVP:
159 mProtocol = kRtpSavp;
160 break;
161 case SDP_TRANSPORT_RTPAVPF:
162 mProtocol = kRtpAvpf;
163 break;
164 case SDP_TRANSPORT_RTPSAVPF:
165 mProtocol = kRtpSavpf;
166 break;
167 case SDP_TRANSPORT_UDPTLSRTPSAVP:
168 mProtocol = kUdpTlsRtpSavp;
169 break;
170 case SDP_TRANSPORT_UDPTLSRTPSAVPF:
171 mProtocol = kUdpTlsRtpSavpf;
172 break;
173 case SDP_TRANSPORT_TCPTLSRTPSAVP:
174 mProtocol = kTcpTlsRtpSavp;
175 break;
176 case SDP_TRANSPORT_TCPTLSRTPSAVPF:
177 mProtocol = kTcpTlsRtpSavpf;
178 break;
179 case SDP_TRANSPORT_DTLSSCTP:
180 mProtocol = kDtlsSctp;
181 break;
182
183 default:
184 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
185 "Unsupported media transport type");
186 return false;
187 }
188 return true;
189 }
190
191 bool
LoadFormats(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder)192 SipccSdpMediaSection::LoadFormats(sdp_t* sdp,
193 uint16_t level,
194 SdpErrorHolder& errorHolder)
195 {
196 sdp_media_e mtype = sdp_get_media_type(sdp, level);
197
198 if (mtype == SDP_MEDIA_APPLICATION) {
199 uint32_t ptype = sdp_get_media_sctp_port(sdp, level);
200 std::ostringstream osPayloadType;
201 osPayloadType << ptype;
202 mFormats.push_back(osPayloadType.str());
203 } else if (mtype == SDP_MEDIA_AUDIO || mtype == SDP_MEDIA_VIDEO) {
204 uint16_t count = sdp_get_media_num_payload_types(sdp, level);
205 for (uint16_t i = 0; i < count; ++i) {
206 sdp_payload_ind_e indicator; // we ignore this, which is fine
207 uint32_t ptype =
208 sdp_get_media_payload_type(sdp, level, i + 1, &indicator);
209
210 if (GET_DYN_PAYLOAD_TYPE_VALUE(ptype) > UINT8_MAX) {
211 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
212 "Format is too large");
213 return false;
214 }
215
216 std::ostringstream osPayloadType;
217 // sipcc stores payload types in a funny way. When sipcc and the SDP it
218 // parsed differ on what payload type number should be used for a given
219 // codec, sipcc's value goes in the lower byte, and the SDP's value in
220 // the upper byte. When they do not differ, only the lower byte is used.
221 // We want what was in the SDP, verbatim.
222 osPayloadType << GET_DYN_PAYLOAD_TYPE_VALUE(ptype);
223 mFormats.push_back(osPayloadType.str());
224 }
225 }
226
227 return true;
228 }
229
230 bool
ValidateSimulcast(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder) const231 SipccSdpMediaSection::ValidateSimulcast(sdp_t* sdp, uint16_t level,
232 SdpErrorHolder& errorHolder) const
233 {
234 if (!GetAttributeList().HasAttribute(SdpAttribute::kSimulcastAttribute)) {
235 return true;
236 }
237
238 const SdpSimulcastAttribute& simulcast(GetAttributeList().GetSimulcast());
239 if (!ValidateSimulcastVersions(
240 sdp, level, simulcast.sendVersions, sdp::kSend, errorHolder)) {
241 return false;
242 }
243 if (!ValidateSimulcastVersions(
244 sdp, level, simulcast.recvVersions, sdp::kRecv, errorHolder)) {
245 return false;
246 }
247 return true;
248 }
249
250 bool
ValidateSimulcastVersions(sdp_t * sdp,uint16_t level,const SdpSimulcastAttribute::Versions & versions,sdp::Direction direction,SdpErrorHolder & errorHolder) const251 SipccSdpMediaSection::ValidateSimulcastVersions(
252 sdp_t* sdp,
253 uint16_t level,
254 const SdpSimulcastAttribute::Versions& versions,
255 sdp::Direction direction,
256 SdpErrorHolder& errorHolder) const
257 {
258 if (versions.IsSet() && !(direction & GetDirectionAttribute().mValue)) {
259 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
260 "simulcast attribute has a direction that is "
261 "inconsistent with the direction of this media "
262 "section.");
263 return false;
264 }
265
266 for (const SdpSimulcastAttribute::Version& version : versions) {
267 for (const std::string& id : version.choices) {
268 if (versions.type == SdpSimulcastAttribute::Versions::kRid) {
269 const SdpRidAttributeList::Rid* ridAttr = FindRid(id);
270 if (!ridAttr || (ridAttr->direction != direction)) {
271 std::ostringstream os;
272 os << "No rid attribute for \'" << id << "\'";
273 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
274 os.str());
275 return false;
276 }
277 } else if (versions.type == SdpSimulcastAttribute::Versions::kPt) {
278 if (std::find(mFormats.begin(), mFormats.end(), id)
279 == mFormats.end()) {
280 std::ostringstream os;
281 os << "No pt for \'" << id << "\'";
282 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
283 os.str());
284 return false;
285 }
286 }
287 }
288 }
289 return true;
290 }
291
292 bool
LoadConnection(sdp_t * sdp,uint16_t level,SdpErrorHolder & errorHolder)293 SipccSdpMediaSection::LoadConnection(sdp_t* sdp, uint16_t level,
294 SdpErrorHolder& errorHolder)
295 {
296 if (!sdp_connection_valid(sdp, level)) {
297 level = SDP_SESSION_LEVEL;
298 if (!sdp_connection_valid(sdp, level)) {
299 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
300 "Missing c= line");
301 return false;
302 }
303 }
304
305 sdp_nettype_e type = sdp_get_conn_nettype(sdp, level);
306 if (type != SDP_NT_INTERNET) {
307 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
308 "Unsupported network type");
309 return false;
310 }
311
312 sdp::AddrType addrType;
313 switch (sdp_get_conn_addrtype(sdp, level)) {
314 case SDP_AT_IP4:
315 addrType = sdp::kIPv4;
316 break;
317 case SDP_AT_IP6:
318 addrType = sdp::kIPv6;
319 break;
320 default:
321 errorHolder.AddParseError(sdp_get_media_line_number(sdp, level),
322 "Unsupported address type");
323 return false;
324 }
325
326 std::string address = sdp_get_conn_address(sdp, level);
327 int16_t ttl = static_cast<uint16_t>(sdp_get_mcast_ttl(sdp, level));
328 if (ttl < 0) {
329 ttl = 0;
330 }
331 int32_t numAddr =
332 static_cast<uint32_t>(sdp_get_mcast_num_of_addresses(sdp, level));
333 if (numAddr < 0) {
334 numAddr = 0;
335 }
336 mConnection = MakeUnique<SdpConnection>(addrType, address, ttl, numAddr);
337 return true;
338 }
339
340 void
AddCodec(const std::string & pt,const std::string & name,uint32_t clockrate,uint16_t channels)341 SipccSdpMediaSection::AddCodec(const std::string& pt, const std::string& name,
342 uint32_t clockrate, uint16_t channels)
343 {
344 mFormats.push_back(pt);
345
346 SdpRtpmapAttributeList* rtpmap = new SdpRtpmapAttributeList();
347 if (mAttributeList.HasAttribute(SdpAttribute::kRtpmapAttribute)) {
348 const SdpRtpmapAttributeList& old = mAttributeList.GetRtpmap();
349 for (auto it = old.mRtpmaps.begin(); it != old.mRtpmaps.end(); ++it) {
350 rtpmap->mRtpmaps.push_back(*it);
351 }
352 }
353 SdpRtpmapAttributeList::CodecType codec = SdpRtpmapAttributeList::kOtherCodec;
354 if (name == "opus") {
355 codec = SdpRtpmapAttributeList::kOpus;
356 } else if (name == "G722") {
357 codec = SdpRtpmapAttributeList::kG722;
358 } else if (name == "PCMU") {
359 codec = SdpRtpmapAttributeList::kPCMU;
360 } else if (name == "PCMA") {
361 codec = SdpRtpmapAttributeList::kPCMA;
362 } else if (name == "VP8") {
363 codec = SdpRtpmapAttributeList::kVP8;
364 } else if (name == "VP9") {
365 codec = SdpRtpmapAttributeList::kVP9;
366 } else if (name == "H264") {
367 codec = SdpRtpmapAttributeList::kH264;
368 }
369
370 rtpmap->PushEntry(pt, codec, name, clockrate, channels);
371 mAttributeList.SetAttribute(rtpmap);
372 }
373
374 void
ClearCodecs()375 SipccSdpMediaSection::ClearCodecs()
376 {
377 mFormats.clear();
378 mAttributeList.RemoveAttribute(SdpAttribute::kRtpmapAttribute);
379 mAttributeList.RemoveAttribute(SdpAttribute::kFmtpAttribute);
380 mAttributeList.RemoveAttribute(SdpAttribute::kSctpmapAttribute);
381 mAttributeList.RemoveAttribute(SdpAttribute::kRtcpFbAttribute);
382 }
383
384 void
AddDataChannel(const std::string & pt,const std::string & name,uint16_t streams)385 SipccSdpMediaSection::AddDataChannel(const std::string& pt,
386 const std::string& name, uint16_t streams)
387 {
388 // Only one allowed, for now. This may change as the specs (and deployments)
389 // evolve.
390 mFormats.clear();
391 mFormats.push_back(pt);
392 SdpSctpmapAttributeList* sctpmap = new SdpSctpmapAttributeList();
393 sctpmap->PushEntry(pt, name, streams);
394 mAttributeList.SetAttribute(sctpmap);
395 }
396
397 void
Serialize(std::ostream & os) const398 SipccSdpMediaSection::Serialize(std::ostream& os) const
399 {
400 os << "m=" << mMediaType << " " << mPort;
401 if (mPortCount) {
402 os << "/" << mPortCount;
403 }
404 os << " " << mProtocol;
405 for (auto i = mFormats.begin(); i != mFormats.end(); ++i) {
406 os << " " << (*i);
407 }
408 os << CRLF;
409
410 // We dont do i=
411
412 if (mConnection) {
413 os << *mConnection;
414 }
415
416 mBandwidths.Serialize(os);
417
418 // We dont do k= because they're evil
419
420 os << mAttributeList;
421 }
422
423 } // namespace mozilla
424