LCOV - code coverage report
Current view: top level - media/webrtc/signaling/src/jsep - JsepSessionImpl.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1203 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 88 0.0 %
Legend: Lines: hit not hit

          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

Generated by: LCOV version 1.13