Line data Source code
1 : /* This Source Code Form is subject to the terms of the Mozilla Public
2 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "signaling/src/sdp/SdpHelper.h"
6 :
7 : #include "signaling/src/sdp/Sdp.h"
8 : #include "signaling/src/sdp/SdpMediaSection.h"
9 : #include "logging.h"
10 :
11 : #include "nsDebug.h"
12 : #include "nsError.h"
13 : #include "prprf.h"
14 :
15 : #include <string.h>
16 : #include <set>
17 :
18 : namespace mozilla {
19 0 : MOZ_MTLOG_MODULE("sdp")
20 :
21 : #define SDP_SET_ERROR(error) \
22 : do { \
23 : std::ostringstream os; \
24 : os << error; \
25 : mLastError = os.str(); \
26 : MOZ_MTLOG(ML_ERROR, mLastError); \
27 : } while (0);
28 :
29 : nsresult
30 0 : SdpHelper::CopyTransportParams(size_t numComponents,
31 : const SdpMediaSection& oldLocal,
32 : SdpMediaSection* newLocal)
33 : {
34 : // Copy over m-section details
35 0 : newLocal->SetPort(oldLocal.GetPort());
36 0 : newLocal->GetConnection() = oldLocal.GetConnection();
37 :
38 0 : const SdpAttributeList& oldLocalAttrs = oldLocal.GetAttributeList();
39 0 : SdpAttributeList& newLocalAttrs = newLocal->GetAttributeList();
40 :
41 : // Now we copy over attributes that won't be added by the usual logic
42 0 : if (oldLocalAttrs.HasAttribute(SdpAttribute::kCandidateAttribute) &&
43 : numComponents) {
44 : UniquePtr<SdpMultiStringAttribute> candidateAttrs(
45 0 : new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
46 0 : for (const std::string& candidate : oldLocalAttrs.GetCandidate()) {
47 : size_t component;
48 0 : nsresult rv = GetComponent(candidate, &component);
49 0 : NS_ENSURE_SUCCESS(rv, rv);
50 0 : if (numComponents >= component) {
51 0 : candidateAttrs->mValues.push_back(candidate);
52 : }
53 : }
54 0 : if (candidateAttrs->mValues.size()) {
55 0 : newLocalAttrs.SetAttribute(candidateAttrs.release());
56 : }
57 : }
58 :
59 0 : if (numComponents == 2 &&
60 0 : oldLocalAttrs.HasAttribute(SdpAttribute::kRtcpAttribute)) {
61 : // copy rtcp attribute if we had one that we are using
62 0 : newLocalAttrs.SetAttribute(new SdpRtcpAttribute(oldLocalAttrs.GetRtcp()));
63 : }
64 :
65 0 : return NS_OK;
66 : }
67 :
68 : bool
69 0 : SdpHelper::AreOldTransportParamsValid(const Sdp& oldAnswer,
70 : const Sdp& offerersPreviousSdp,
71 : const Sdp& newOffer,
72 : size_t level)
73 : {
74 0 : if (MsectionIsDisabled(oldAnswer.GetMediaSection(level)) ||
75 0 : MsectionIsDisabled(newOffer.GetMediaSection(level))) {
76 : // Obvious
77 0 : return false;
78 : }
79 :
80 0 : if (IsBundleSlave(oldAnswer, level)) {
81 : // The transport attributes on this m-section were thrown away, because it
82 : // was bundled.
83 0 : return false;
84 : }
85 :
86 0 : if (newOffer.GetMediaSection(level).GetAttributeList().HasAttribute(
87 0 : SdpAttribute::kBundleOnlyAttribute) &&
88 0 : IsBundleSlave(newOffer, level)) {
89 : // It never makes sense to put transport attributes in a bundle-only
90 : // m-section
91 0 : return false;
92 : }
93 :
94 0 : if (IceCredentialsDiffer(newOffer.GetMediaSection(level),
95 0 : offerersPreviousSdp.GetMediaSection(level))) {
96 0 : return false;
97 : }
98 :
99 0 : return true;
100 : }
101 :
102 : bool
103 0 : SdpHelper::IceCredentialsDiffer(const SdpMediaSection& msection1,
104 : const SdpMediaSection& msection2)
105 : {
106 0 : const SdpAttributeList& attrs1(msection1.GetAttributeList());
107 0 : const SdpAttributeList& attrs2(msection2.GetAttributeList());
108 :
109 0 : if ((attrs1.GetIceUfrag() != attrs2.GetIceUfrag()) ||
110 0 : (attrs1.GetIcePwd() != attrs2.GetIcePwd())) {
111 0 : return true;
112 : }
113 :
114 0 : return false;
115 : }
116 :
117 : nsresult
118 0 : SdpHelper::GetComponent(const std::string& candidate, size_t* component)
119 : {
120 : unsigned int temp;
121 0 : int32_t result = PR_sscanf(candidate.c_str(), "%*s %u", &temp);
122 0 : if (result == 1) {
123 0 : *component = temp;
124 0 : return NS_OK;
125 : }
126 0 : SDP_SET_ERROR("Malformed ICE candidate: " << candidate);
127 0 : return NS_ERROR_INVALID_ARG;
128 : }
129 :
130 : bool
131 0 : SdpHelper::MsectionIsDisabled(const SdpMediaSection& msection) const
132 : {
133 0 : return !msection.GetPort() &&
134 0 : !msection.GetAttributeList().HasAttribute(
135 0 : SdpAttribute::kBundleOnlyAttribute);
136 : }
137 :
138 : void
139 0 : SdpHelper::DisableMsection(Sdp* sdp, SdpMediaSection* msection)
140 : {
141 : // Make sure to remove the mid from any group attributes
142 0 : if (msection->GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
143 0 : std::string mid = msection->GetAttributeList().GetMid();
144 0 : if (sdp->GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) {
145 : UniquePtr<SdpGroupAttributeList> newGroupAttr(new SdpGroupAttributeList(
146 0 : sdp->GetAttributeList().GetGroup()));
147 0 : newGroupAttr->RemoveMid(mid);
148 0 : sdp->GetAttributeList().SetAttribute(newGroupAttr.release());
149 : }
150 : }
151 :
152 : // Clear out attributes.
153 0 : msection->GetAttributeList().Clear();
154 :
155 : auto* direction =
156 0 : new SdpDirectionAttribute(SdpDirectionAttribute::kInactive);
157 0 : msection->GetAttributeList().SetAttribute(direction);
158 0 : msection->SetPort(0);
159 :
160 0 : msection->ClearCodecs();
161 :
162 0 : auto mediaType = msection->GetMediaType();
163 0 : switch (mediaType) {
164 : case SdpMediaSection::kAudio:
165 0 : msection->AddCodec("0", "PCMU", 8000, 1);
166 0 : break;
167 : case SdpMediaSection::kVideo:
168 0 : msection->AddCodec("120", "VP8", 90000, 1);
169 0 : break;
170 : case SdpMediaSection::kApplication:
171 0 : msection->AddDataChannel("rejected", 0, 0, 0);
172 0 : break;
173 : default:
174 : // We need to have something here to fit the grammar, this seems safe
175 : // and 19 is a reserved payload type which should not be used by anyone.
176 0 : msection->AddCodec("19", "reserved", 8000, 1);
177 : }
178 0 : }
179 :
180 : void
181 0 : SdpHelper::GetBundleGroups(
182 : const Sdp& sdp,
183 : std::vector<SdpGroupAttributeList::Group>* bundleGroups) const
184 : {
185 0 : if (sdp.GetAttributeList().HasAttribute(SdpAttribute::kGroupAttribute)) {
186 0 : for (auto& group : sdp.GetAttributeList().GetGroup().mGroups) {
187 0 : if (group.semantics == SdpGroupAttributeList::kBundle) {
188 0 : bundleGroups->push_back(group);
189 : }
190 : }
191 : }
192 0 : }
193 :
194 : nsresult
195 0 : SdpHelper::GetBundledMids(const Sdp& sdp, BundledMids* bundledMids)
196 : {
197 0 : std::vector<SdpGroupAttributeList::Group> bundleGroups;
198 0 : GetBundleGroups(sdp, &bundleGroups);
199 :
200 0 : for (SdpGroupAttributeList::Group& group : bundleGroups) {
201 0 : if (group.tags.empty()) {
202 0 : SDP_SET_ERROR("Empty BUNDLE group");
203 0 : return NS_ERROR_INVALID_ARG;
204 : }
205 :
206 : const SdpMediaSection* masterBundleMsection(
207 0 : FindMsectionByMid(sdp, group.tags[0]));
208 :
209 0 : if (!masterBundleMsection) {
210 0 : SDP_SET_ERROR("mid specified for bundle transport in group attribute"
211 : " does not exist in the SDP. (mid=" << group.tags[0] << ")");
212 0 : return NS_ERROR_INVALID_ARG;
213 : }
214 :
215 0 : if (MsectionIsDisabled(*masterBundleMsection)) {
216 0 : SDP_SET_ERROR("mid specified for bundle transport in group attribute"
217 : " points at a disabled m-section. (mid=" << group.tags[0] << ")");
218 0 : return NS_ERROR_INVALID_ARG;
219 : }
220 :
221 0 : for (const std::string& mid : group.tags) {
222 0 : if (bundledMids->count(mid)) {
223 0 : SDP_SET_ERROR("mid \'" << mid << "\' appears more than once in a "
224 : "BUNDLE group");
225 0 : return NS_ERROR_INVALID_ARG;
226 : }
227 :
228 0 : (*bundledMids)[mid] = masterBundleMsection;
229 : }
230 : }
231 :
232 0 : return NS_OK;
233 : }
234 :
235 : bool
236 0 : SdpHelper::IsBundleSlave(const Sdp& sdp, uint16_t level)
237 : {
238 0 : auto& msection = sdp.GetMediaSection(level);
239 :
240 0 : if (!msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
241 : // No mid, definitely no bundle for this m-section
242 0 : return false;
243 : }
244 0 : std::string mid(msection.GetAttributeList().GetMid());
245 :
246 0 : BundledMids bundledMids;
247 0 : nsresult rv = GetBundledMids(sdp, &bundledMids);
248 0 : if (NS_FAILED(rv)) {
249 : // Should have been caught sooner.
250 0 : MOZ_ASSERT(false);
251 : return false;
252 : }
253 :
254 0 : if (bundledMids.count(mid) && level != bundledMids[mid]->GetLevel()) {
255 : // mid is bundled, and isn't the bundle m-section
256 0 : return true;
257 : }
258 :
259 0 : return false;
260 : }
261 :
262 : nsresult
263 0 : SdpHelper::GetMidFromLevel(const Sdp& sdp,
264 : uint16_t level,
265 : std::string* mid)
266 : {
267 0 : if (level >= sdp.GetMediaSectionCount()) {
268 0 : SDP_SET_ERROR("Index " << level << " out of range");
269 0 : return NS_ERROR_INVALID_ARG;
270 : }
271 :
272 0 : const SdpMediaSection& msection = sdp.GetMediaSection(level);
273 0 : const SdpAttributeList& attrList = msection.GetAttributeList();
274 :
275 : // grab the mid and set the outparam
276 0 : if (attrList.HasAttribute(SdpAttribute::kMidAttribute)) {
277 0 : *mid = attrList.GetMid();
278 : }
279 :
280 0 : return NS_OK;
281 : }
282 :
283 : nsresult
284 0 : SdpHelper::AddCandidateToSdp(Sdp* sdp,
285 : const std::string& candidateUntrimmed,
286 : const std::string& mid,
287 : uint16_t level)
288 : {
289 :
290 0 : if (level >= sdp->GetMediaSectionCount()) {
291 0 : SDP_SET_ERROR("Index " << level << " out of range");
292 0 : return NS_ERROR_INVALID_ARG;
293 : }
294 :
295 : // Trim off '[a=]candidate:'
296 0 : size_t begin = candidateUntrimmed.find(':');
297 0 : if (begin == std::string::npos) {
298 0 : SDP_SET_ERROR("Invalid candidate, no ':' (" << candidateUntrimmed << ")");
299 0 : return NS_ERROR_INVALID_ARG;
300 : }
301 0 : ++begin;
302 :
303 0 : std::string candidate = candidateUntrimmed.substr(begin);
304 :
305 : // https://tools.ietf.org/html/draft-ietf-rtcweb-jsep-11#section-3.4.2.1
306 : // Implementations receiving an ICE Candidate object MUST use the MID if
307 : // present, or the m= line index, if not (as it could have come from a
308 : // non-JSEP endpoint). (bug 1095793)
309 0 : SdpMediaSection* msection = 0;
310 0 : if (!mid.empty()) {
311 : // FindMsectionByMid could return nullptr
312 0 : msection = FindMsectionByMid(*sdp, mid);
313 :
314 : // Check to make sure mid matches what we'd get by
315 : // looking up the m= line using the level. (mjf)
316 0 : std::string checkMid;
317 0 : nsresult rv = GetMidFromLevel(*sdp, level, &checkMid);
318 0 : if (NS_FAILED(rv)) {
319 0 : return rv;
320 : }
321 0 : if (mid != checkMid) {
322 0 : SDP_SET_ERROR("Mismatch between mid and level - \"" << mid
323 : << "\" is not the mid for level " << level
324 : << "; \"" << checkMid << "\" is");
325 0 : return NS_ERROR_INVALID_ARG;
326 : }
327 : }
328 0 : if (!msection) {
329 0 : msection = &(sdp->GetMediaSection(level));
330 : }
331 :
332 0 : SdpAttributeList& attrList = msection->GetAttributeList();
333 :
334 0 : UniquePtr<SdpMultiStringAttribute> candidates;
335 0 : if (!attrList.HasAttribute(SdpAttribute::kCandidateAttribute)) {
336 : // Create new
337 : candidates.reset(
338 0 : new SdpMultiStringAttribute(SdpAttribute::kCandidateAttribute));
339 : } else {
340 : // Copy existing
341 : candidates.reset(new SdpMultiStringAttribute(
342 0 : *static_cast<const SdpMultiStringAttribute*>(
343 0 : attrList.GetAttribute(SdpAttribute::kCandidateAttribute))));
344 : }
345 0 : candidates->PushEntry(candidate);
346 0 : attrList.SetAttribute(candidates.release());
347 :
348 0 : return NS_OK;
349 : }
350 :
351 : void
352 0 : SdpHelper::SetIceGatheringComplete(Sdp* sdp,
353 : uint16_t level,
354 : BundledMids bundledMids)
355 : {
356 0 : SdpMediaSection& msection = sdp->GetMediaSection(level);
357 :
358 0 : if (kSlaveBundle == GetMsectionBundleType(*sdp,
359 : level,
360 : bundledMids,
361 : nullptr)) {
362 0 : return; // Slave bundle m-section. Skip.
363 : }
364 :
365 0 : SdpAttributeList& attrs = msection.GetAttributeList();
366 : attrs.SetAttribute(
367 0 : new SdpFlagAttribute(SdpAttribute::kEndOfCandidatesAttribute));
368 : // Remove trickle-ice option
369 0 : attrs.RemoveAttribute(SdpAttribute::kIceOptionsAttribute);
370 : }
371 :
372 : void
373 0 : SdpHelper::SetDefaultAddresses(const std::string& defaultCandidateAddr,
374 : uint16_t defaultCandidatePort,
375 : const std::string& defaultRtcpCandidateAddr,
376 : uint16_t defaultRtcpCandidatePort,
377 : Sdp* sdp,
378 : uint16_t level,
379 : BundledMids bundledMids)
380 : {
381 0 : SdpMediaSection& msection = sdp->GetMediaSection(level);
382 0 : std::string masterMid;
383 :
384 0 : MsectionBundleType bundleType = GetMsectionBundleType(*sdp,
385 : level,
386 : bundledMids,
387 0 : &masterMid);
388 0 : if (kSlaveBundle == bundleType) {
389 0 : return; // Slave bundle m-section. Skip.
390 : }
391 0 : if (kMasterBundle == bundleType) {
392 : // Master bundle m-section. Set defaultCandidateAddr and
393 : // defaultCandidatePort on all bundled m-sections.
394 0 : const SdpMediaSection* masterBundleMsection(bundledMids[masterMid]);
395 0 : for (auto i = bundledMids.begin(); i != bundledMids.end(); ++i) {
396 0 : if (i->second != masterBundleMsection) {
397 0 : continue;
398 : }
399 0 : SdpMediaSection* bundledMsection = FindMsectionByMid(*sdp, i->first);
400 0 : if (!bundledMsection) {
401 0 : MOZ_ASSERT(false);
402 : continue;
403 : }
404 0 : SetDefaultAddresses(defaultCandidateAddr,
405 : defaultCandidatePort,
406 : defaultRtcpCandidateAddr,
407 : defaultRtcpCandidatePort,
408 0 : bundledMsection);
409 : }
410 : }
411 :
412 0 : SetDefaultAddresses(defaultCandidateAddr,
413 : defaultCandidatePort,
414 : defaultRtcpCandidateAddr,
415 : defaultRtcpCandidatePort,
416 0 : &msection);
417 : }
418 :
419 : void
420 0 : SdpHelper::SetDefaultAddresses(const std::string& defaultCandidateAddr,
421 : uint16_t defaultCandidatePort,
422 : const std::string& defaultRtcpCandidateAddr,
423 : uint16_t defaultRtcpCandidatePort,
424 : SdpMediaSection* msection)
425 : {
426 0 : msection->GetConnection().SetAddress(defaultCandidateAddr);
427 0 : SdpAttributeList& attrList = msection->GetAttributeList();
428 :
429 : // only set the port if there is no bundle-only attribute
430 0 : if (!attrList.HasAttribute(SdpAttribute::kBundleOnlyAttribute)) {
431 0 : msection->SetPort(defaultCandidatePort);
432 : }
433 :
434 0 : if (!defaultRtcpCandidateAddr.empty()) {
435 0 : sdp::AddrType ipVersion = sdp::kIPv4;
436 0 : if (defaultRtcpCandidateAddr.find(':') != std::string::npos) {
437 0 : ipVersion = sdp::kIPv6;
438 : }
439 : attrList.SetAttribute(new SdpRtcpAttribute(
440 : defaultRtcpCandidatePort,
441 : sdp::kInternet,
442 : ipVersion,
443 0 : defaultRtcpCandidateAddr));
444 : }
445 0 : }
446 :
447 : nsresult
448 0 : SdpHelper::GetIdsFromMsid(const Sdp& sdp,
449 : const SdpMediaSection& msection,
450 : std::string* streamId,
451 : std::string* trackId)
452 : {
453 0 : if (!sdp.GetAttributeList().HasAttribute(
454 : SdpAttribute::kMsidSemanticAttribute)) {
455 0 : return NS_ERROR_NOT_AVAILABLE;
456 : }
457 :
458 0 : auto& msidSemantics = sdp.GetAttributeList().GetMsidSemantic().mMsidSemantics;
459 0 : std::vector<SdpMsidAttributeList::Msid> allMsids;
460 0 : nsresult rv = GetMsids(msection, &allMsids);
461 0 : NS_ENSURE_SUCCESS(rv, rv);
462 :
463 0 : bool allMsidsAreWebrtc = false;
464 0 : std::set<std::string> webrtcMsids;
465 :
466 0 : for (auto i = msidSemantics.begin(); i != msidSemantics.end(); ++i) {
467 0 : if (i->semantic == "WMS") {
468 0 : for (auto j = i->msids.begin(); j != i->msids.end(); ++j) {
469 0 : if (*j == "*") {
470 0 : allMsidsAreWebrtc = true;
471 : } else {
472 0 : webrtcMsids.insert(*j);
473 : }
474 : }
475 0 : break;
476 : }
477 : }
478 :
479 0 : bool found = false;
480 :
481 0 : for (auto i = allMsids.begin(); i != allMsids.end(); ++i) {
482 0 : if (allMsidsAreWebrtc || webrtcMsids.count(i->identifier)) {
483 0 : if (i->appdata.empty()) {
484 0 : SDP_SET_ERROR("Invalid webrtc msid at level " << msection.GetLevel()
485 : << ": Missing track id.");
486 0 : return NS_ERROR_INVALID_ARG;
487 : }
488 0 : if (!found) {
489 0 : *streamId = i->identifier;
490 0 : *trackId = i->appdata;
491 0 : found = true;
492 0 : } else if ((*streamId != i->identifier) || (*trackId != i->appdata)) {
493 0 : MOZ_MTLOG(ML_WARNING, "Found multiple different webrtc msids in "
494 : "m-section " << msection.GetLevel() << ". The "
495 : "behavior w/o transceivers is undefined.");
496 : }
497 : }
498 : }
499 :
500 0 : if (!found) {
501 0 : return NS_ERROR_NOT_AVAILABLE;
502 : }
503 :
504 0 : return NS_OK;
505 : }
506 :
507 : nsresult
508 0 : SdpHelper::GetMsids(const SdpMediaSection& msection,
509 : std::vector<SdpMsidAttributeList::Msid>* msids)
510 : {
511 0 : if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMsidAttribute)) {
512 0 : *msids = msection.GetAttributeList().GetMsid().mMsids;
513 : }
514 :
515 : // Can we find some additional msids in ssrc attributes?
516 : // (Chrome does not put plain-old msid attributes in its SDP)
517 0 : if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
518 0 : auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;
519 :
520 0 : for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
521 0 : if (i->attribute.find("msid:") == 0) {
522 0 : std::string streamId;
523 0 : std::string trackId;
524 0 : nsresult rv = ParseMsid(i->attribute, &streamId, &trackId);
525 0 : NS_ENSURE_SUCCESS(rv, rv);
526 0 : msids->push_back({streamId, trackId});
527 : }
528 : }
529 : }
530 :
531 0 : return NS_OK;
532 : }
533 :
534 : nsresult
535 0 : SdpHelper::ParseMsid(const std::string& msidAttribute,
536 : std::string* streamId,
537 : std::string* trackId)
538 : {
539 : // Would be nice if SdpSsrcAttributeList could parse out the contained
540 : // attribute, but at least the parse here is simple.
541 : // We are being very forgiving here wrt whitespace; tabs are not actually
542 : // allowed, nor is leading/trailing whitespace.
543 0 : size_t streamIdStart = msidAttribute.find_first_not_of(" \t", 5);
544 : // We do not assume the appdata token is here, since this is not
545 : // necessarily a webrtc msid
546 0 : if (streamIdStart == std::string::npos) {
547 0 : SDP_SET_ERROR("Malformed source-level msid attribute: "
548 : << msidAttribute);
549 0 : return NS_ERROR_INVALID_ARG;
550 : }
551 :
552 0 : size_t streamIdEnd = msidAttribute.find_first_of(" \t", streamIdStart);
553 0 : if (streamIdEnd == std::string::npos) {
554 0 : streamIdEnd = msidAttribute.size();
555 : }
556 :
557 : size_t trackIdStart =
558 0 : msidAttribute.find_first_not_of(" \t", streamIdEnd);
559 0 : if (trackIdStart == std::string::npos) {
560 0 : trackIdStart = msidAttribute.size();
561 : }
562 :
563 0 : size_t trackIdEnd = msidAttribute.find_first_of(" \t", trackIdStart);
564 0 : if (trackIdEnd == std::string::npos) {
565 0 : trackIdEnd = msidAttribute.size();
566 : }
567 :
568 0 : size_t streamIdSize = streamIdEnd - streamIdStart;
569 0 : size_t trackIdSize = trackIdEnd - trackIdStart;
570 :
571 0 : *streamId = msidAttribute.substr(streamIdStart, streamIdSize);
572 0 : *trackId = msidAttribute.substr(trackIdStart, trackIdSize);
573 0 : return NS_OK;
574 : }
575 :
576 : void
577 0 : SdpHelper::SetupMsidSemantic(const std::vector<std::string>& msids,
578 : Sdp* sdp) const
579 : {
580 0 : if (!msids.empty()) {
581 : UniquePtr<SdpMsidSemanticAttributeList> msidSemantics(
582 0 : new SdpMsidSemanticAttributeList);
583 0 : msidSemantics->PushEntry("WMS", msids);
584 0 : sdp->GetAttributeList().SetAttribute(msidSemantics.release());
585 : }
586 0 : }
587 :
588 : std::string
589 0 : SdpHelper::GetCNAME(const SdpMediaSection& msection) const
590 : {
591 0 : if (msection.GetAttributeList().HasAttribute(SdpAttribute::kSsrcAttribute)) {
592 0 : auto& ssrcs = msection.GetAttributeList().GetSsrc().mSsrcs;
593 0 : for (auto i = ssrcs.begin(); i != ssrcs.end(); ++i) {
594 0 : if (i->attribute.find("cname:") == 0) {
595 0 : return i->attribute.substr(6);
596 : }
597 : }
598 : }
599 0 : return "";
600 : }
601 :
602 : const SdpMediaSection*
603 0 : SdpHelper::FindMsectionByMid(const Sdp& sdp,
604 : const std::string& mid) const
605 : {
606 0 : for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
607 0 : auto& attrs = sdp.GetMediaSection(i).GetAttributeList();
608 0 : if (attrs.HasAttribute(SdpAttribute::kMidAttribute) &&
609 0 : attrs.GetMid() == mid) {
610 0 : return &sdp.GetMediaSection(i);
611 : }
612 : }
613 0 : return nullptr;
614 : }
615 :
616 : SdpMediaSection*
617 0 : SdpHelper::FindMsectionByMid(Sdp& sdp,
618 : const std::string& mid) const
619 : {
620 0 : for (size_t i = 0; i < sdp.GetMediaSectionCount(); ++i) {
621 0 : auto& attrs = sdp.GetMediaSection(i).GetAttributeList();
622 0 : if (attrs.HasAttribute(SdpAttribute::kMidAttribute) &&
623 0 : attrs.GetMid() == mid) {
624 0 : return &sdp.GetMediaSection(i);
625 : }
626 : }
627 0 : return nullptr;
628 : }
629 :
630 : nsresult
631 0 : SdpHelper::CopyStickyParams(const SdpMediaSection& source,
632 : SdpMediaSection* dest)
633 : {
634 0 : auto& sourceAttrs = source.GetAttributeList();
635 0 : auto& destAttrs = dest->GetAttributeList();
636 :
637 : // There's no reason to renegotiate rtcp-mux
638 0 : if (sourceAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
639 : destAttrs.SetAttribute(
640 0 : new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
641 : }
642 :
643 : // mid should stay the same
644 0 : if (sourceAttrs.HasAttribute(SdpAttribute::kMidAttribute)) {
645 0 : destAttrs.SetAttribute(
646 : new SdpStringAttribute(SdpAttribute::kMidAttribute,
647 0 : sourceAttrs.GetMid()));
648 : }
649 :
650 0 : return NS_OK;
651 : }
652 :
653 : bool
654 0 : SdpHelper::HasRtcp(SdpMediaSection::Protocol proto) const
655 : {
656 0 : switch (proto) {
657 : case SdpMediaSection::kRtpAvpf:
658 : case SdpMediaSection::kDccpRtpAvpf:
659 : case SdpMediaSection::kDccpRtpSavpf:
660 : case SdpMediaSection::kRtpSavpf:
661 : case SdpMediaSection::kUdpTlsRtpSavpf:
662 : case SdpMediaSection::kTcpTlsRtpSavpf:
663 : case SdpMediaSection::kDccpTlsRtpSavpf:
664 0 : return true;
665 : case SdpMediaSection::kRtpAvp:
666 : case SdpMediaSection::kUdp:
667 : case SdpMediaSection::kVat:
668 : case SdpMediaSection::kRtp:
669 : case SdpMediaSection::kUdptl:
670 : case SdpMediaSection::kTcp:
671 : case SdpMediaSection::kTcpRtpAvp:
672 : case SdpMediaSection::kRtpSavp:
673 : case SdpMediaSection::kTcpBfcp:
674 : case SdpMediaSection::kTcpTlsBfcp:
675 : case SdpMediaSection::kTcpTls:
676 : case SdpMediaSection::kFluteUdp:
677 : case SdpMediaSection::kTcpMsrp:
678 : case SdpMediaSection::kTcpTlsMsrp:
679 : case SdpMediaSection::kDccp:
680 : case SdpMediaSection::kDccpRtpAvp:
681 : case SdpMediaSection::kDccpRtpSavp:
682 : case SdpMediaSection::kUdpTlsRtpSavp:
683 : case SdpMediaSection::kTcpTlsRtpSavp:
684 : case SdpMediaSection::kDccpTlsRtpSavp:
685 : case SdpMediaSection::kUdpMbmsFecRtpAvp:
686 : case SdpMediaSection::kUdpMbmsFecRtpSavp:
687 : case SdpMediaSection::kUdpMbmsRepair:
688 : case SdpMediaSection::kFecUdp:
689 : case SdpMediaSection::kUdpFec:
690 : case SdpMediaSection::kTcpMrcpv2:
691 : case SdpMediaSection::kTcpTlsMrcpv2:
692 : case SdpMediaSection::kPstn:
693 : case SdpMediaSection::kUdpTlsUdptl:
694 : case SdpMediaSection::kSctp:
695 : case SdpMediaSection::kDtlsSctp:
696 : case SdpMediaSection::kUdpDtlsSctp:
697 : case SdpMediaSection::kTcpDtlsSctp:
698 0 : return false;
699 : }
700 0 : MOZ_CRASH("Unknown protocol, probably corruption.");
701 : }
702 :
703 : SdpMediaSection::Protocol
704 0 : SdpHelper::GetProtocolForMediaType(SdpMediaSection::MediaType type)
705 : {
706 0 : if (type == SdpMediaSection::kApplication) {
707 0 : return SdpMediaSection::kDtlsSctp;
708 : // TODO switch to offer the new SCTP SDP (Bug 1335206)
709 : //return SdpMediaSection::kUdpDtlsSctp;
710 : }
711 :
712 0 : return SdpMediaSection::kUdpTlsRtpSavpf;
713 : }
714 :
715 : void
716 0 : SdpHelper::appendSdpParseErrors(
717 : const std::vector<std::pair<size_t, std::string> >& aErrors,
718 : std::string* aErrorString)
719 : {
720 0 : std::ostringstream os;
721 0 : for (auto i = aErrors.begin(); i != aErrors.end(); ++i) {
722 0 : os << "SDP Parse Error on line " << i->first << ": " + i->second
723 0 : << std::endl;
724 : }
725 0 : *aErrorString += os.str();
726 0 : }
727 :
728 : /* static */ bool
729 0 : SdpHelper::GetPtAsInt(const std::string& ptString, uint16_t* ptOutparam)
730 : {
731 : char* end;
732 0 : unsigned long pt = strtoul(ptString.c_str(), &end, 10);
733 0 : size_t length = static_cast<size_t>(end - ptString.c_str());
734 0 : if ((pt > UINT16_MAX) || (length != ptString.size())) {
735 0 : return false;
736 : }
737 0 : *ptOutparam = pt;
738 0 : return true;
739 : }
740 :
741 : void
742 0 : SdpHelper::AddCommonExtmaps(
743 : const SdpMediaSection& remoteMsection,
744 : const std::vector<SdpExtmapAttributeList::Extmap>& localExtensions,
745 : SdpMediaSection* localMsection)
746 : {
747 0 : if (!remoteMsection.GetAttributeList().HasAttribute(
748 : SdpAttribute::kExtmapAttribute)) {
749 0 : return;
750 : }
751 :
752 0 : UniquePtr<SdpExtmapAttributeList> localExtmap(new SdpExtmapAttributeList);
753 0 : auto& theirExtmap = remoteMsection.GetAttributeList().GetExtmap().mExtmaps;
754 0 : for (const auto& theirExt : theirExtmap) {
755 0 : for (const auto& ourExt : localExtensions) {
756 0 : if (theirExt.extensionname != ourExt.extensionname) {
757 0 : continue;
758 : }
759 :
760 0 : auto negotiatedExt = theirExt;
761 :
762 0 : negotiatedExt.direction =
763 0 : reverse(negotiatedExt.direction) & ourExt.direction;
764 0 : if (negotiatedExt.direction ==
765 : SdpDirectionAttribute::Direction::kInactive) {
766 0 : continue;
767 : }
768 :
769 : // RFC 5285 says that ids >= 4096 can be used by the offerer to
770 : // force the answerer to pick, otherwise the value in the offer is
771 : // used.
772 0 : if (negotiatedExt.entry >= 4096) {
773 0 : negotiatedExt.entry = ourExt.entry;
774 : }
775 :
776 0 : localExtmap->mExtmaps.push_back(negotiatedExt);
777 : }
778 : }
779 :
780 0 : if (!localExtmap->mExtmaps.empty()) {
781 0 : localMsection->GetAttributeList().SetAttribute(localExtmap.release());
782 : }
783 : }
784 :
785 : SdpHelper::MsectionBundleType
786 0 : SdpHelper::GetMsectionBundleType(const Sdp& sdp,
787 : uint16_t level,
788 : BundledMids& bundledMids,
789 : std::string* masterMid) const
790 : {
791 0 : const SdpMediaSection& msection = sdp.GetMediaSection(level);
792 0 : if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
793 0 : std::string mid(msection.GetAttributeList().GetMid());
794 0 : if (bundledMids.count(mid)) {
795 0 : const SdpMediaSection* masterBundleMsection(bundledMids[mid]);
796 0 : if (msection.GetLevel() != masterBundleMsection->GetLevel()) {
797 0 : return kSlaveBundle;
798 : }
799 :
800 : // allow the caller not to care about the masterMid
801 0 : if (masterMid) {
802 0 : *masterMid = mid;
803 : }
804 0 : return kMasterBundle;
805 : }
806 : }
807 0 : return kNoBundle;
808 : }
809 :
810 : } // namespace mozilla
811 :
812 :
|