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/jsep/JsepSessionImpl.h"
6 :
7 : #include <string>
8 : #include <set>
9 : #include <bitset>
10 : #include <stdlib.h>
11 :
12 : #include "nspr.h"
13 : #include "nss.h"
14 : #include "pk11pub.h"
15 : #include "nsDebug.h"
16 : #include "logging.h"
17 :
18 : #include "mozilla/Move.h"
19 : #include "mozilla/UniquePtr.h"
20 :
21 : #include "webrtc/config.h"
22 :
23 : #include "signaling/src/jsep/JsepTrack.h"
24 : #include "signaling/src/jsep/JsepTrack.h"
25 : #include "signaling/src/jsep/JsepTransport.h"
26 : #include "signaling/src/sdp/Sdp.h"
27 : #include "signaling/src/sdp/SipccSdp.h"
28 : #include "signaling/src/sdp/SipccSdpParser.h"
29 : #include "mozilla/net/DataChannelProtocol.h"
30 :
31 : namespace mozilla {
32 :
33 0 : MOZ_MTLOG_MODULE("jsep")
34 :
35 : #define JSEP_SET_ERROR(error) \
36 : do { \
37 : std::ostringstream os; \
38 : os << error; \
39 : mLastError = os.str(); \
40 : MOZ_MTLOG(ML_ERROR, mLastError); \
41 : } while (0);
42 :
43 0 : static std::bitset<128> GetForbiddenSdpPayloadTypes() {
44 0 : std::bitset<128> forbidden(0);
45 0 : forbidden[1] = true;
46 0 : forbidden[2] = true;
47 0 : forbidden[19] = true;
48 0 : for (uint16_t i = 64; i < 96; ++i) {
49 0 : forbidden[i] = true;
50 : }
51 0 : return forbidden;
52 : }
53 :
54 : nsresult
55 0 : JsepSessionImpl::Init()
56 : {
57 0 : mLastError.clear();
58 :
59 0 : MOZ_ASSERT(!mSessionId, "Init called more than once");
60 :
61 0 : nsresult rv = SetupIds();
62 0 : NS_ENSURE_SUCCESS(rv, rv);
63 :
64 0 : SetupDefaultCodecs();
65 0 : SetupDefaultRtpExtensions();
66 :
67 0 : return NS_OK;
68 : }
69 :
70 : // Helper function to find the track for a given m= section.
71 : template <class T>
72 : typename std::vector<T>::iterator
73 0 : FindTrackByLevel(std::vector<T>& tracks, size_t level)
74 : {
75 0 : for (auto t = tracks.begin(); t != tracks.end(); ++t) {
76 0 : if (t->mAssignedMLine.isSome() &&
77 0 : (*t->mAssignedMLine == level)) {
78 0 : return t;
79 : }
80 : }
81 :
82 0 : return tracks.end();
83 : }
84 :
85 : template <class T>
86 : typename std::vector<T>::iterator
87 0 : FindTrackByIds(std::vector<T>& tracks,
88 : const std::string& streamId,
89 : const std::string& trackId)
90 : {
91 0 : for (auto t = tracks.begin(); t != tracks.end(); ++t) {
92 0 : if (t->mTrack->GetStreamId() == streamId &&
93 0 : (t->mTrack->GetTrackId() == trackId)) {
94 0 : return t;
95 : }
96 : }
97 :
98 0 : return tracks.end();
99 : }
100 :
101 : template <class T>
102 : typename std::vector<T>::iterator
103 0 : FindUnassignedTrackByType(std::vector<T>& tracks,
104 : SdpMediaSection::MediaType type)
105 : {
106 0 : for (auto t = tracks.begin(); t != tracks.end(); ++t) {
107 0 : if (!t->mAssignedMLine.isSome() &&
108 0 : (t->mTrack->GetMediaType() == type)) {
109 0 : return t;
110 : }
111 : }
112 :
113 0 : return tracks.end();
114 : }
115 :
116 : nsresult
117 0 : JsepSessionImpl::AddTrack(const RefPtr<JsepTrack>& track)
118 : {
119 0 : mLastError.clear();
120 0 : MOZ_ASSERT(track->GetDirection() == sdp::kSend);
121 0 : MOZ_MTLOG(ML_DEBUG, "Adding track.");
122 0 : if (track->GetMediaType() != SdpMediaSection::kApplication) {
123 0 : track->SetCNAME(mCNAME);
124 : // Establish minimum number of required SSRCs
125 : // Note that AddTrack is only for send direction
126 0 : size_t minimumSsrcCount = 0;
127 0 : std::vector<JsepTrack::JsConstraints> constraints;
128 0 : track->GetJsConstraints(&constraints);
129 0 : for (auto constraint : constraints) {
130 0 : if (constraint.rid != "") {
131 0 : minimumSsrcCount++;
132 : }
133 : }
134 : // We need at least 1 SSRC
135 0 : minimumSsrcCount = std::max<size_t>(1, minimumSsrcCount);
136 0 : size_t currSsrcCount = track->GetSsrcs().size();
137 0 : if (currSsrcCount < minimumSsrcCount ) {
138 0 : MOZ_MTLOG(ML_DEBUG,
139 : "Adding " << (minimumSsrcCount - currSsrcCount) << " SSRCs.");
140 : }
141 0 : while (track->GetSsrcs().size() < minimumSsrcCount) {
142 0 : uint32_t ssrc=0;
143 0 : nsresult rv = CreateSsrc(&ssrc);
144 0 : NS_ENSURE_SUCCESS(rv, rv);
145 : // Don't add duplicate ssrcs
146 0 : std::vector<uint32_t> ssrcs = track->GetSsrcs();
147 0 : if (std::find(ssrcs.begin(), ssrcs.end(), ssrc) == ssrcs.end()) {
148 0 : track->AddSsrc(ssrc);
149 : }
150 : }
151 : }
152 :
153 0 : track->PopulateCodecs(mSupportedCodecs.values);
154 :
155 0 : JsepSendingTrack strack;
156 0 : strack.mTrack = track;
157 :
158 0 : mLocalTracks.push_back(strack);
159 :
160 0 : return NS_OK;
161 : }
162 :
163 : nsresult
164 0 : JsepSessionImpl::RemoveTrack(const std::string& streamId,
165 : const std::string& trackId)
166 : {
167 0 : if (mState != kJsepStateStable) {
168 0 : JSEP_SET_ERROR("Removing tracks outside of stable is unsupported.");
169 0 : return NS_ERROR_UNEXPECTED;
170 : }
171 :
172 0 : auto track = FindTrackByIds(mLocalTracks, streamId, trackId);
173 :
174 0 : if (track == mLocalTracks.end()) {
175 0 : return NS_ERROR_INVALID_ARG;
176 : }
177 :
178 0 : mLocalTracks.erase(track);
179 0 : return NS_OK;
180 : }
181 :
182 : nsresult
183 0 : JsepSessionImpl::SetIceCredentials(const std::string& ufrag,
184 : const std::string& pwd)
185 : {
186 0 : mLastError.clear();
187 0 : mIceUfrag = ufrag;
188 0 : mIcePwd = pwd;
189 :
190 0 : return NS_OK;
191 : }
192 :
193 : nsresult
194 0 : JsepSessionImpl::SetBundlePolicy(JsepBundlePolicy policy)
195 : {
196 0 : mLastError.clear();
197 0 : if (mCurrentLocalDescription) {
198 0 : JSEP_SET_ERROR("Changing the bundle policy is only supported before the "
199 : "first SetLocalDescription.");
200 0 : return NS_ERROR_UNEXPECTED;
201 : }
202 :
203 0 : mBundlePolicy = policy;
204 0 : return NS_OK;
205 : }
206 :
207 : nsresult
208 0 : JsepSessionImpl::AddDtlsFingerprint(const std::string& algorithm,
209 : const std::vector<uint8_t>& value)
210 : {
211 0 : mLastError.clear();
212 0 : JsepDtlsFingerprint fp;
213 :
214 0 : fp.mAlgorithm = algorithm;
215 0 : fp.mValue = value;
216 :
217 0 : mDtlsFingerprints.push_back(fp);
218 :
219 0 : return NS_OK;
220 : }
221 :
222 : nsresult
223 0 : JsepSessionImpl::AddRtpExtension(std::vector<SdpExtmapAttributeList::Extmap>& extensions,
224 : const std::string& extensionName,
225 : SdpDirectionAttribute::Direction direction)
226 : {
227 0 : mLastError.clear();
228 :
229 0 : if (extensions.size() + 1 > UINT16_MAX) {
230 0 : JSEP_SET_ERROR("Too many rtp extensions have been added");
231 0 : return NS_ERROR_FAILURE;
232 : }
233 :
234 : SdpExtmapAttributeList::Extmap extmap =
235 0 : { static_cast<uint16_t>(extensions.size() + 1),
236 : direction,
237 0 : direction != SdpDirectionAttribute::kSendrecv, // do we want to specify direction?
238 : extensionName,
239 0 : "" };
240 :
241 0 : extensions.push_back(extmap);
242 0 : return NS_OK;
243 : }
244 :
245 : nsresult
246 0 : JsepSessionImpl::AddAudioRtpExtension(const std::string& extensionName,
247 : SdpDirectionAttribute::Direction direction)
248 : {
249 0 : return AddRtpExtension(mAudioRtpExtensions, extensionName, direction);
250 : }
251 :
252 : nsresult
253 0 : JsepSessionImpl::AddVideoRtpExtension(const std::string& extensionName,
254 : SdpDirectionAttribute::Direction direction)
255 : {
256 0 : return AddRtpExtension(mVideoRtpExtensions, extensionName, direction);
257 : }
258 :
259 : template<class T>
260 : std::vector<RefPtr<JsepTrack>>
261 0 : GetTracks(const std::vector<T>& wrappedTracks)
262 : {
263 0 : std::vector<RefPtr<JsepTrack>> result;
264 0 : for (auto i = wrappedTracks.begin(); i != wrappedTracks.end(); ++i) {
265 0 : result.push_back(i->mTrack);
266 : }
267 0 : return result;
268 : }
269 :
270 : nsresult
271 0 : JsepSessionImpl::ReplaceTrack(const std::string& oldStreamId,
272 : const std::string& oldTrackId,
273 : const std::string& newStreamId,
274 : const std::string& newTrackId)
275 : {
276 0 : auto it = FindTrackByIds(mLocalTracks, oldStreamId, oldTrackId);
277 :
278 0 : if (it == mLocalTracks.end()) {
279 0 : JSEP_SET_ERROR("Track " << oldStreamId << "/" << oldTrackId
280 : << " was never added.");
281 0 : return NS_ERROR_INVALID_ARG;
282 : }
283 :
284 0 : if (FindTrackByIds(mLocalTracks, newStreamId, newTrackId) !=
285 0 : mLocalTracks.end()) {
286 0 : JSEP_SET_ERROR("Track " << newStreamId << "/" << newTrackId
287 : << " was already added.");
288 0 : return NS_ERROR_INVALID_ARG;
289 : }
290 :
291 0 : it->mTrack->SetStreamId(newStreamId);
292 0 : it->mTrack->SetTrackId(newTrackId);
293 :
294 0 : return NS_OK;
295 : }
296 :
297 : nsresult
298 0 : JsepSessionImpl::SetParameters(const std::string& streamId,
299 : const std::string& trackId,
300 : const std::vector<JsepTrack::JsConstraints>& constraints)
301 : {
302 0 : auto it = FindTrackByIds(mLocalTracks, streamId, trackId);
303 :
304 0 : if (it == mLocalTracks.end()) {
305 0 : JSEP_SET_ERROR("Track " << streamId << "/" << trackId << " was never added.");
306 0 : return NS_ERROR_INVALID_ARG;
307 : }
308 :
309 : // Add RtpStreamId Extmap
310 : // SdpDirectionAttribute::Direction is a bitmask
311 0 : SdpDirectionAttribute::Direction addVideoExt = SdpDirectionAttribute::kInactive;
312 0 : SdpDirectionAttribute::Direction addAudioExt = SdpDirectionAttribute::kInactive;
313 0 : for (auto constraintEntry: constraints) {
314 0 : if (constraintEntry.rid != "") {
315 0 : switch (it->mTrack->GetMediaType()) {
316 : case SdpMediaSection::kVideo: {
317 0 : addVideoExt = static_cast<SdpDirectionAttribute::Direction>(addVideoExt
318 0 : | it->mTrack->GetDirection());
319 0 : break;
320 : }
321 : case SdpMediaSection::kAudio: {
322 0 : addAudioExt = static_cast<SdpDirectionAttribute::Direction>(addAudioExt
323 0 : | it->mTrack->GetDirection());
324 0 : break;
325 : }
326 : default: {
327 0 : MOZ_ASSERT(false);
328 : return NS_ERROR_INVALID_ARG;
329 : }
330 : }
331 : }
332 : }
333 0 : if (addVideoExt != SdpDirectionAttribute::kInactive) {
334 0 : AddVideoRtpExtension("urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id", addVideoExt);
335 : }
336 :
337 0 : it->mTrack->SetJsConstraints(constraints);
338 :
339 0 : auto track = it->mTrack;
340 0 : if (track->GetDirection() == sdp::kSend) {
341 : // Establish minimum number of required SSRCs
342 : // Note that AddTrack is only for send direction
343 0 : size_t minimumSsrcCount = 0;
344 0 : std::vector<JsepTrack::JsConstraints> constraints;
345 0 : track->GetJsConstraints(&constraints);
346 0 : for (auto constraint : constraints) {
347 0 : if (constraint.rid != "") {
348 0 : minimumSsrcCount++;
349 : }
350 : }
351 : // We need at least 1 SSRC
352 0 : minimumSsrcCount = std::max<size_t>(1, minimumSsrcCount);
353 0 : size_t currSsrcCount = track->GetSsrcs().size();
354 0 : if (currSsrcCount < minimumSsrcCount ) {
355 0 : MOZ_MTLOG(ML_DEBUG,
356 : "Adding " << (minimumSsrcCount - currSsrcCount) << " SSRCs.");
357 : }
358 0 : while (track->GetSsrcs().size() < minimumSsrcCount) {
359 0 : uint32_t ssrc=0;
360 0 : nsresult rv = CreateSsrc(&ssrc);
361 0 : NS_ENSURE_SUCCESS(rv, rv);
362 : // Don't add duplicate ssrcs
363 0 : std::vector<uint32_t> ssrcs = track->GetSsrcs();
364 0 : if (std::find(ssrcs.begin(), ssrcs.end(), ssrc) == ssrcs.end()) {
365 0 : track->AddSsrc(ssrc);
366 : }
367 : }
368 : }
369 0 : return NS_OK;
370 : }
371 :
372 : nsresult
373 0 : JsepSessionImpl::GetParameters(const std::string& streamId,
374 : const std::string& trackId,
375 : std::vector<JsepTrack::JsConstraints>* outConstraints)
376 : {
377 0 : auto it = FindTrackByIds(mLocalTracks, streamId, trackId);
378 :
379 0 : if (it == mLocalTracks.end()) {
380 0 : JSEP_SET_ERROR("Track " << streamId << "/" << trackId << " was never added.");
381 0 : return NS_ERROR_INVALID_ARG;
382 : }
383 :
384 0 : it->mTrack->GetJsConstraints(outConstraints);
385 0 : return NS_OK;
386 : }
387 :
388 : std::vector<RefPtr<JsepTrack>>
389 0 : JsepSessionImpl::GetLocalTracks() const
390 : {
391 0 : return GetTracks(mLocalTracks);
392 : }
393 :
394 : std::vector<RefPtr<JsepTrack>>
395 0 : JsepSessionImpl::GetRemoteTracks() const
396 : {
397 0 : return GetTracks(mRemoteTracks);
398 : }
399 :
400 : std::vector<RefPtr<JsepTrack>>
401 0 : JsepSessionImpl::GetRemoteTracksAdded() const
402 : {
403 0 : return GetTracks(mRemoteTracksAdded);
404 : }
405 :
406 : std::vector<RefPtr<JsepTrack>>
407 0 : JsepSessionImpl::GetRemoteTracksRemoved() const
408 : {
409 0 : return GetTracks(mRemoteTracksRemoved);
410 : }
411 :
412 : nsresult
413 0 : JsepSessionImpl::SetupOfferMSections(const JsepOfferOptions& options, Sdp* sdp)
414 : {
415 : // First audio, then video, then datachannel, for interop
416 : // TODO(bug 1121756): We need to group these by stream-id, _then_ by media
417 : // type, according to the spec. However, this is not going to interop with
418 : // older versions of Firefox if a video-only stream is added before an
419 : // audio-only stream.
420 : // We should probably wait until 38 is ESR before trying to do this.
421 0 : nsresult rv = SetupOfferMSectionsByType(
422 0 : SdpMediaSection::kAudio, options.mOfferToReceiveAudio, sdp);
423 :
424 0 : NS_ENSURE_SUCCESS(rv, rv);
425 :
426 0 : rv = SetupOfferMSectionsByType(
427 0 : SdpMediaSection::kVideo, options.mOfferToReceiveVideo, sdp);
428 :
429 0 : NS_ENSURE_SUCCESS(rv, rv);
430 :
431 0 : if (!(options.mDontOfferDataChannel.isSome() &&
432 0 : *options.mDontOfferDataChannel)) {
433 : rv = SetupOfferMSectionsByType(
434 0 : SdpMediaSection::kApplication, Maybe<size_t>(), sdp);
435 :
436 0 : NS_ENSURE_SUCCESS(rv, rv);
437 : }
438 :
439 0 : if (!sdp->GetMediaSectionCount()) {
440 0 : JSEP_SET_ERROR("Cannot create an offer with no local tracks, "
441 : "no offerToReceiveAudio/Video, and no DataChannel.");
442 0 : return NS_ERROR_INVALID_ARG;
443 : }
444 :
445 0 : return NS_OK;
446 : }
447 :
448 : nsresult
449 0 : JsepSessionImpl::SetupOfferMSectionsByType(SdpMediaSection::MediaType mediatype,
450 : const Maybe<size_t>& offerToReceiveMaybe,
451 : Sdp* sdp)
452 : {
453 : // Convert the Maybe into a size_t*, since that is more readable, especially
454 : // when using it as an in/out param.
455 : size_t offerToReceiveCount;
456 0 : size_t* offerToReceiveCountPtr = nullptr;
457 :
458 0 : if (offerToReceiveMaybe) {
459 0 : offerToReceiveCount = *offerToReceiveMaybe;
460 0 : offerToReceiveCountPtr = &offerToReceiveCount;
461 : }
462 :
463 : // Make sure every local track has an m-section
464 0 : nsresult rv = BindLocalTracks(mediatype, sdp);
465 0 : NS_ENSURE_SUCCESS(rv, rv);
466 :
467 : // Make sure that m-sections that previously had a remote track have the
468 : // recv bit set. Only matters for renegotiation.
469 0 : rv = BindRemoteTracks(mediatype, sdp, offerToReceiveCountPtr);
470 0 : NS_ENSURE_SUCCESS(rv, rv);
471 :
472 : // If we need more recv sections, start setting the recv bit on other
473 : // msections. If not, disable msections that have no tracks.
474 : rv = SetRecvAsNeededOrDisable(mediatype,
475 : sdp,
476 0 : offerToReceiveCountPtr);
477 0 : NS_ENSURE_SUCCESS(rv, rv);
478 :
479 : // If we still don't have enough recv m-sections, add some.
480 0 : if (offerToReceiveCountPtr && *offerToReceiveCountPtr) {
481 0 : rv = AddRecvonlyMsections(mediatype, *offerToReceiveCountPtr, sdp);
482 0 : NS_ENSURE_SUCCESS(rv, rv);
483 : }
484 :
485 0 : return NS_OK;
486 : }
487 :
488 : nsresult
489 0 : JsepSessionImpl::BindLocalTracks(SdpMediaSection::MediaType mediatype, Sdp* sdp)
490 : {
491 0 : for (JsepSendingTrack& track : mLocalTracks) {
492 0 : if (mediatype != track.mTrack->GetMediaType()) {
493 0 : continue;
494 : }
495 :
496 : SdpMediaSection* msection;
497 0 : if (track.mAssignedMLine.isSome()) {
498 0 : msection = &sdp->GetMediaSection(*track.mAssignedMLine);
499 : } else {
500 0 : nsresult rv = GetFreeMsectionForSend(track.mTrack->GetMediaType(),
501 : sdp,
502 0 : &msection);
503 0 : NS_ENSURE_SUCCESS(rv, rv);
504 0 : track.mAssignedMLine = Some(msection->GetLevel());
505 : }
506 :
507 0 : track.mTrack->AddToOffer(msection);
508 : }
509 0 : return NS_OK;
510 : }
511 :
512 : nsresult
513 0 : JsepSessionImpl::BindRemoteTracks(SdpMediaSection::MediaType mediatype,
514 : Sdp* sdp,
515 : size_t* offerToReceive)
516 : {
517 0 : for (JsepReceivingTrack& track : mRemoteTracks) {
518 0 : if (mediatype != track.mTrack->GetMediaType()) {
519 0 : continue;
520 : }
521 :
522 0 : if (!track.mAssignedMLine.isSome()) {
523 0 : MOZ_ASSERT(false);
524 : continue;
525 : }
526 :
527 0 : auto& msection = sdp->GetMediaSection(*track.mAssignedMLine);
528 :
529 0 : if (mSdpHelper.MsectionIsDisabled(msection)) {
530 : // TODO(bug 1095226) Content probably disabled this? Should we allow
531 : // content to do this?
532 0 : continue;
533 : }
534 :
535 0 : track.mTrack->AddToOffer(&msection);
536 :
537 0 : if (offerToReceive && *offerToReceive) {
538 0 : --(*offerToReceive);
539 : }
540 : }
541 :
542 0 : return NS_OK;
543 : }
544 :
545 : nsresult
546 0 : JsepSessionImpl::SetRecvAsNeededOrDisable(SdpMediaSection::MediaType mediatype,
547 : Sdp* sdp,
548 : size_t* offerToRecv)
549 : {
550 0 : for (size_t i = 0; i < sdp->GetMediaSectionCount(); ++i) {
551 0 : auto& msection = sdp->GetMediaSection(i);
552 :
553 0 : if (mSdpHelper.MsectionIsDisabled(msection) ||
554 0 : msection.GetMediaType() != mediatype ||
555 0 : msection.IsReceiving()) {
556 0 : continue;
557 : }
558 :
559 0 : if (offerToRecv) {
560 0 : if (*offerToRecv) {
561 0 : SetupOfferToReceiveMsection(&msection);
562 0 : --(*offerToRecv);
563 0 : continue;
564 : }
565 0 : } else if (msection.IsSending()) {
566 0 : SetupOfferToReceiveMsection(&msection);
567 0 : continue;
568 : }
569 :
570 0 : if (!msection.IsSending()) {
571 : // Unused m-section, and no reason to offer to recv on it
572 0 : mSdpHelper.DisableMsection(sdp, &msection);
573 : }
574 : }
575 :
576 0 : return NS_OK;
577 : }
578 :
579 : void
580 0 : JsepSessionImpl::SetupOfferToReceiveMsection(SdpMediaSection* offer)
581 : {
582 : // Create a dummy recv track, and have it add codecs, set direction, etc.
583 0 : RefPtr<JsepTrack> dummy = new JsepTrack(offer->GetMediaType(),
584 : "",
585 : "",
586 0 : sdp::kRecv);
587 0 : dummy->PopulateCodecs(mSupportedCodecs.values);
588 0 : dummy->AddToOffer(offer);
589 0 : }
590 :
591 : nsresult
592 0 : JsepSessionImpl::AddRecvonlyMsections(SdpMediaSection::MediaType mediatype,
593 : size_t count,
594 : Sdp* sdp)
595 : {
596 0 : while (count--) {
597 0 : nsresult rv = CreateOfferMSection(
598 : mediatype,
599 : mSdpHelper.GetProtocolForMediaType(mediatype),
600 : SdpDirectionAttribute::kRecvonly,
601 0 : sdp);
602 :
603 0 : NS_ENSURE_SUCCESS(rv, rv);
604 : SetupOfferToReceiveMsection(
605 0 : &sdp->GetMediaSection(sdp->GetMediaSectionCount() - 1));
606 : }
607 0 : return NS_OK;
608 : }
609 :
610 : // This function creates a skeleton SDP based on the old descriptions
611 : // (ie; all m-sections are inactive).
612 : nsresult
613 0 : JsepSessionImpl::AddReofferMsections(const Sdp& oldLocalSdp,
614 : const Sdp& oldAnswer,
615 : Sdp* newSdp)
616 : {
617 : nsresult rv;
618 :
619 0 : for (size_t i = 0; i < oldLocalSdp.GetMediaSectionCount(); ++i) {
620 : // We do not set the direction in this function (or disable when previously
621 : // disabled), that happens in |SetupOfferMSectionsByType|
622 0 : rv = CreateOfferMSection(oldLocalSdp.GetMediaSection(i).GetMediaType(),
623 0 : oldLocalSdp.GetMediaSection(i).GetProtocol(),
624 : SdpDirectionAttribute::kInactive,
625 0 : newSdp);
626 0 : NS_ENSURE_SUCCESS(rv, rv);
627 :
628 0 : rv = mSdpHelper.CopyStickyParams(oldAnswer.GetMediaSection(i),
629 0 : &newSdp->GetMediaSection(i));
630 0 : NS_ENSURE_SUCCESS(rv, rv);
631 : }
632 :
633 0 : return NS_OK;
634 : }
635 :
636 : void
637 0 : JsepSessionImpl::SetupBundle(Sdp* sdp) const
638 : {
639 0 : std::vector<std::string> mids;
640 0 : std::set<SdpMediaSection::MediaType> observedTypes;
641 :
642 : // This has the effect of changing the bundle level if the first m-section
643 : // goes from disabled to enabled. This is kinda inefficient.
644 :
645 0 : for (size_t i = 0; i < sdp->GetMediaSectionCount(); ++i) {
646 0 : auto& attrs = sdp->GetMediaSection(i).GetAttributeList();
647 0 : if (attrs.HasAttribute(SdpAttribute::kMidAttribute)) {
648 0 : bool useBundleOnly = false;
649 0 : switch (mBundlePolicy) {
650 : case kBundleMaxCompat:
651 : // We don't use bundle-only for max-compat
652 0 : break;
653 : case kBundleBalanced:
654 : // balanced means we use bundle-only on everything but the first
655 : // m-section of a given type
656 0 : if (observedTypes.count(sdp->GetMediaSection(i).GetMediaType())) {
657 0 : useBundleOnly = true;
658 : }
659 0 : observedTypes.insert(sdp->GetMediaSection(i).GetMediaType());
660 0 : break;
661 : case kBundleMaxBundle:
662 : // max-bundle means we use bundle-only on everything but the first
663 : // m-section
664 0 : useBundleOnly = !mids.empty();
665 0 : break;
666 : }
667 :
668 0 : if (useBundleOnly) {
669 : attrs.SetAttribute(
670 0 : new SdpFlagAttribute(SdpAttribute::kBundleOnlyAttribute));
671 : // Set port to 0 for sections with bundle-only attribute. (mjf)
672 0 : sdp->GetMediaSection(i).SetPort(0);
673 : }
674 :
675 0 : mids.push_back(attrs.GetMid());
676 : }
677 : }
678 :
679 0 : if (mids.size() >= 1) {
680 0 : UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList);
681 0 : groupAttr->PushEntry(SdpGroupAttributeList::kBundle, mids);
682 0 : sdp->GetAttributeList().SetAttribute(groupAttr.release());
683 : }
684 0 : }
685 :
686 : nsresult
687 0 : JsepSessionImpl::GetRemoteIds(const Sdp& sdp,
688 : const SdpMediaSection& msection,
689 : std::string* streamId,
690 : std::string* trackId)
691 : {
692 0 : nsresult rv = mSdpHelper.GetIdsFromMsid(sdp, msection, streamId, trackId);
693 0 : if (rv == NS_ERROR_NOT_AVAILABLE) {
694 0 : *streamId = mDefaultRemoteStreamId;
695 :
696 0 : if (!mDefaultRemoteTrackIdsByLevel.count(msection.GetLevel())) {
697 : // Generate random track ids.
698 0 : if (!mUuidGen->Generate(trackId)) {
699 0 : JSEP_SET_ERROR("Failed to generate UUID for JsepTrack");
700 0 : return NS_ERROR_FAILURE;
701 : }
702 :
703 0 : mDefaultRemoteTrackIdsByLevel[msection.GetLevel()] = *trackId;
704 : } else {
705 0 : *trackId = mDefaultRemoteTrackIdsByLevel[msection.GetLevel()];
706 : }
707 0 : return NS_OK;
708 : }
709 :
710 0 : if (NS_SUCCEEDED(rv)) {
711 : // If, for whatever reason, the other end renegotiates with an msid where
712 : // there wasn't one before, don't allow the old default to pop up again
713 : // later.
714 0 : mDefaultRemoteTrackIdsByLevel.erase(msection.GetLevel());
715 : }
716 :
717 0 : return rv;
718 : }
719 :
720 : nsresult
721 0 : JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
722 : std::string* offer)
723 : {
724 0 : mLastError.clear();
725 :
726 0 : if (mState != kJsepStateStable) {
727 0 : JSEP_SET_ERROR("Cannot create offer in state " << GetStateStr(mState));
728 0 : return NS_ERROR_UNEXPECTED;
729 : }
730 :
731 : // Undo track assignments from a previous call to CreateOffer
732 : // (ie; if the track has not been negotiated yet, it doesn't necessarily need
733 : // to stay in the same m-section that it was in)
734 0 : for (JsepSendingTrack& trackWrapper : mLocalTracks) {
735 0 : if (!trackWrapper.mTrack->GetNegotiatedDetails()) {
736 0 : trackWrapper.mAssignedMLine.reset();
737 : }
738 : }
739 :
740 0 : UniquePtr<Sdp> sdp;
741 :
742 : // Make the basic SDP that is common to offer/answer.
743 0 : nsresult rv = CreateGenericSDP(&sdp);
744 0 : NS_ENSURE_SUCCESS(rv, rv);
745 :
746 0 : if (mCurrentLocalDescription) {
747 0 : rv = AddReofferMsections(*mCurrentLocalDescription,
748 0 : *GetAnswer(),
749 0 : sdp.get());
750 0 : NS_ENSURE_SUCCESS(rv, rv);
751 : }
752 :
753 : // Ensure that we have all the m-sections we need, and disable extras
754 0 : rv = SetupOfferMSections(options, sdp.get());
755 0 : NS_ENSURE_SUCCESS(rv, rv);
756 :
757 0 : SetupBundle(sdp.get());
758 :
759 0 : if (mCurrentLocalDescription) {
760 0 : rv = CopyPreviousTransportParams(*GetAnswer(),
761 0 : *mCurrentLocalDescription,
762 0 : *sdp,
763 0 : sdp.get());
764 0 : NS_ENSURE_SUCCESS(rv,rv);
765 : }
766 :
767 0 : *offer = sdp->ToString();
768 0 : mGeneratedLocalDescription = Move(sdp);
769 0 : ++mSessionVersion;
770 :
771 0 : return NS_OK;
772 : }
773 :
774 : std::string
775 0 : JsepSessionImpl::GetLocalDescription(JsepDescriptionPendingOrCurrent type) const
776 : {
777 0 : std::ostringstream os;
778 0 : mozilla::Sdp* sdp = GetParsedLocalDescription(type);
779 0 : if (sdp) {
780 0 : sdp->Serialize(os);
781 : }
782 0 : return os.str();
783 : }
784 :
785 : std::string
786 0 : JsepSessionImpl::GetRemoteDescription(JsepDescriptionPendingOrCurrent type) const
787 : {
788 0 : std::ostringstream os;
789 0 : mozilla::Sdp* sdp = GetParsedRemoteDescription(type);
790 0 : if (sdp) {
791 0 : sdp->Serialize(os);
792 : }
793 0 : return os.str();
794 : }
795 :
796 : void
797 0 : JsepSessionImpl::AddExtmap(SdpMediaSection* msection) const
798 : {
799 0 : const auto* extensions = GetRtpExtensions(msection->GetMediaType());
800 :
801 0 : if (extensions && !extensions->empty()) {
802 0 : SdpExtmapAttributeList* extmap = new SdpExtmapAttributeList;
803 0 : extmap->mExtmaps = *extensions;
804 0 : msection->GetAttributeList().SetAttribute(extmap);
805 : }
806 0 : }
807 :
808 : void
809 0 : JsepSessionImpl::AddMid(const std::string& mid,
810 : SdpMediaSection* msection) const
811 : {
812 0 : msection->GetAttributeList().SetAttribute(new SdpStringAttribute(
813 0 : SdpAttribute::kMidAttribute, mid));
814 0 : }
815 :
816 : const std::vector<SdpExtmapAttributeList::Extmap>*
817 0 : JsepSessionImpl::GetRtpExtensions(SdpMediaSection::MediaType type) const
818 : {
819 0 : switch (type) {
820 : case SdpMediaSection::kAudio:
821 0 : return &mAudioRtpExtensions;
822 : case SdpMediaSection::kVideo:
823 0 : return &mVideoRtpExtensions;
824 : default:
825 0 : return nullptr;
826 : }
827 : }
828 :
829 : void
830 0 : JsepSessionImpl::AddCommonExtmaps(const SdpMediaSection& remoteMsection,
831 : SdpMediaSection* msection)
832 : {
833 0 : auto* ourExtensions = GetRtpExtensions(remoteMsection.GetMediaType());
834 :
835 0 : if (ourExtensions) {
836 0 : mSdpHelper.AddCommonExtmaps(remoteMsection, *ourExtensions, msection);
837 : }
838 0 : }
839 :
840 : nsresult
841 0 : JsepSessionImpl::CreateAnswer(const JsepAnswerOptions& options,
842 : std::string* answer)
843 : {
844 0 : mLastError.clear();
845 :
846 0 : if (mState != kJsepStateHaveRemoteOffer) {
847 0 : JSEP_SET_ERROR("Cannot create answer in state " << GetStateStr(mState));
848 0 : return NS_ERROR_UNEXPECTED;
849 : }
850 :
851 : // This is the heart of the negotiation code. Depressing that it's
852 : // so bad.
853 : //
854 : // Here's the current algorithm:
855 : // 1. Walk through all the m-lines on the other side.
856 : // 2. For each m-line, walk through all of our local tracks
857 : // in sequence and see if any are unassigned. If so, assign
858 : // them and mark it sendrecv, otherwise it's recvonly.
859 : // 3. Just replicate their media attributes.
860 : // 4. Profit.
861 0 : UniquePtr<Sdp> sdp;
862 :
863 : // Make the basic SDP that is common to offer/answer.
864 0 : nsresult rv = CreateGenericSDP(&sdp);
865 0 : NS_ENSURE_SUCCESS(rv, rv);
866 :
867 0 : const Sdp& offer = *mPendingRemoteDescription;
868 :
869 : // Copy the bundle groups into our answer
870 0 : UniquePtr<SdpGroupAttributeList> groupAttr(new SdpGroupAttributeList);
871 0 : mSdpHelper.GetBundleGroups(offer, &groupAttr->mGroups);
872 0 : sdp->GetAttributeList().SetAttribute(groupAttr.release());
873 :
874 : // Disable send for local tracks if the offer no longer allows it
875 : // (i.e., the m-section is recvonly, inactive or disabled)
876 0 : for (JsepSendingTrack& trackWrapper : mLocalTracks) {
877 0 : if (!trackWrapper.mAssignedMLine.isSome()) {
878 0 : continue;
879 : }
880 :
881 : // Get rid of all m-line assignments that have not been negotiated
882 0 : if (!trackWrapper.mTrack->GetNegotiatedDetails()) {
883 0 : trackWrapper.mAssignedMLine.reset();
884 0 : continue;
885 : }
886 :
887 0 : if (!offer.GetMediaSection(*trackWrapper.mAssignedMLine).IsReceiving()) {
888 0 : trackWrapper.mAssignedMLine.reset();
889 : }
890 : }
891 :
892 0 : size_t numMsections = offer.GetMediaSectionCount();
893 :
894 0 : for (size_t i = 0; i < numMsections; ++i) {
895 0 : const SdpMediaSection& remoteMsection = offer.GetMediaSection(i);
896 0 : rv = CreateAnswerMSection(options, i, remoteMsection, sdp.get());
897 0 : NS_ENSURE_SUCCESS(rv, rv);
898 : }
899 :
900 0 : if (mCurrentLocalDescription) {
901 : // per discussion with bwc, 3rd parm here should be offer, not *sdp. (mjf)
902 0 : rv = CopyPreviousTransportParams(*GetAnswer(),
903 0 : *mCurrentRemoteDescription,
904 : offer,
905 0 : sdp.get());
906 0 : NS_ENSURE_SUCCESS(rv,rv);
907 : }
908 :
909 0 : *answer = sdp->ToString();
910 0 : mGeneratedLocalDescription = Move(sdp);
911 0 : ++mSessionVersion;
912 :
913 0 : return NS_OK;
914 : }
915 :
916 : nsresult
917 0 : JsepSessionImpl::CreateOfferMSection(SdpMediaSection::MediaType mediatype,
918 : SdpMediaSection::Protocol proto,
919 : SdpDirectionAttribute::Direction dir,
920 : Sdp* sdp)
921 : {
922 : SdpMediaSection* msection =
923 0 : &sdp->AddMediaSection(mediatype, dir, 0, proto, sdp::kIPv4, "0.0.0.0");
924 :
925 0 : return EnableOfferMsection(msection);
926 : }
927 :
928 : nsresult
929 0 : JsepSessionImpl::GetFreeMsectionForSend(
930 : SdpMediaSection::MediaType type,
931 : Sdp* sdp,
932 : SdpMediaSection** msectionOut)
933 : {
934 0 : for (size_t i = 0; i < sdp->GetMediaSectionCount(); ++i) {
935 0 : SdpMediaSection& msection = sdp->GetMediaSection(i);
936 : // draft-ietf-rtcweb-jsep-08 says we should reclaim disabled m-sections
937 : // regardless of media type. This breaks some pretty fundamental rules of
938 : // SDP offer/answer, so we probably should not do it.
939 0 : if (msection.GetMediaType() != type) {
940 0 : continue;
941 : }
942 :
943 0 : if (FindTrackByLevel(mLocalTracks, i) != mLocalTracks.end()) {
944 : // Not free
945 0 : continue;
946 : }
947 :
948 0 : if (mSdpHelper.MsectionIsDisabled(msection)) {
949 : // Was disabled; revive
950 0 : nsresult rv = EnableOfferMsection(&msection);
951 0 : NS_ENSURE_SUCCESS(rv, rv);
952 : }
953 :
954 0 : *msectionOut = &msection;
955 0 : return NS_OK;
956 : }
957 :
958 : // Ok, no pre-existing m-section. Make a new one.
959 0 : nsresult rv = CreateOfferMSection(type,
960 : mSdpHelper.GetProtocolForMediaType(type),
961 : SdpDirectionAttribute::kInactive,
962 0 : sdp);
963 0 : NS_ENSURE_SUCCESS(rv, rv);
964 :
965 0 : *msectionOut = &sdp->GetMediaSection(sdp->GetMediaSectionCount() - 1);
966 0 : return NS_OK;
967 : }
968 :
969 : nsresult
970 0 : JsepSessionImpl::CreateAnswerMSection(const JsepAnswerOptions& options,
971 : size_t mlineIndex,
972 : const SdpMediaSection& remoteMsection,
973 : Sdp* sdp)
974 : {
975 : SdpMediaSection& msection =
976 0 : sdp->AddMediaSection(remoteMsection.GetMediaType(),
977 : SdpDirectionAttribute::kInactive,
978 : 9,
979 0 : remoteMsection.GetProtocol(),
980 : sdp::kIPv4,
981 0 : "0.0.0.0");
982 :
983 0 : nsresult rv = mSdpHelper.CopyStickyParams(remoteMsection, &msection);
984 0 : NS_ENSURE_SUCCESS(rv, rv);
985 :
986 0 : if (mSdpHelper.MsectionIsDisabled(remoteMsection)) {
987 0 : mSdpHelper.DisableMsection(sdp, &msection);
988 0 : return NS_OK;
989 : }
990 :
991 : SdpSetupAttribute::Role role;
992 0 : rv = DetermineAnswererSetupRole(remoteMsection, &role);
993 0 : NS_ENSURE_SUCCESS(rv, rv);
994 :
995 0 : rv = AddTransportAttributes(&msection, role);
996 0 : NS_ENSURE_SUCCESS(rv, rv);
997 :
998 0 : rv = SetRecvonlySsrc(&msection);
999 0 : NS_ENSURE_SUCCESS(rv, rv);
1000 :
1001 : // Only attempt to match up local tracks if the offerer has elected to
1002 : // receive traffic.
1003 0 : if (remoteMsection.IsReceiving()) {
1004 0 : rv = BindMatchingLocalTrackToAnswer(&msection);
1005 0 : NS_ENSURE_SUCCESS(rv, rv);
1006 : }
1007 :
1008 0 : if (remoteMsection.IsSending()) {
1009 0 : BindMatchingRemoteTrackToAnswer(&msection);
1010 : }
1011 :
1012 : // Add extmap attributes.
1013 0 : AddCommonExtmaps(remoteMsection, &msection);
1014 :
1015 0 : if (msection.GetFormats().empty()) {
1016 : // Could not negotiate anything. Disable m-section.
1017 0 : mSdpHelper.DisableMsection(sdp, &msection);
1018 : }
1019 :
1020 0 : return NS_OK;
1021 : }
1022 :
1023 : nsresult
1024 0 : JsepSessionImpl::SetRecvonlySsrc(SdpMediaSection* msection)
1025 : {
1026 0 : if (msection->GetMediaType() == SdpMediaSection::kApplication) {
1027 0 : return NS_OK;
1028 : }
1029 :
1030 : // If previous m-sections are disabled, we do not call this function for them
1031 0 : while (mRecvonlySsrcs.size() <= msection->GetLevel()) {
1032 : uint32_t ssrc;
1033 0 : nsresult rv = CreateSsrc(&ssrc);
1034 0 : NS_ENSURE_SUCCESS(rv, rv);
1035 0 : mRecvonlySsrcs.push_back(ssrc);
1036 : }
1037 :
1038 0 : std::vector<uint32_t> ssrcs;
1039 0 : ssrcs.push_back(mRecvonlySsrcs[msection->GetLevel()]);
1040 0 : msection->SetSsrcs(ssrcs, mCNAME);
1041 0 : return NS_OK;
1042 : }
1043 :
1044 : nsresult
1045 0 : JsepSessionImpl::BindMatchingLocalTrackToAnswer(SdpMediaSection* msection)
1046 : {
1047 0 : auto track = FindTrackByLevel(mLocalTracks, msection->GetLevel());
1048 :
1049 0 : if (track == mLocalTracks.end()) {
1050 0 : track = FindUnassignedTrackByType(mLocalTracks, msection->GetMediaType());
1051 : }
1052 :
1053 0 : if (track == mLocalTracks.end() &&
1054 0 : msection->GetMediaType() == SdpMediaSection::kApplication) {
1055 : // If we are offered datachannel, we need to play along even if no track
1056 : // for it has been added yet.
1057 0 : std::string streamId;
1058 0 : std::string trackId;
1059 :
1060 0 : if (!mUuidGen->Generate(&streamId) || !mUuidGen->Generate(&trackId)) {
1061 0 : JSEP_SET_ERROR("Failed to generate UUIDs for datachannel track");
1062 0 : return NS_ERROR_FAILURE;
1063 : }
1064 :
1065 0 : AddTrack(RefPtr<JsepTrack>(
1066 0 : new JsepTrack(SdpMediaSection::kApplication, streamId, trackId)));
1067 0 : track = FindUnassignedTrackByType(mLocalTracks, msection->GetMediaType());
1068 0 : MOZ_ASSERT(track != mLocalTracks.end());
1069 : }
1070 :
1071 0 : if (track != mLocalTracks.end()) {
1072 0 : track->mAssignedMLine = Some(msection->GetLevel());
1073 0 : track->mTrack->AddToAnswer(
1074 0 : mPendingRemoteDescription->GetMediaSection(msection->GetLevel()),
1075 0 : msection);
1076 : }
1077 :
1078 0 : return NS_OK;
1079 : }
1080 :
1081 : nsresult
1082 0 : JsepSessionImpl::BindMatchingRemoteTrackToAnswer(SdpMediaSection* msection)
1083 : {
1084 0 : auto it = FindTrackByLevel(mRemoteTracks, msection->GetLevel());
1085 0 : if (it == mRemoteTracks.end()) {
1086 0 : MOZ_ASSERT(false);
1087 : JSEP_SET_ERROR("Failed to find remote track for local answer m-section");
1088 : return NS_ERROR_FAILURE;
1089 : }
1090 :
1091 0 : it->mTrack->AddToAnswer(
1092 0 : mPendingRemoteDescription->GetMediaSection(msection->GetLevel()),
1093 0 : msection);
1094 0 : return NS_OK;
1095 : }
1096 :
1097 : nsresult
1098 0 : JsepSessionImpl::DetermineAnswererSetupRole(
1099 : const SdpMediaSection& remoteMsection,
1100 : SdpSetupAttribute::Role* rolep)
1101 : {
1102 : // Determine the role.
1103 : // RFC 5763 says:
1104 : //
1105 : // The endpoint MUST use the setup attribute defined in [RFC4145].
1106 : // The endpoint that is the offerer MUST use the setup attribute
1107 : // value of setup:actpass and be prepared to receive a client_hello
1108 : // before it receives the answer. The answerer MUST use either a
1109 : // setup attribute value of setup:active or setup:passive. Note that
1110 : // if the answerer uses setup:passive, then the DTLS handshake will
1111 : // not begin until the answerer is received, which adds additional
1112 : // latency. setup:active allows the answer and the DTLS handshake to
1113 : // occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
1114 : // party is active MUST initiate a DTLS handshake by sending a
1115 : // ClientHello over each flow (host/port quartet).
1116 : //
1117 : // We default to assuming that the offerer is passive and we are active.
1118 0 : SdpSetupAttribute::Role role = SdpSetupAttribute::kActive;
1119 :
1120 0 : if (remoteMsection.GetAttributeList().HasAttribute(
1121 : SdpAttribute::kSetupAttribute)) {
1122 0 : switch (remoteMsection.GetAttributeList().GetSetup().mRole) {
1123 : case SdpSetupAttribute::kActive:
1124 0 : role = SdpSetupAttribute::kPassive;
1125 0 : break;
1126 : case SdpSetupAttribute::kPassive:
1127 : case SdpSetupAttribute::kActpass:
1128 0 : role = SdpSetupAttribute::kActive;
1129 0 : break;
1130 : case SdpSetupAttribute::kHoldconn:
1131 : // This should have been caught by ParseSdp
1132 0 : MOZ_ASSERT(false);
1133 : JSEP_SET_ERROR("The other side used an illegal setup attribute"
1134 : " (\"holdconn\").");
1135 : return NS_ERROR_INVALID_ARG;
1136 : }
1137 : }
1138 :
1139 0 : *rolep = role;
1140 0 : return NS_OK;
1141 : }
1142 :
1143 : nsresult
1144 0 : JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
1145 : {
1146 0 : mLastError.clear();
1147 :
1148 0 : MOZ_MTLOG(ML_DEBUG, "SetLocalDescription type=" << type << "\nSDP=\n"
1149 : << sdp);
1150 :
1151 0 : if (type == kJsepSdpRollback) {
1152 0 : if (mState != kJsepStateHaveLocalOffer) {
1153 0 : JSEP_SET_ERROR("Cannot rollback local description in "
1154 : << GetStateStr(mState));
1155 0 : return NS_ERROR_UNEXPECTED;
1156 : }
1157 :
1158 0 : mPendingLocalDescription.reset();
1159 0 : SetState(kJsepStateStable);
1160 0 : mTransports = mOldTransports;
1161 0 : mOldTransports.clear();
1162 0 : return NS_OK;
1163 : }
1164 :
1165 0 : switch (mState) {
1166 : case kJsepStateStable:
1167 0 : if (type != kJsepSdpOffer) {
1168 0 : JSEP_SET_ERROR("Cannot set local answer in state "
1169 : << GetStateStr(mState));
1170 0 : return NS_ERROR_UNEXPECTED;
1171 : }
1172 0 : mIsOfferer = true;
1173 0 : break;
1174 : case kJsepStateHaveRemoteOffer:
1175 0 : if (type != kJsepSdpAnswer && type != kJsepSdpPranswer) {
1176 0 : JSEP_SET_ERROR("Cannot set local offer in state "
1177 : << GetStateStr(mState));
1178 0 : return NS_ERROR_UNEXPECTED;
1179 : }
1180 0 : break;
1181 : default:
1182 0 : JSEP_SET_ERROR("Cannot set local offer or answer in state "
1183 : << GetStateStr(mState));
1184 0 : return NS_ERROR_UNEXPECTED;
1185 : }
1186 :
1187 0 : UniquePtr<Sdp> parsed;
1188 0 : nsresult rv = ParseSdp(sdp, &parsed);
1189 0 : NS_ENSURE_SUCCESS(rv, rv);
1190 :
1191 : // Check that content hasn't done anything unsupported with the SDP
1192 0 : rv = ValidateLocalDescription(*parsed);
1193 0 : NS_ENSURE_SUCCESS(rv, rv);
1194 :
1195 : // Create transport objects.
1196 0 : mOldTransports = mTransports; // Save in case we need to rollback
1197 0 : mTransports.clear();
1198 0 : for (size_t t = 0; t < parsed->GetMediaSectionCount(); ++t) {
1199 0 : mTransports.push_back(RefPtr<JsepTransport>(new JsepTransport));
1200 0 : InitTransport(parsed->GetMediaSection(t), mTransports[t].get());
1201 : }
1202 :
1203 0 : switch (type) {
1204 : case kJsepSdpOffer:
1205 0 : rv = SetLocalDescriptionOffer(Move(parsed));
1206 0 : break;
1207 : case kJsepSdpAnswer:
1208 : case kJsepSdpPranswer:
1209 0 : rv = SetLocalDescriptionAnswer(type, Move(parsed));
1210 0 : break;
1211 : case kJsepSdpRollback:
1212 0 : MOZ_CRASH(); // Handled above
1213 : }
1214 :
1215 0 : return rv;
1216 : }
1217 :
1218 : nsresult
1219 0 : JsepSessionImpl::SetLocalDescriptionOffer(UniquePtr<Sdp> offer)
1220 : {
1221 0 : MOZ_ASSERT(mState == kJsepStateStable);
1222 0 : mPendingLocalDescription = Move(offer);
1223 0 : SetState(kJsepStateHaveLocalOffer);
1224 0 : return NS_OK;
1225 : }
1226 :
1227 : nsresult
1228 0 : JsepSessionImpl::SetLocalDescriptionAnswer(JsepSdpType type,
1229 : UniquePtr<Sdp> answer)
1230 : {
1231 0 : MOZ_ASSERT(mState == kJsepStateHaveRemoteOffer);
1232 0 : mPendingLocalDescription = Move(answer);
1233 :
1234 0 : nsresult rv = ValidateAnswer(*mPendingRemoteDescription,
1235 0 : *mPendingLocalDescription);
1236 0 : NS_ENSURE_SUCCESS(rv, rv);
1237 :
1238 0 : rv = HandleNegotiatedSession(mPendingLocalDescription,
1239 0 : mPendingRemoteDescription);
1240 0 : NS_ENSURE_SUCCESS(rv, rv);
1241 :
1242 0 : mCurrentRemoteDescription = Move(mPendingRemoteDescription);
1243 0 : mCurrentLocalDescription = Move(mPendingLocalDescription);
1244 0 : mWasOffererLastTime = mIsOfferer;
1245 :
1246 0 : SetState(kJsepStateStable);
1247 0 : return NS_OK;
1248 : }
1249 :
1250 : nsresult
1251 0 : JsepSessionImpl::SetRemoteDescription(JsepSdpType type, const std::string& sdp)
1252 : {
1253 0 : mLastError.clear();
1254 0 : mRemoteTracksAdded.clear();
1255 0 : mRemoteTracksRemoved.clear();
1256 :
1257 0 : MOZ_MTLOG(ML_DEBUG, "SetRemoteDescription type=" << type << "\nSDP=\n"
1258 : << sdp);
1259 :
1260 0 : if (type == kJsepSdpRollback) {
1261 0 : if (mState != kJsepStateHaveRemoteOffer) {
1262 0 : JSEP_SET_ERROR("Cannot rollback remote description in "
1263 : << GetStateStr(mState));
1264 0 : return NS_ERROR_UNEXPECTED;
1265 : }
1266 :
1267 0 : mPendingRemoteDescription.reset();
1268 0 : SetState(kJsepStateStable);
1269 :
1270 : // Update the remote tracks to what they were before the SetRemote
1271 0 : return SetRemoteTracksFromDescription(mCurrentRemoteDescription.get());
1272 : }
1273 :
1274 0 : switch (mState) {
1275 : case kJsepStateStable:
1276 0 : if (type != kJsepSdpOffer) {
1277 0 : JSEP_SET_ERROR("Cannot set remote answer in state "
1278 : << GetStateStr(mState));
1279 0 : return NS_ERROR_UNEXPECTED;
1280 : }
1281 0 : mIsOfferer = false;
1282 0 : break;
1283 : case kJsepStateHaveLocalOffer:
1284 : case kJsepStateHaveRemotePranswer:
1285 0 : if (type != kJsepSdpAnswer && type != kJsepSdpPranswer) {
1286 0 : JSEP_SET_ERROR("Cannot set remote offer in state "
1287 : << GetStateStr(mState));
1288 0 : return NS_ERROR_UNEXPECTED;
1289 : }
1290 0 : break;
1291 : default:
1292 0 : JSEP_SET_ERROR("Cannot set remote offer or answer in current state "
1293 : << GetStateStr(mState));
1294 0 : return NS_ERROR_UNEXPECTED;
1295 : }
1296 :
1297 : // Parse.
1298 0 : UniquePtr<Sdp> parsed;
1299 0 : nsresult rv = ParseSdp(sdp, &parsed);
1300 0 : NS_ENSURE_SUCCESS(rv, rv);
1301 :
1302 0 : rv = ValidateRemoteDescription(*parsed);
1303 0 : NS_ENSURE_SUCCESS(rv, rv);
1304 :
1305 : bool iceLite =
1306 0 : parsed->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute);
1307 :
1308 : // check for mismatch ufrag/pwd indicating ice restart
1309 : // can't just check the first one because it might be disabled
1310 0 : bool iceRestarting = false;
1311 0 : if (mCurrentRemoteDescription.get()) {
1312 0 : for (size_t i = 0;
1313 0 : !iceRestarting &&
1314 0 : i < mCurrentRemoteDescription->GetMediaSectionCount();
1315 : ++i) {
1316 :
1317 0 : const SdpMediaSection& newMsection = parsed->GetMediaSection(i);
1318 : const SdpMediaSection& oldMsection =
1319 0 : mCurrentRemoteDescription->GetMediaSection(i);
1320 :
1321 0 : if (mSdpHelper.MsectionIsDisabled(newMsection) ||
1322 0 : mSdpHelper.MsectionIsDisabled(oldMsection)) {
1323 0 : continue;
1324 : }
1325 :
1326 0 : iceRestarting = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
1327 : }
1328 : }
1329 :
1330 0 : std::vector<std::string> iceOptions;
1331 0 : if (parsed->GetAttributeList().HasAttribute(
1332 : SdpAttribute::kIceOptionsAttribute)) {
1333 0 : iceOptions = parsed->GetAttributeList().GetIceOptions().mValues;
1334 : }
1335 :
1336 0 : switch (type) {
1337 : case kJsepSdpOffer:
1338 0 : rv = SetRemoteDescriptionOffer(Move(parsed));
1339 0 : break;
1340 : case kJsepSdpAnswer:
1341 : case kJsepSdpPranswer:
1342 0 : rv = SetRemoteDescriptionAnswer(type, Move(parsed));
1343 0 : break;
1344 : case kJsepSdpRollback:
1345 0 : MOZ_CRASH(); // Handled above
1346 : }
1347 :
1348 0 : if (NS_SUCCEEDED(rv)) {
1349 0 : mRemoteIsIceLite = iceLite;
1350 0 : mIceOptions = iceOptions;
1351 0 : mRemoteIceIsRestarting = iceRestarting;
1352 : }
1353 :
1354 0 : return rv;
1355 : }
1356 :
1357 : nsresult
1358 0 : JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
1359 : const UniquePtr<Sdp>& remote)
1360 : {
1361 : bool remoteIceLite =
1362 0 : remote->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute);
1363 :
1364 0 : mIceControlling = remoteIceLite || mIsOfferer;
1365 :
1366 0 : const Sdp& answer = mIsOfferer ? *remote : *local;
1367 :
1368 0 : SdpHelper::BundledMids bundledMids;
1369 0 : nsresult rv = mSdpHelper.GetBundledMids(answer, &bundledMids);
1370 0 : NS_ENSURE_SUCCESS(rv, rv);
1371 :
1372 0 : if (mTransports.size() < local->GetMediaSectionCount()) {
1373 0 : JSEP_SET_ERROR("Fewer transports set up than m-lines");
1374 0 : MOZ_ASSERT(false);
1375 : return NS_ERROR_FAILURE;
1376 : }
1377 :
1378 0 : for (JsepSendingTrack& trackWrapper : mLocalTracks) {
1379 0 : trackWrapper.mTrack->ClearNegotiatedDetails();
1380 : }
1381 :
1382 0 : for (JsepReceivingTrack& trackWrapper : mRemoteTracks) {
1383 0 : trackWrapper.mTrack->ClearNegotiatedDetails();
1384 : }
1385 :
1386 0 : std::vector<JsepTrackPair> trackPairs;
1387 :
1388 : // Now walk through the m-sections, make sure they match, and create
1389 : // track pairs that describe the media to be set up.
1390 0 : for (size_t i = 0; i < local->GetMediaSectionCount(); ++i) {
1391 : // Skip disabled m-sections.
1392 0 : if (answer.GetMediaSection(i).GetPort() == 0) {
1393 0 : mTransports[i]->Close();
1394 0 : continue;
1395 : }
1396 :
1397 : // The transport details are not necessarily on the m-section we're
1398 : // currently processing.
1399 0 : size_t transportLevel = i;
1400 0 : bool usingBundle = false;
1401 : {
1402 0 : const SdpMediaSection& answerMsection(answer.GetMediaSection(i));
1403 0 : if (answerMsection.GetAttributeList().HasAttribute(
1404 : SdpAttribute::kMidAttribute)) {
1405 0 : if (bundledMids.count(answerMsection.GetAttributeList().GetMid())) {
1406 : const SdpMediaSection* masterBundleMsection =
1407 0 : bundledMids[answerMsection.GetAttributeList().GetMid()];
1408 0 : transportLevel = masterBundleMsection->GetLevel();
1409 0 : usingBundle = true;
1410 0 : if (i != transportLevel) {
1411 0 : mTransports[i]->Close();
1412 : }
1413 : }
1414 : }
1415 : }
1416 :
1417 0 : RefPtr<JsepTransport> transport = mTransports[transportLevel];
1418 :
1419 0 : rv = FinalizeTransport(
1420 0 : remote->GetMediaSection(transportLevel).GetAttributeList(),
1421 0 : answer.GetMediaSection(transportLevel).GetAttributeList(),
1422 0 : transport);
1423 0 : NS_ENSURE_SUCCESS(rv, rv);
1424 :
1425 0 : JsepTrackPair trackPair;
1426 0 : rv = MakeNegotiatedTrackPair(remote->GetMediaSection(i),
1427 0 : local->GetMediaSection(i),
1428 : transport,
1429 : usingBundle,
1430 : transportLevel,
1431 0 : &trackPair);
1432 0 : NS_ENSURE_SUCCESS(rv, rv);
1433 :
1434 0 : trackPairs.push_back(trackPair);
1435 : }
1436 :
1437 0 : JsepTrack::SetUniquePayloadTypes(GetTracks(mRemoteTracks));
1438 :
1439 : // Ouch, this probably needs some dirty bit instead of just clearing
1440 : // stuff for renegotiation.
1441 0 : mNegotiatedTrackPairs = trackPairs;
1442 :
1443 0 : mGeneratedLocalDescription.reset();
1444 :
1445 0 : mNegotiations++;
1446 0 : return NS_OK;
1447 : }
1448 :
1449 : nsresult
1450 0 : JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
1451 : const SdpMediaSection& local,
1452 : const RefPtr<JsepTransport>& transport,
1453 : bool usingBundle,
1454 : size_t transportLevel,
1455 : JsepTrackPair* trackPairOut)
1456 : {
1457 0 : MOZ_ASSERT(transport->mComponents);
1458 0 : const SdpMediaSection& answer = mIsOfferer ? remote : local;
1459 :
1460 : bool sending;
1461 : bool receiving;
1462 :
1463 0 : if (mIsOfferer) {
1464 0 : receiving = answer.IsSending();
1465 0 : sending = answer.IsReceiving();
1466 : } else {
1467 0 : sending = answer.IsSending();
1468 0 : receiving = answer.IsReceiving();
1469 : }
1470 :
1471 0 : MOZ_MTLOG(ML_DEBUG, "Negotiated m= line"
1472 : << " index=" << local.GetLevel()
1473 : << " type=" << local.GetMediaType()
1474 : << " sending=" << sending
1475 : << " receiving=" << receiving);
1476 :
1477 0 : trackPairOut->mLevel = local.GetLevel();
1478 :
1479 0 : if (local.GetMediaType() != SdpMediaSection::kApplication) {
1480 0 : MOZ_ASSERT(mRecvonlySsrcs.size() > local.GetLevel(),
1481 : "Failed to set the default ssrc for an active m-section");
1482 0 : trackPairOut->mRecvonlySsrc = mRecvonlySsrcs[local.GetLevel()];
1483 : }
1484 :
1485 0 : if (usingBundle) {
1486 0 : trackPairOut->SetBundleLevel(transportLevel);
1487 : }
1488 :
1489 0 : auto sendTrack = FindTrackByLevel(mLocalTracks, local.GetLevel());
1490 0 : if (sendTrack != mLocalTracks.end()) {
1491 0 : sendTrack->mTrack->Negotiate(answer, remote);
1492 0 : sendTrack->mTrack->SetActive(sending);
1493 0 : trackPairOut->mSending = sendTrack->mTrack;
1494 0 : } else if (sending) {
1495 0 : JSEP_SET_ERROR("Failed to find local track for level " <<
1496 : local.GetLevel()
1497 : << " in local SDP. This should never happen.");
1498 0 : NS_ASSERTION(false, "Failed to find local track for level");
1499 0 : return NS_ERROR_FAILURE;
1500 : }
1501 :
1502 0 : auto recvTrack = FindTrackByLevel(mRemoteTracks, local.GetLevel());
1503 0 : if (recvTrack != mRemoteTracks.end()) {
1504 0 : recvTrack->mTrack->Negotiate(answer, remote);
1505 0 : recvTrack->mTrack->SetActive(receiving);
1506 0 : trackPairOut->mReceiving = recvTrack->mTrack;
1507 :
1508 0 : if (receiving &&
1509 0 : trackPairOut->HasBundleLevel() &&
1510 0 : recvTrack->mTrack->GetSsrcs().empty() &&
1511 0 : recvTrack->mTrack->GetMediaType() != SdpMediaSection::kApplication) {
1512 0 : MOZ_MTLOG(ML_ERROR, "Bundled m-section has no ssrc attributes. "
1513 : "This may cause media packets to be dropped.");
1514 : }
1515 0 : } else if (receiving) {
1516 0 : JSEP_SET_ERROR("Failed to find remote track for level "
1517 : << local.GetLevel()
1518 : << " in remote SDP. This should never happen.");
1519 0 : NS_ASSERTION(false, "Failed to find remote track for level");
1520 0 : return NS_ERROR_FAILURE;
1521 : }
1522 :
1523 0 : trackPairOut->mRtpTransport = transport;
1524 :
1525 0 : if (transport->mComponents == 2) {
1526 : // RTCP MUX or not.
1527 : // TODO(bug 1095743): verify that the PTs are consistent with mux.
1528 0 : MOZ_MTLOG(ML_DEBUG, "RTCP-MUX is off");
1529 0 : trackPairOut->mRtcpTransport = transport;
1530 : }
1531 :
1532 0 : return NS_OK;
1533 : }
1534 :
1535 : void
1536 0 : JsepSessionImpl::InitTransport(const SdpMediaSection& msection,
1537 : JsepTransport* transport)
1538 : {
1539 0 : if (mSdpHelper.MsectionIsDisabled(msection)) {
1540 0 : transport->Close();
1541 0 : return;
1542 : }
1543 :
1544 0 : if (mSdpHelper.HasRtcp(msection.GetProtocol())) {
1545 0 : transport->mComponents = 2;
1546 : } else {
1547 0 : transport->mComponents = 1;
1548 : }
1549 :
1550 0 : if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
1551 0 : transport->mTransportId = msection.GetAttributeList().GetMid();
1552 : } else {
1553 0 : std::ostringstream os;
1554 0 : os << "level_" << msection.GetLevel() << "(no mid)";
1555 0 : transport->mTransportId = os.str();
1556 : }
1557 : }
1558 :
1559 : nsresult
1560 0 : JsepSessionImpl::FinalizeTransport(const SdpAttributeList& remote,
1561 : const SdpAttributeList& answer,
1562 : const RefPtr<JsepTransport>& transport)
1563 : {
1564 0 : UniquePtr<JsepIceTransport> ice = MakeUnique<JsepIceTransport>();
1565 :
1566 : // We do sanity-checking for these in ParseSdp
1567 0 : ice->mUfrag = remote.GetIceUfrag();
1568 0 : ice->mPwd = remote.GetIcePwd();
1569 0 : if (remote.HasAttribute(SdpAttribute::kCandidateAttribute)) {
1570 0 : ice->mCandidates = remote.GetCandidate();
1571 : }
1572 :
1573 : // RFC 5763 says:
1574 : //
1575 : // The endpoint MUST use the setup attribute defined in [RFC4145].
1576 : // The endpoint that is the offerer MUST use the setup attribute
1577 : // value of setup:actpass and be prepared to receive a client_hello
1578 : // before it receives the answer. The answerer MUST use either a
1579 : // setup attribute value of setup:active or setup:passive. Note that
1580 : // if the answerer uses setup:passive, then the DTLS handshake will
1581 : // not begin until the answerer is received, which adds additional
1582 : // latency. setup:active allows the answer and the DTLS handshake to
1583 : // occur in parallel. Thus, setup:active is RECOMMENDED. Whichever
1584 : // party is active MUST initiate a DTLS handshake by sending a
1585 : // ClientHello over each flow (host/port quartet).
1586 0 : UniquePtr<JsepDtlsTransport> dtls = MakeUnique<JsepDtlsTransport>();
1587 0 : dtls->mFingerprints = remote.GetFingerprint();
1588 0 : if (!answer.HasAttribute(mozilla::SdpAttribute::kSetupAttribute)) {
1589 0 : dtls->mRole = mIsOfferer ? JsepDtlsTransport::kJsepDtlsServer
1590 : : JsepDtlsTransport::kJsepDtlsClient;
1591 : } else {
1592 0 : if (mIsOfferer) {
1593 0 : dtls->mRole = (answer.GetSetup().mRole == SdpSetupAttribute::kActive)
1594 0 : ? JsepDtlsTransport::kJsepDtlsServer
1595 : : JsepDtlsTransport::kJsepDtlsClient;
1596 : } else {
1597 0 : dtls->mRole = (answer.GetSetup().mRole == SdpSetupAttribute::kActive)
1598 0 : ? JsepDtlsTransport::kJsepDtlsClient
1599 : : JsepDtlsTransport::kJsepDtlsServer;
1600 : }
1601 : }
1602 :
1603 0 : transport->mIce = Move(ice);
1604 0 : transport->mDtls = Move(dtls);
1605 :
1606 0 : if (answer.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
1607 0 : transport->mComponents = 1;
1608 : }
1609 :
1610 0 : return NS_OK;
1611 : }
1612 :
1613 : nsresult
1614 0 : JsepSessionImpl::AddTransportAttributes(SdpMediaSection* msection,
1615 : SdpSetupAttribute::Role dtlsRole)
1616 : {
1617 0 : if (mIceUfrag.empty() || mIcePwd.empty()) {
1618 0 : JSEP_SET_ERROR("Missing ICE ufrag or password");
1619 0 : return NS_ERROR_FAILURE;
1620 : }
1621 :
1622 0 : SdpAttributeList& attrList = msection->GetAttributeList();
1623 : attrList.SetAttribute(
1624 0 : new SdpStringAttribute(SdpAttribute::kIceUfragAttribute, mIceUfrag));
1625 : attrList.SetAttribute(
1626 0 : new SdpStringAttribute(SdpAttribute::kIcePwdAttribute, mIcePwd));
1627 :
1628 0 : msection->GetAttributeList().SetAttribute(new SdpSetupAttribute(dtlsRole));
1629 :
1630 0 : return NS_OK;
1631 : }
1632 :
1633 : nsresult
1634 0 : JsepSessionImpl::CopyPreviousTransportParams(const Sdp& oldAnswer,
1635 : const Sdp& offerersPreviousSdp,
1636 : const Sdp& newOffer,
1637 : Sdp* newLocal)
1638 : {
1639 0 : for (size_t i = 0; i < oldAnswer.GetMediaSectionCount(); ++i) {
1640 0 : if (!mSdpHelper.MsectionIsDisabled(newLocal->GetMediaSection(i)) &&
1641 0 : mSdpHelper.AreOldTransportParamsValid(oldAnswer,
1642 : offerersPreviousSdp,
1643 : newOffer,
1644 0 : i) &&
1645 0 : !mRemoteIceIsRestarting
1646 : ) {
1647 : // If newLocal is an offer, this will be the number of components we used
1648 : // last time, and if it is an answer, this will be the number of
1649 : // components we've decided we're using now.
1650 0 : size_t numComponents = mTransports[i]->mComponents;
1651 0 : nsresult rv = mSdpHelper.CopyTransportParams(
1652 : numComponents,
1653 0 : mCurrentLocalDescription->GetMediaSection(i),
1654 0 : &newLocal->GetMediaSection(i));
1655 0 : NS_ENSURE_SUCCESS(rv, rv);
1656 : }
1657 : }
1658 :
1659 0 : return NS_OK;
1660 : }
1661 :
1662 : nsresult
1663 0 : JsepSessionImpl::ParseSdp(const std::string& sdp, UniquePtr<Sdp>* parsedp)
1664 : {
1665 0 : UniquePtr<Sdp> parsed = mParser.Parse(sdp);
1666 0 : if (!parsed) {
1667 0 : std::string error = "Failed to parse SDP: ";
1668 0 : mSdpHelper.appendSdpParseErrors(mParser.GetParseErrors(), &error);
1669 0 : JSEP_SET_ERROR(error);
1670 0 : return NS_ERROR_INVALID_ARG;
1671 : }
1672 :
1673 : // Verify that the JSEP rules for all SDP are followed
1674 0 : if (!parsed->GetMediaSectionCount()) {
1675 0 : JSEP_SET_ERROR("Description has no media sections");
1676 0 : return NS_ERROR_INVALID_ARG;
1677 : }
1678 :
1679 0 : std::set<std::string> trackIds;
1680 :
1681 0 : for (size_t i = 0; i < parsed->GetMediaSectionCount(); ++i) {
1682 0 : if (mSdpHelper.MsectionIsDisabled(parsed->GetMediaSection(i))) {
1683 : // Disabled, let this stuff slide.
1684 0 : continue;
1685 : }
1686 :
1687 0 : const SdpMediaSection& msection(parsed->GetMediaSection(i));
1688 0 : auto& mediaAttrs = msection.GetAttributeList();
1689 :
1690 0 : if (mediaAttrs.GetIceUfrag().empty()) {
1691 0 : JSEP_SET_ERROR("Invalid description, no ice-ufrag attribute");
1692 0 : return NS_ERROR_INVALID_ARG;
1693 : }
1694 :
1695 0 : if (mediaAttrs.GetIcePwd().empty()) {
1696 0 : JSEP_SET_ERROR("Invalid description, no ice-pwd attribute");
1697 0 : return NS_ERROR_INVALID_ARG;
1698 : }
1699 :
1700 0 : if (!mediaAttrs.HasAttribute(SdpAttribute::kFingerprintAttribute)) {
1701 0 : JSEP_SET_ERROR("Invalid description, no fingerprint attribute");
1702 0 : return NS_ERROR_INVALID_ARG;
1703 : }
1704 :
1705 : const SdpFingerprintAttributeList& fingerprints(
1706 0 : mediaAttrs.GetFingerprint());
1707 0 : if (fingerprints.mFingerprints.empty()) {
1708 0 : JSEP_SET_ERROR("Invalid description, no supported fingerprint algorithms "
1709 : "present");
1710 0 : return NS_ERROR_INVALID_ARG;
1711 : }
1712 :
1713 0 : if (mediaAttrs.HasAttribute(SdpAttribute::kSetupAttribute, true) &&
1714 0 : mediaAttrs.GetSetup().mRole == SdpSetupAttribute::kHoldconn) {
1715 0 : JSEP_SET_ERROR("Description has illegal setup attribute "
1716 : "\"holdconn\" in m-section at level "
1717 : << i);
1718 0 : return NS_ERROR_INVALID_ARG;
1719 : }
1720 :
1721 0 : std::string streamId;
1722 0 : std::string trackId;
1723 0 : nsresult rv = mSdpHelper.GetIdsFromMsid(*parsed,
1724 0 : parsed->GetMediaSection(i),
1725 : &streamId,
1726 0 : &trackId);
1727 :
1728 0 : if (NS_SUCCEEDED(rv)) {
1729 0 : if (trackIds.count(trackId)) {
1730 0 : JSEP_SET_ERROR("track id:" << trackId
1731 : << " appears in more than one m-section at level " << i);
1732 0 : return NS_ERROR_INVALID_ARG;
1733 : }
1734 :
1735 0 : trackIds.insert(trackId);
1736 0 : } else if (rv != NS_ERROR_NOT_AVAILABLE) {
1737 : // Error has already been set
1738 0 : return rv;
1739 : }
1740 :
1741 0 : static const std::bitset<128> forbidden = GetForbiddenSdpPayloadTypes();
1742 0 : if (msection.GetMediaType() == SdpMediaSection::kAudio ||
1743 0 : msection.GetMediaType() == SdpMediaSection::kVideo) {
1744 : // Sanity-check that payload type can work with RTP
1745 0 : for (const std::string& fmt : msection.GetFormats()) {
1746 : uint16_t payloadType;
1747 0 : if (!SdpHelper::GetPtAsInt(fmt, &payloadType)) {
1748 0 : JSEP_SET_ERROR("Payload type \"" << fmt <<
1749 : "\" is not a 16-bit unsigned int at level " << i);
1750 0 : return NS_ERROR_INVALID_ARG;
1751 : }
1752 0 : if (payloadType > 127) {
1753 0 : JSEP_SET_ERROR("audio/video payload type \"" << fmt <<
1754 : "\" is too large at level " << i);
1755 0 : return NS_ERROR_INVALID_ARG;
1756 : }
1757 0 : if (forbidden.test(payloadType)) {
1758 0 : JSEP_SET_ERROR("Illegal audio/video payload type \"" << fmt <<
1759 : "\" at level " << i);
1760 0 : return NS_ERROR_INVALID_ARG;
1761 : }
1762 : }
1763 : }
1764 : }
1765 :
1766 0 : *parsedp = Move(parsed);
1767 0 : return NS_OK;
1768 : }
1769 :
1770 : nsresult
1771 0 : JsepSessionImpl::SetRemoteDescriptionOffer(UniquePtr<Sdp> offer)
1772 : {
1773 0 : MOZ_ASSERT(mState == kJsepStateStable);
1774 :
1775 0 : nsresult rv = ValidateOffer(*offer);
1776 0 : NS_ENSURE_SUCCESS(rv, rv);
1777 :
1778 : // TODO(bug 1095780): Note that we create remote tracks even when
1779 : // They contain only codecs we can't negotiate or other craziness.
1780 0 : rv = SetRemoteTracksFromDescription(offer.get());
1781 0 : NS_ENSURE_SUCCESS(rv, rv);
1782 :
1783 0 : mPendingRemoteDescription = Move(offer);
1784 :
1785 0 : SetState(kJsepStateHaveRemoteOffer);
1786 0 : return NS_OK;
1787 : }
1788 :
1789 : nsresult
1790 0 : JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type,
1791 : UniquePtr<Sdp> answer)
1792 : {
1793 0 : MOZ_ASSERT(mState == kJsepStateHaveLocalOffer ||
1794 : mState == kJsepStateHaveRemotePranswer);
1795 :
1796 0 : mPendingRemoteDescription = Move(answer);
1797 :
1798 0 : nsresult rv = ValidateAnswer(*mPendingLocalDescription,
1799 0 : *mPendingRemoteDescription);
1800 0 : NS_ENSURE_SUCCESS(rv, rv);
1801 :
1802 : // TODO(bug 1095780): Note that this creates remote tracks even if
1803 : // we offered sendonly and other side offered sendrecv or recvonly.
1804 0 : rv = SetRemoteTracksFromDescription(mPendingRemoteDescription.get());
1805 0 : NS_ENSURE_SUCCESS(rv, rv);
1806 :
1807 0 : rv = HandleNegotiatedSession(mPendingLocalDescription,
1808 0 : mPendingRemoteDescription);
1809 0 : NS_ENSURE_SUCCESS(rv, rv);
1810 :
1811 0 : mCurrentRemoteDescription = Move(mPendingRemoteDescription);
1812 0 : mCurrentLocalDescription = Move(mPendingLocalDescription);
1813 0 : mWasOffererLastTime = mIsOfferer;
1814 :
1815 0 : SetState(kJsepStateStable);
1816 0 : return NS_OK;
1817 : }
1818 :
1819 : nsresult
1820 0 : JsepSessionImpl::SetRemoteTracksFromDescription(const Sdp* remoteDescription)
1821 : {
1822 : // Unassign all remote tracks
1823 0 : for (auto& remoteTrack : mRemoteTracks) {
1824 0 : remoteTrack.mAssignedMLine.reset();
1825 : }
1826 :
1827 : // This will not exist if we're rolling back the first remote description
1828 0 : if (remoteDescription) {
1829 0 : size_t numMlines = remoteDescription->GetMediaSectionCount();
1830 : nsresult rv;
1831 :
1832 : // Iterate over the sdp, re-assigning or creating remote tracks as we go
1833 0 : for (size_t i = 0; i < numMlines; ++i) {
1834 0 : const SdpMediaSection& msection = remoteDescription->GetMediaSection(i);
1835 :
1836 0 : if (mSdpHelper.MsectionIsDisabled(msection) || !msection.IsSending()) {
1837 0 : continue;
1838 : }
1839 :
1840 0 : std::vector<JsepReceivingTrack>::iterator track;
1841 :
1842 0 : if (msection.GetMediaType() == SdpMediaSection::kApplication) {
1843 : // Datachannel doesn't have msid, just search by type
1844 : track = FindUnassignedTrackByType(mRemoteTracks,
1845 0 : msection.GetMediaType());
1846 : } else {
1847 0 : std::string streamId;
1848 0 : std::string trackId;
1849 0 : rv = GetRemoteIds(*remoteDescription, msection, &streamId, &trackId);
1850 0 : NS_ENSURE_SUCCESS(rv, rv);
1851 :
1852 0 : track = FindTrackByIds(mRemoteTracks, streamId, trackId);
1853 : }
1854 :
1855 0 : if (track == mRemoteTracks.end()) {
1856 0 : RefPtr<JsepTrack> track;
1857 0 : rv = CreateReceivingTrack(i, *remoteDescription, msection, &track);
1858 0 : NS_ENSURE_SUCCESS(rv, rv);
1859 :
1860 0 : JsepReceivingTrack rtrack;
1861 0 : rtrack.mTrack = track;
1862 0 : rtrack.mAssignedMLine = Some(i);
1863 0 : mRemoteTracks.push_back(rtrack);
1864 0 : mRemoteTracksAdded.push_back(rtrack);
1865 : } else {
1866 0 : track->mAssignedMLine = Some(i);
1867 : }
1868 : }
1869 : }
1870 :
1871 : // Remove any unassigned remote track ids
1872 0 : for (size_t i = 0; i < mRemoteTracks.size();) {
1873 0 : if (!mRemoteTracks[i].mAssignedMLine.isSome()) {
1874 0 : mRemoteTracksRemoved.push_back(mRemoteTracks[i]);
1875 0 : mRemoteTracks.erase(mRemoteTracks.begin() + i);
1876 : } else {
1877 0 : ++i;
1878 : }
1879 : }
1880 :
1881 0 : return NS_OK;
1882 : }
1883 :
1884 : nsresult
1885 0 : JsepSessionImpl::ValidateLocalDescription(const Sdp& description)
1886 : {
1887 : // TODO(bug 1095226): Better checking.
1888 0 : if (!mGeneratedLocalDescription) {
1889 0 : JSEP_SET_ERROR("Calling SetLocal without first calling CreateOffer/Answer"
1890 : " is not supported.");
1891 0 : return NS_ERROR_UNEXPECTED;
1892 : }
1893 :
1894 0 : if (description.GetMediaSectionCount() !=
1895 0 : mGeneratedLocalDescription->GetMediaSectionCount()) {
1896 0 : JSEP_SET_ERROR("Changing the number of m-sections is not allowed");
1897 0 : return NS_ERROR_INVALID_ARG;
1898 : }
1899 :
1900 0 : for (size_t i = 0; i < description.GetMediaSectionCount(); ++i) {
1901 0 : auto& origMsection = mGeneratedLocalDescription->GetMediaSection(i);
1902 0 : auto& finalMsection = description.GetMediaSection(i);
1903 0 : if (origMsection.GetMediaType() != finalMsection.GetMediaType()) {
1904 0 : JSEP_SET_ERROR("Changing the media-type of m-sections is not allowed");
1905 0 : return NS_ERROR_INVALID_ARG;
1906 : }
1907 :
1908 : // These will be present in reoffer
1909 0 : if (!mCurrentLocalDescription) {
1910 0 : if (finalMsection.GetAttributeList().HasAttribute(
1911 : SdpAttribute::kCandidateAttribute)) {
1912 0 : JSEP_SET_ERROR("Adding your own candidate attributes is not supported");
1913 0 : return NS_ERROR_INVALID_ARG;
1914 : }
1915 :
1916 0 : if (finalMsection.GetAttributeList().HasAttribute(
1917 : SdpAttribute::kEndOfCandidatesAttribute)) {
1918 0 : JSEP_SET_ERROR("Why are you trying to set a=end-of-candidates?");
1919 0 : return NS_ERROR_INVALID_ARG;
1920 : }
1921 : }
1922 :
1923 : // TODO(bug 1095218): Check msid
1924 : // TODO(bug 1095226): Check ice-ufrag and ice-pwd
1925 : // TODO(bug 1095226): Check fingerprints
1926 : // TODO(bug 1095226): Check payload types (at least ensure that payload
1927 : // types we don't actually support weren't added)
1928 : // TODO(bug 1095226): Check ice-options?
1929 : }
1930 :
1931 0 : if (description.GetAttributeList().HasAttribute(
1932 : SdpAttribute::kIceLiteAttribute)) {
1933 0 : JSEP_SET_ERROR("Running ICE in lite mode is unsupported");
1934 0 : return NS_ERROR_INVALID_ARG;
1935 : }
1936 :
1937 0 : return NS_OK;
1938 : }
1939 :
1940 : nsresult
1941 0 : JsepSessionImpl::ValidateRemoteDescription(const Sdp& description)
1942 : {
1943 0 : if (!mCurrentRemoteDescription || !mCurrentLocalDescription) {
1944 : // Not renegotiation; checks for whether a remote answer are consistent
1945 : // with our offer are handled in ValidateAnswer()
1946 0 : return NS_OK;
1947 : }
1948 :
1949 0 : if (mCurrentRemoteDescription->GetMediaSectionCount() >
1950 0 : description.GetMediaSectionCount()) {
1951 0 : JSEP_SET_ERROR("New remote description has fewer m-sections than the "
1952 : "previous remote description.");
1953 0 : return NS_ERROR_INVALID_ARG;
1954 : }
1955 :
1956 : // These are solely to check that bundle is valid
1957 0 : SdpHelper::BundledMids bundledMids;
1958 0 : nsresult rv = GetNegotiatedBundledMids(&bundledMids);
1959 0 : NS_ENSURE_SUCCESS(rv, rv);
1960 :
1961 0 : SdpHelper::BundledMids newBundledMids;
1962 0 : rv = mSdpHelper.GetBundledMids(description, &newBundledMids);
1963 0 : NS_ENSURE_SUCCESS(rv, rv);
1964 :
1965 : // check for partial ice restart, which is not supported
1966 0 : Maybe<bool> iceCredsDiffer;
1967 0 : for (size_t i = 0;
1968 0 : i < mCurrentRemoteDescription->GetMediaSectionCount();
1969 : ++i) {
1970 :
1971 0 : const SdpMediaSection& newMsection = description.GetMediaSection(i);
1972 : const SdpMediaSection& oldMsection =
1973 0 : mCurrentRemoteDescription->GetMediaSection(i);
1974 :
1975 0 : if (mSdpHelper.MsectionIsDisabled(newMsection) ||
1976 0 : mSdpHelper.MsectionIsDisabled(oldMsection)) {
1977 0 : continue;
1978 : }
1979 :
1980 0 : if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
1981 0 : JSEP_SET_ERROR("Remote description changes the media type of m-line "
1982 : << i);
1983 0 : return NS_ERROR_INVALID_ARG;
1984 : }
1985 :
1986 0 : bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
1987 : // Detect whether all the creds are the same or all are different
1988 0 : if (!iceCredsDiffer.isSome()) {
1989 : // for the first msection capture whether creds are different or same
1990 0 : iceCredsDiffer = mozilla::Some(differ);
1991 0 : } else if (iceCredsDiffer.isSome() && *iceCredsDiffer != differ) {
1992 : // subsequent msections must match the first sections
1993 0 : JSEP_SET_ERROR("Partial ICE restart is unsupported at this time "
1994 : "(new remote description changes either the ice-ufrag "
1995 : "or ice-pwd on fewer than all msections)");
1996 0 : return NS_ERROR_INVALID_ARG;
1997 : }
1998 : }
1999 :
2000 0 : return NS_OK;
2001 : }
2002 :
2003 : nsresult
2004 0 : JsepSessionImpl::ValidateOffer(const Sdp& offer)
2005 : {
2006 0 : for (size_t i = 0; i < offer.GetMediaSectionCount(); ++i) {
2007 0 : const SdpMediaSection& offerMsection = offer.GetMediaSection(i);
2008 0 : if (mSdpHelper.MsectionIsDisabled(offerMsection)) {
2009 0 : continue;
2010 : }
2011 :
2012 0 : const SdpAttributeList& offerAttrs(offerMsection.GetAttributeList());
2013 0 : if (!offerAttrs.HasAttribute(SdpAttribute::kSetupAttribute, true)) {
2014 0 : JSEP_SET_ERROR("Offer is missing required setup attribute "
2015 : " at level " << i);
2016 0 : return NS_ERROR_INVALID_ARG;
2017 : }
2018 : }
2019 :
2020 0 : return NS_OK;
2021 : }
2022 :
2023 : nsresult
2024 0 : JsepSessionImpl::ValidateAnswer(const Sdp& offer, const Sdp& answer)
2025 : {
2026 0 : if (offer.GetMediaSectionCount() != answer.GetMediaSectionCount()) {
2027 0 : JSEP_SET_ERROR("Offer and answer have different number of m-lines "
2028 : << "(" << offer.GetMediaSectionCount() << " vs "
2029 : << answer.GetMediaSectionCount() << ")");
2030 0 : return NS_ERROR_INVALID_ARG;
2031 : }
2032 :
2033 0 : for (size_t i = 0; i < offer.GetMediaSectionCount(); ++i) {
2034 0 : const SdpMediaSection& offerMsection = offer.GetMediaSection(i);
2035 0 : const SdpMediaSection& answerMsection = answer.GetMediaSection(i);
2036 :
2037 0 : if (offerMsection.GetMediaType() != answerMsection.GetMediaType()) {
2038 0 : JSEP_SET_ERROR(
2039 : "Answer and offer have different media types at m-line " << i);
2040 0 : return NS_ERROR_INVALID_ARG;
2041 : }
2042 :
2043 0 : if (!offerMsection.IsSending() && answerMsection.IsReceiving()) {
2044 0 : JSEP_SET_ERROR("Answer tried to set recv when offer did not set send");
2045 0 : return NS_ERROR_INVALID_ARG;
2046 : }
2047 :
2048 0 : if (!offerMsection.IsReceiving() && answerMsection.IsSending()) {
2049 0 : JSEP_SET_ERROR("Answer tried to set send when offer did not set recv");
2050 0 : return NS_ERROR_INVALID_ARG;
2051 : }
2052 :
2053 0 : const SdpAttributeList& answerAttrs(answerMsection.GetAttributeList());
2054 0 : const SdpAttributeList& offerAttrs(offerMsection.GetAttributeList());
2055 0 : if (answerAttrs.HasAttribute(SdpAttribute::kMidAttribute) &&
2056 0 : offerAttrs.HasAttribute(SdpAttribute::kMidAttribute) &&
2057 0 : offerAttrs.GetMid() != answerAttrs.GetMid()) {
2058 0 : JSEP_SET_ERROR("Answer changes mid for level, was \'"
2059 : << offerMsection.GetAttributeList().GetMid()
2060 : << "\', now \'"
2061 : << answerMsection.GetAttributeList().GetMid() << "\'");
2062 0 : return NS_ERROR_INVALID_ARG;
2063 : }
2064 :
2065 0 : if (answerAttrs.HasAttribute(SdpAttribute::kSetupAttribute, true) &&
2066 0 : answerAttrs.GetSetup().mRole == SdpSetupAttribute::kActpass) {
2067 0 : JSEP_SET_ERROR("Answer contains illegal setup attribute \"actpass\""
2068 : " at level " << i);
2069 0 : return NS_ERROR_INVALID_ARG;
2070 : }
2071 :
2072 : // Sanity check extmap
2073 0 : if (answerAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)) {
2074 0 : if (!offerAttrs.HasAttribute(SdpAttribute::kExtmapAttribute)) {
2075 0 : JSEP_SET_ERROR("Answer adds extmap attributes to level " << i);
2076 0 : return NS_ERROR_INVALID_ARG;
2077 : }
2078 :
2079 0 : for (const auto& ansExt : answerAttrs.GetExtmap().mExtmaps) {
2080 0 : bool found = false;
2081 0 : for (const auto& offExt : offerAttrs.GetExtmap().mExtmaps) {
2082 0 : if (ansExt.extensionname == offExt.extensionname) {
2083 0 : if ((ansExt.direction & reverse(offExt.direction))
2084 0 : != ansExt.direction) {
2085 : // FIXME we do not return an error here, because Chrome up to
2086 : // version 57 is actually tripping over this if they are the
2087 : // answerer. See bug 1355010 for details.
2088 0 : MOZ_MTLOG(ML_WARNING, "Answer has inconsistent direction on extmap "
2089 : "attribute at level " << i << " ("
2090 : << ansExt.extensionname << "). Offer had "
2091 : << offExt.direction << ", answer had "
2092 : << ansExt.direction << ".");
2093 : // return NS_ERROR_INVALID_ARG;
2094 : }
2095 :
2096 0 : if (offExt.entry < 4096 && (offExt.entry != ansExt.entry)) {
2097 : // FIXME we do not return an error here, because Cisco Spark
2098 : // actually does respond with different extension ID's then we
2099 : // offer. See bug 1361206 for details.
2100 0 : MOZ_MTLOG(ML_WARNING, "Answer changed id for extmap attribute at"
2101 : " level " << i << " (" << offExt.extensionname << ") "
2102 : "from " << offExt.entry << " to "
2103 : << ansExt.entry << ".");
2104 : // return NS_ERROR_INVALID_ARG;
2105 : }
2106 :
2107 0 : if (ansExt.entry >= 4096) {
2108 0 : JSEP_SET_ERROR("Answer used an invalid id (" << ansExt.entry
2109 : << ") for extmap attribute at level " << i
2110 : << " (" << ansExt.extensionname << ").");
2111 0 : return NS_ERROR_INVALID_ARG;
2112 : }
2113 :
2114 0 : found = true;
2115 0 : break;
2116 : }
2117 : }
2118 :
2119 0 : if (!found) {
2120 0 : JSEP_SET_ERROR("Answer has extmap " << ansExt.extensionname << " at "
2121 : "level " << i << " that was not present in offer.");
2122 0 : return NS_ERROR_INVALID_ARG;
2123 : }
2124 : }
2125 : }
2126 : }
2127 :
2128 0 : return NS_OK;
2129 : }
2130 :
2131 : nsresult
2132 0 : JsepSessionImpl::CreateReceivingTrack(size_t mline,
2133 : const Sdp& sdp,
2134 : const SdpMediaSection& msection,
2135 : RefPtr<JsepTrack>* track)
2136 : {
2137 0 : std::string streamId;
2138 0 : std::string trackId;
2139 :
2140 0 : nsresult rv = GetRemoteIds(sdp, msection, &streamId, &trackId);
2141 0 : NS_ENSURE_SUCCESS(rv, rv);
2142 :
2143 0 : *track = new JsepTrack(msection.GetMediaType(),
2144 : streamId,
2145 : trackId,
2146 0 : sdp::kRecv);
2147 :
2148 0 : (*track)->SetCNAME(mSdpHelper.GetCNAME(msection));
2149 0 : (*track)->PopulateCodecs(mSupportedCodecs.values);
2150 :
2151 0 : return NS_OK;
2152 : }
2153 :
2154 : nsresult
2155 0 : JsepSessionImpl::CreateGenericSDP(UniquePtr<Sdp>* sdpp)
2156 : {
2157 : // draft-ietf-rtcweb-jsep-08 Section 5.2.1:
2158 : // o The second SDP line MUST be an "o=" line, as specified in
2159 : // [RFC4566], Section 5.2. The value of the <username> field SHOULD
2160 : // be "-". The value of the <sess-id> field SHOULD be a
2161 : // cryptographically random number. To ensure uniqueness, this
2162 : // number SHOULD be at least 64 bits long. The value of the <sess-
2163 : // version> field SHOULD be zero. The value of the <nettype>
2164 : // <addrtype> <unicast-address> tuple SHOULD be set to a non-
2165 : // meaningful address, such as IN IP4 0.0.0.0, to prevent leaking the
2166 : // local address in this field. As mentioned in [RFC4566], the
2167 : // entire o= line needs to be unique, but selecting a random number
2168 : // for <sess-id> is sufficient to accomplish this.
2169 :
2170 : auto origin =
2171 : SdpOrigin("mozilla...THIS_IS_SDPARTA-" MOZ_APP_UA_VERSION,
2172 : mSessionId,
2173 : mSessionVersion,
2174 : sdp::kIPv4,
2175 0 : "0.0.0.0");
2176 :
2177 0 : UniquePtr<Sdp> sdp = MakeUnique<SipccSdp>(origin);
2178 :
2179 0 : if (mDtlsFingerprints.empty()) {
2180 0 : JSEP_SET_ERROR("Missing DTLS fingerprint");
2181 0 : return NS_ERROR_FAILURE;
2182 : }
2183 :
2184 : UniquePtr<SdpFingerprintAttributeList> fpl =
2185 0 : MakeUnique<SdpFingerprintAttributeList>();
2186 0 : for (auto& dtlsFingerprint : mDtlsFingerprints) {
2187 0 : fpl->PushEntry(dtlsFingerprint.mAlgorithm, dtlsFingerprint.mValue);
2188 : }
2189 0 : sdp->GetAttributeList().SetAttribute(fpl.release());
2190 :
2191 0 : auto* iceOpts = new SdpOptionsAttribute(SdpAttribute::kIceOptionsAttribute);
2192 0 : iceOpts->PushEntry("trickle");
2193 0 : sdp->GetAttributeList().SetAttribute(iceOpts);
2194 :
2195 : // This assumes content doesn't add a bunch of msid attributes with a
2196 : // different semantic in mind.
2197 0 : std::vector<std::string> msids;
2198 0 : msids.push_back("*");
2199 0 : mSdpHelper.SetupMsidSemantic(msids, sdp.get());
2200 :
2201 0 : *sdpp = Move(sdp);
2202 0 : return NS_OK;
2203 : }
2204 :
2205 : nsresult
2206 0 : JsepSessionImpl::SetupIds()
2207 : {
2208 : SECStatus rv = PK11_GenerateRandom(
2209 0 : reinterpret_cast<unsigned char*>(&mSessionId), sizeof(mSessionId));
2210 : // RFC 3264 says that session-ids MUST be representable as a _signed_
2211 : // 64 bit number, meaning the MSB cannot be set.
2212 0 : mSessionId = mSessionId >> 1;
2213 0 : if (rv != SECSuccess) {
2214 0 : JSEP_SET_ERROR("Failed to generate session id: " << rv);
2215 0 : return NS_ERROR_FAILURE;
2216 : }
2217 :
2218 0 : if (!mUuidGen->Generate(&mDefaultRemoteStreamId)) {
2219 0 : JSEP_SET_ERROR("Failed to generate default uuid for streams");
2220 0 : return NS_ERROR_FAILURE;
2221 : }
2222 :
2223 0 : if (!mUuidGen->Generate(&mCNAME)) {
2224 0 : JSEP_SET_ERROR("Failed to generate CNAME");
2225 0 : return NS_ERROR_FAILURE;
2226 : }
2227 :
2228 0 : return NS_OK;
2229 : }
2230 :
2231 : nsresult
2232 0 : JsepSessionImpl::CreateSsrc(uint32_t* ssrc)
2233 : {
2234 0 : do {
2235 : SECStatus rv = PK11_GenerateRandom(
2236 0 : reinterpret_cast<unsigned char*>(ssrc), sizeof(uint32_t));
2237 0 : if (rv != SECSuccess) {
2238 0 : JSEP_SET_ERROR("Failed to generate SSRC, error=" << rv);
2239 0 : return NS_ERROR_FAILURE;
2240 : }
2241 0 : } while (mSsrcs.count(*ssrc));
2242 0 : mSsrcs.insert(*ssrc);
2243 :
2244 0 : return NS_OK;
2245 : }
2246 :
2247 : void
2248 0 : JsepSessionImpl::SetupDefaultCodecs()
2249 : {
2250 : // Supported audio codecs.
2251 : // Per jmspeex on IRC:
2252 : // For 32KHz sampling, 28 is ok, 32 is good, 40 should be really good
2253 : // quality. Note that 1-2Kbps will be wasted on a stereo Opus channel
2254 : // with mono input compared to configuring it for mono.
2255 : // If we reduce bitrate enough Opus will low-pass us; 16000 will kill a
2256 : // 9KHz tone. This should be adaptive when we're at the low-end of video
2257 : // bandwidth (say <100Kbps), and if we're audio-only, down to 8 or
2258 : // 12Kbps.
2259 0 : mSupportedCodecs.values.push_back(new JsepAudioCodecDescription(
2260 : "109",
2261 : "opus",
2262 : 48000,
2263 : 2,
2264 : 960,
2265 : #ifdef WEBRTC_GONK
2266 : // TODO Move this elsewhere to be adaptive to rate - Bug 1207925
2267 : 16000 // B2G uses lower capture sampling rate
2268 : #else
2269 : 40000
2270 : #endif
2271 0 : ));
2272 :
2273 0 : mSupportedCodecs.values.push_back(new JsepAudioCodecDescription(
2274 : "9",
2275 : "G722",
2276 : 8000,
2277 : 1,
2278 : 320,
2279 0 : 64000));
2280 :
2281 : // packet size and bitrate values below copied from sipcc.
2282 : // May need reevaluation from a media expert.
2283 0 : mSupportedCodecs.values.push_back(
2284 0 : new JsepAudioCodecDescription("0",
2285 : "PCMU",
2286 : 8000,
2287 : 1,
2288 : 8000 / 50, // frequency / 50
2289 : 8 * 8000 * 1 // 8 * frequency * channels
2290 0 : ));
2291 :
2292 0 : mSupportedCodecs.values.push_back(
2293 0 : new JsepAudioCodecDescription("8",
2294 : "PCMA",
2295 : 8000,
2296 : 1,
2297 : 8000 / 50, // frequency / 50
2298 : 8 * 8000 * 1 // 8 * frequency * channels
2299 0 : ));
2300 :
2301 : // note: because telephone-event is effectively a marker codec that indicates
2302 : // that dtmf rtp packets may be passed, the packetSize and bitRate fields
2303 : // don't make sense here. For now, use zero. (mjf)
2304 0 : mSupportedCodecs.values.push_back(
2305 0 : new JsepAudioCodecDescription("101",
2306 : "telephone-event",
2307 : 8000,
2308 : 1,
2309 : 0, // packetSize doesn't make sense here
2310 : 0 // bitRate doesn't make sense here
2311 0 : ));
2312 :
2313 : // Supported video codecs.
2314 : // Note: order here implies priority for building offers!
2315 : JsepVideoCodecDescription* vp8 = new JsepVideoCodecDescription(
2316 : "120",
2317 : "VP8",
2318 : 90000
2319 0 : );
2320 : // Defaults for mandatory params
2321 0 : vp8->mConstraints.maxFs = 12288; // Enough for 2048x1536
2322 0 : vp8->mConstraints.maxFps = 60;
2323 0 : mSupportedCodecs.values.push_back(vp8);
2324 :
2325 : JsepVideoCodecDescription* vp9 = new JsepVideoCodecDescription(
2326 : "121",
2327 : "VP9",
2328 : 90000
2329 0 : );
2330 : // Defaults for mandatory params
2331 0 : vp9->mConstraints.maxFs = 12288; // Enough for 2048x1536
2332 0 : vp9->mConstraints.maxFps = 60;
2333 0 : mSupportedCodecs.values.push_back(vp9);
2334 :
2335 : JsepVideoCodecDescription* h264_1 = new JsepVideoCodecDescription(
2336 : "126",
2337 : "H264",
2338 : 90000
2339 0 : );
2340 0 : h264_1->mPacketizationMode = 1;
2341 : // Defaults for mandatory params
2342 0 : h264_1->mProfileLevelId = 0x42E00D;
2343 0 : mSupportedCodecs.values.push_back(h264_1);
2344 :
2345 : JsepVideoCodecDescription* h264_0 = new JsepVideoCodecDescription(
2346 : "97",
2347 : "H264",
2348 : 90000
2349 0 : );
2350 0 : h264_0->mPacketizationMode = 0;
2351 : // Defaults for mandatory params
2352 0 : h264_0->mProfileLevelId = 0x42E00D;
2353 0 : mSupportedCodecs.values.push_back(h264_0);
2354 :
2355 : JsepVideoCodecDescription* red = new JsepVideoCodecDescription(
2356 : "122", // payload type
2357 : "red", // codec name
2358 : 90000 // clock rate (match other video codecs)
2359 0 : );
2360 0 : mSupportedCodecs.values.push_back(red);
2361 :
2362 : JsepVideoCodecDescription* ulpfec = new JsepVideoCodecDescription(
2363 : "123", // payload type
2364 : "ulpfec", // codec name
2365 : 90000 // clock rate (match other video codecs)
2366 0 : );
2367 0 : mSupportedCodecs.values.push_back(ulpfec);
2368 :
2369 0 : mSupportedCodecs.values.push_back(new JsepApplicationCodecDescription(
2370 : "webrtc-datachannel",
2371 : WEBRTC_DATACHANNEL_STREAMS_DEFAULT,
2372 : WEBRTC_DATACHANNEL_PORT_DEFAULT,
2373 : // TODO: Bug 979417 needs to change this to
2374 : // WEBRTC_DATACHANELL_MAX_MESSAGE_SIZE_DEFAULT
2375 : 0
2376 0 : ));
2377 :
2378 : // Update the redundant encodings for the RED codec with the supported
2379 : // codecs. Note: only uses the video codecs.
2380 0 : red->UpdateRedundantEncodings(mSupportedCodecs.values);
2381 0 : }
2382 :
2383 : void
2384 0 : JsepSessionImpl::SetupDefaultRtpExtensions()
2385 : {
2386 0 : AddAudioRtpExtension("urn:ietf:params:rtp-hdrext:ssrc-audio-level",
2387 0 : SdpDirectionAttribute::Direction::kSendonly);
2388 0 : AddVideoRtpExtension(
2389 : "http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
2390 0 : SdpDirectionAttribute::Direction::kSendrecv);
2391 0 : AddVideoRtpExtension("urn:ietf:params:rtp-hdrext:toffset",
2392 0 : SdpDirectionAttribute::Direction::kSendrecv);
2393 0 : }
2394 :
2395 : void
2396 0 : JsepSessionImpl::SetState(JsepSignalingState state)
2397 : {
2398 0 : if (state == mState)
2399 0 : return;
2400 :
2401 0 : MOZ_MTLOG(ML_NOTICE, "[" << mName << "]: " <<
2402 : GetStateStr(mState) << " -> " << GetStateStr(state));
2403 0 : mState = state;
2404 : }
2405 :
2406 : nsresult
2407 0 : JsepSessionImpl::AddRemoteIceCandidate(const std::string& candidate,
2408 : const std::string& mid,
2409 : uint16_t level)
2410 : {
2411 0 : mLastError.clear();
2412 :
2413 0 : mozilla::Sdp* sdp = GetParsedRemoteDescription(kJsepDescriptionPendingOrCurrent);
2414 :
2415 0 : if (!sdp) {
2416 0 : JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState));
2417 0 : return NS_ERROR_UNEXPECTED;
2418 : }
2419 :
2420 0 : return mSdpHelper.AddCandidateToSdp(sdp, candidate, mid, level);
2421 : }
2422 :
2423 : nsresult
2424 0 : JsepSessionImpl::AddLocalIceCandidate(const std::string& candidate,
2425 : uint16_t level,
2426 : std::string* mid,
2427 : bool* skipped)
2428 : {
2429 0 : mLastError.clear();
2430 :
2431 0 : mozilla::Sdp* sdp = GetParsedLocalDescription(kJsepDescriptionPendingOrCurrent);
2432 :
2433 0 : if (!sdp) {
2434 0 : JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState));
2435 0 : return NS_ERROR_UNEXPECTED;
2436 : }
2437 :
2438 0 : if (sdp->GetMediaSectionCount() <= level) {
2439 : // mainly here to make some testing less complicated, but also just in case
2440 0 : *skipped = true;
2441 0 : return NS_OK;
2442 : }
2443 :
2444 0 : if (mState == kJsepStateStable) {
2445 0 : const Sdp* answer(GetAnswer());
2446 0 : if (mSdpHelper.IsBundleSlave(*answer, level)) {
2447 : // We do not add candidate attributes to bundled m-sections unless they
2448 : // are the "master" bundle m-section.
2449 0 : *skipped = true;
2450 0 : return NS_OK;
2451 : }
2452 : }
2453 :
2454 0 : nsresult rv = mSdpHelper.GetMidFromLevel(*sdp, level, mid);
2455 0 : if (NS_FAILED(rv)) {
2456 0 : return rv;
2457 : }
2458 :
2459 0 : *skipped = false;
2460 :
2461 0 : return mSdpHelper.AddCandidateToSdp(sdp, candidate, *mid, level);
2462 : }
2463 :
2464 : nsresult
2465 0 : JsepSessionImpl::UpdateDefaultCandidate(
2466 : const std::string& defaultCandidateAddr,
2467 : uint16_t defaultCandidatePort,
2468 : const std::string& defaultRtcpCandidateAddr,
2469 : uint16_t defaultRtcpCandidatePort,
2470 : uint16_t level)
2471 : {
2472 0 : mLastError.clear();
2473 :
2474 0 : mozilla::Sdp* sdp = GetParsedLocalDescription(kJsepDescriptionPendingOrCurrent);
2475 :
2476 0 : if (!sdp) {
2477 0 : JSEP_SET_ERROR("Cannot add ICE candidate in state " << GetStateStr(mState));
2478 0 : return NS_ERROR_UNEXPECTED;
2479 : }
2480 :
2481 0 : if (level >= sdp->GetMediaSectionCount()) {
2482 0 : return NS_OK;
2483 : }
2484 :
2485 0 : std::string defaultRtcpCandidateAddrCopy(defaultRtcpCandidateAddr);
2486 0 : if (mState == kJsepStateStable && mTransports[level]->mComponents == 1) {
2487 : // We know we're doing rtcp-mux by now. Don't create an rtcp attr.
2488 0 : defaultRtcpCandidateAddrCopy = "";
2489 0 : defaultRtcpCandidatePort = 0;
2490 : }
2491 :
2492 : // If offer/answer isn't done, it is too early to tell whether these defaults
2493 : // need to be applied to other m-sections.
2494 0 : SdpHelper::BundledMids bundledMids;
2495 0 : if (mState == kJsepStateStable) {
2496 0 : nsresult rv = GetNegotiatedBundledMids(&bundledMids);
2497 0 : if (NS_FAILED(rv)) {
2498 0 : MOZ_ASSERT(false);
2499 : mLastError += " (This should have been caught sooner!)";
2500 : return NS_ERROR_FAILURE;
2501 : }
2502 : }
2503 :
2504 0 : mSdpHelper.SetDefaultAddresses(
2505 : defaultCandidateAddr,
2506 : defaultCandidatePort,
2507 : defaultRtcpCandidateAddrCopy,
2508 : defaultRtcpCandidatePort,
2509 : sdp,
2510 : level,
2511 0 : bundledMids);
2512 :
2513 0 : return NS_OK;
2514 : }
2515 :
2516 : nsresult
2517 0 : JsepSessionImpl::EndOfLocalCandidates(uint16_t level)
2518 : {
2519 0 : mLastError.clear();
2520 :
2521 0 : mozilla::Sdp* sdp = GetParsedLocalDescription(kJsepDescriptionPendingOrCurrent);
2522 :
2523 0 : if (!sdp) {
2524 0 : JSEP_SET_ERROR("Cannot mark end of local ICE candidates in state "
2525 : << GetStateStr(mState));
2526 0 : return NS_ERROR_UNEXPECTED;
2527 : }
2528 :
2529 0 : if (level >= sdp->GetMediaSectionCount()) {
2530 0 : return NS_OK;
2531 : }
2532 :
2533 : // If offer/answer isn't done, it is too early to tell whether this update
2534 : // needs to be applied to other m-sections.
2535 0 : SdpHelper::BundledMids bundledMids;
2536 0 : if (mState == kJsepStateStable) {
2537 0 : nsresult rv = GetNegotiatedBundledMids(&bundledMids);
2538 0 : if (NS_FAILED(rv)) {
2539 0 : MOZ_ASSERT(false);
2540 : mLastError += " (This should have been caught sooner!)";
2541 : return NS_ERROR_FAILURE;
2542 : }
2543 : }
2544 :
2545 0 : mSdpHelper.SetIceGatheringComplete(sdp,
2546 : level,
2547 0 : bundledMids);
2548 :
2549 0 : return NS_OK;
2550 : }
2551 :
2552 : nsresult
2553 0 : JsepSessionImpl::GetNegotiatedBundledMids(SdpHelper::BundledMids* bundledMids)
2554 : {
2555 0 : const Sdp* answerSdp = GetAnswer();
2556 :
2557 0 : if (!answerSdp) {
2558 0 : return NS_OK;
2559 : }
2560 :
2561 0 : return mSdpHelper.GetBundledMids(*answerSdp, bundledMids);
2562 : }
2563 :
2564 : nsresult
2565 0 : JsepSessionImpl::EnableOfferMsection(SdpMediaSection* msection)
2566 : {
2567 : // We assert here because adding rtcp-mux to a non-disabled m-section that
2568 : // did not already have rtcp-mux can cause problems.
2569 0 : MOZ_ASSERT(mSdpHelper.MsectionIsDisabled(*msection));
2570 :
2571 0 : msection->SetPort(9);
2572 :
2573 : // We don't do this in AddTransportAttributes because that is also used for
2574 : // making answers, and we don't want to unconditionally set rtcp-mux there.
2575 0 : if (mSdpHelper.HasRtcp(msection->GetProtocol())) {
2576 : // Set RTCP-MUX.
2577 0 : msection->GetAttributeList().SetAttribute(
2578 0 : new SdpFlagAttribute(SdpAttribute::kRtcpMuxAttribute));
2579 : }
2580 :
2581 0 : nsresult rv = AddTransportAttributes(msection, SdpSetupAttribute::kActpass);
2582 0 : NS_ENSURE_SUCCESS(rv, rv);
2583 :
2584 0 : rv = SetRecvonlySsrc(msection);
2585 0 : NS_ENSURE_SUCCESS(rv, rv);
2586 :
2587 0 : AddExtmap(msection);
2588 :
2589 0 : std::ostringstream osMid;
2590 0 : osMid << "sdparta_" << msection->GetLevel();
2591 0 : AddMid(osMid.str(), msection);
2592 :
2593 0 : return NS_OK;
2594 : }
2595 :
2596 : mozilla::Sdp*
2597 0 : JsepSessionImpl::GetParsedLocalDescription(JsepDescriptionPendingOrCurrent type) const
2598 : {
2599 0 : if (type == kJsepDescriptionPending) {
2600 0 : return mPendingLocalDescription.get();
2601 0 : } else if (mPendingLocalDescription &&
2602 0 : type == kJsepDescriptionPendingOrCurrent) {
2603 0 : return mPendingLocalDescription.get();
2604 : }
2605 0 : return mCurrentLocalDescription.get();
2606 : }
2607 :
2608 : mozilla::Sdp*
2609 0 : JsepSessionImpl::GetParsedRemoteDescription(JsepDescriptionPendingOrCurrent type) const
2610 : {
2611 0 : if (type == kJsepDescriptionPending) {
2612 0 : return mPendingRemoteDescription.get();
2613 0 : } else if (mPendingRemoteDescription &&
2614 0 : type == kJsepDescriptionPendingOrCurrent) {
2615 0 : return mPendingRemoteDescription.get();
2616 : }
2617 0 : return mCurrentRemoteDescription.get();
2618 : }
2619 :
2620 : const Sdp*
2621 0 : JsepSessionImpl::GetAnswer() const
2622 : {
2623 0 : return mWasOffererLastTime ? mCurrentRemoteDescription.get()
2624 0 : : mCurrentLocalDescription.get();
2625 : }
2626 :
2627 : nsresult
2628 0 : JsepSessionImpl::Close()
2629 : {
2630 0 : mLastError.clear();
2631 0 : SetState(kJsepStateClosed);
2632 0 : return NS_OK;
2633 : }
2634 :
2635 : const std::string
2636 0 : JsepSessionImpl::GetLastError() const
2637 : {
2638 0 : return mLastError;
2639 : }
2640 :
2641 : bool
2642 0 : JsepSessionImpl::AllLocalTracksAreAssigned() const
2643 : {
2644 0 : for (const auto& localTrack : mLocalTracks) {
2645 0 : if (!localTrack.mAssignedMLine.isSome()) {
2646 0 : return false;
2647 : }
2648 : }
2649 :
2650 0 : return true;
2651 : }
2652 :
2653 : } // namespace mozilla
|