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