mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 21:31:04 +00:00
Bug 952145: Rollback support r=mt, r=smaug
--HG-- extra : rebase_source : c5615117716ca907d61b4b7c7eeff71e926821bd
This commit is contained in:
parent
9fce2db2f1
commit
1fec8cf0e8
@ -695,6 +695,9 @@ RTCPeerConnection.prototype = {
|
||||
case "pranswer":
|
||||
throw new this._win.DOMException("pranswer not yet implemented",
|
||||
"NotSupportedError");
|
||||
case "rollback":
|
||||
type = Ci.IPeerConnection.kActionRollback;
|
||||
break;
|
||||
default:
|
||||
throw new this._win.DOMException(
|
||||
"Invalid type " + desc.type + " provided to setLocalDescription",
|
||||
@ -768,6 +771,9 @@ RTCPeerConnection.prototype = {
|
||||
case "pranswer":
|
||||
throw new this._win.DOMException("pranswer not yet implemented",
|
||||
"NotSupportedError");
|
||||
case "rollback":
|
||||
type = Ci.IPeerConnection.kActionRollback;
|
||||
break;
|
||||
default:
|
||||
throw new this._win.DOMException(
|
||||
"Invalid type " + desc.type + " provided to setRemoteDescription",
|
||||
|
@ -28,7 +28,7 @@ interface IPeerConnectionObserver : nsISupports
|
||||
{
|
||||
};
|
||||
|
||||
[scriptable, uuid(c9c31639-1a49-4533-8429-f6a348c4d8c3)]
|
||||
[scriptable, uuid(14afc8e7-e421-4d0c-99a5-69308d871481)]
|
||||
interface IPeerConnection : nsISupports
|
||||
{
|
||||
const unsigned long kHintAudio = 0x00000001;
|
||||
@ -38,6 +38,7 @@ interface IPeerConnection : nsISupports
|
||||
const long kActionOffer = 0;
|
||||
const long kActionAnswer = 1;
|
||||
const long kActionPRAnswer = 2;
|
||||
const long kActionRollback = 3;
|
||||
|
||||
const long kIceGathering = 0;
|
||||
const long kIceWaiting = 1;
|
||||
|
@ -380,8 +380,8 @@ CommandChain.prototype = {
|
||||
/**
|
||||
* Inserts the new commands after the specified command.
|
||||
*/
|
||||
insertAfter: function(functionOrName, commands) {
|
||||
this._insertHelper(functionOrName, commands, 1);
|
||||
insertAfter: function(functionOrName, commands, all, start) {
|
||||
this._insertHelper(functionOrName, commands, 1, all, start);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -431,8 +431,8 @@ CommandChain.prototype = {
|
||||
/**
|
||||
* Removes all commands after the specified one, returns what was removed.
|
||||
*/
|
||||
removeAfter: function(functionOrName) {
|
||||
var index = this.indexOf(functionOrName);
|
||||
removeAfter: function(functionOrName, start) {
|
||||
var index = this.indexOf(functionOrName, start);
|
||||
if (index >= 0) {
|
||||
return this.commands.splice(index + 1);
|
||||
}
|
||||
@ -461,8 +461,8 @@ CommandChain.prototype = {
|
||||
/**
|
||||
* Replaces all commands after the specified one, returns what was removed.
|
||||
*/
|
||||
replaceAfter: function(functionOrName, commands) {
|
||||
var oldCommands = this.removeAfter(functionOrName);
|
||||
replaceAfter: function(functionOrName, commands, start) {
|
||||
var oldCommands = this.removeAfter(functionOrName, start);
|
||||
this.append(commands);
|
||||
return oldCommands;
|
||||
},
|
||||
|
@ -196,6 +196,14 @@ skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
|
||||
[test_peerConnection_webAudio.html]
|
||||
tags = webaudio webrtc
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
|
||||
[test_peerConnection_localRollback.html]
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
|
||||
[test_peerConnection_localReofferRollback.html]
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
|
||||
[test_peerConnection_remoteRollback.html]
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
|
||||
[test_peerConnection_remoteReofferRollback.html]
|
||||
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
|
||||
|
||||
# Bug 950317: Hack for making a cleanup hook after finishing all WebRTC cases
|
||||
[test_zmedia_cleanup.html]
|
||||
|
@ -774,7 +774,6 @@ function PeerConnectionWrapper(label, configuration, h264) {
|
||||
|
||||
this._local_ice_candidates = [];
|
||||
this._remote_ice_candidates = [];
|
||||
this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r);
|
||||
this.localRequiresTrickleIce = false;
|
||||
this.remoteRequiresTrickleIce = false;
|
||||
this.localMediaElements = [];
|
||||
@ -1085,7 +1084,12 @@ PeerConnectionWrapper.prototype = {
|
||||
this.observedNegotiationNeeded = undefined;
|
||||
return this._pc.setRemoteDescription(desc).then(() => {
|
||||
info(this + ": Successfully set remote description");
|
||||
this.releaseIceCandidates();
|
||||
if (desc.type == "rollback") {
|
||||
this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r);
|
||||
|
||||
} else {
|
||||
this.releaseIceCandidates();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@ -1318,6 +1322,7 @@ PeerConnectionWrapper.prototype = {
|
||||
|
||||
var resolveEndOfTrickle;
|
||||
this.endOfTrickleIce = new Promise(r => resolveEndOfTrickle = r);
|
||||
this.holdIceCandidates = new Promise(r => this.releaseIceCandidates = r);
|
||||
|
||||
this.endOfTrickleIce.then(() => {
|
||||
this._pc.onicecandidate = () =>
|
||||
|
@ -0,0 +1,57 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="pc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "952145",
|
||||
title: "Rollback local reoffer"
|
||||
});
|
||||
|
||||
var test;
|
||||
runNetworkTest(function (options) {
|
||||
test = new PeerConnectionTest(options);
|
||||
addRenegotiation(test.chain, [
|
||||
function PC_LOCAL_ADD_SECOND_STREAM(test) {
|
||||
test.setMediaConstraints([{audio: true}, {audio: true}],
|
||||
[{audio: true}]);
|
||||
return test.pcLocal.getAllUserMedia([{audio: true}]);
|
||||
},
|
||||
|
||||
function PC_REMOTE_SETUP_ICE_HANDLER(test) {
|
||||
test.pcRemote.setupIceCandidateHandler(test);
|
||||
if (test.steeplechase) {
|
||||
test.pcRemote.endOfTrickleIce.then(() => {
|
||||
send_message({"type": "end_of_trickle_ice"});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
function PC_REMOTE_CREATE_AND_SET_OFFER(test) {
|
||||
return test.createOffer(test.pcRemote).then(offer => {
|
||||
return test.setLocalDescription(test.pcRemote, offer, HAVE_LOCAL_OFFER);
|
||||
});
|
||||
},
|
||||
|
||||
function PC_REMOTE_ROLLBACK(test) {
|
||||
return test.setLocalDescription(
|
||||
test.pcRemote,
|
||||
new mozRTCSessionDescription({ type: "rollback", sdp: ""}),
|
||||
STABLE);
|
||||
},
|
||||
|
||||
// Rolling back should shut down gathering
|
||||
function PC_REMOTE_WAIT_FOR_END_OF_TRICKLE(test) {
|
||||
return test.pcRemote.endOfTrickleIce;
|
||||
},
|
||||
]);
|
||||
test.setMediaConstraints([{audio: true}], [{audio: true}]);
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,51 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="pc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "952145",
|
||||
title: "Rollback local offer"
|
||||
});
|
||||
|
||||
var test;
|
||||
runNetworkTest(function (options) {
|
||||
test = new PeerConnectionTest(options);
|
||||
test.setMediaConstraints([{audio: true}], [{audio: true}]);
|
||||
test.chain.insertBefore('PC_LOCAL_CREATE_OFFER', [
|
||||
function PC_REMOTE_CREATE_AND_SET_OFFER(test) {
|
||||
return test.createOffer(test.pcRemote).then(offer => {
|
||||
return test.setLocalDescription(test.pcRemote, offer, HAVE_LOCAL_OFFER);
|
||||
});
|
||||
},
|
||||
|
||||
function PC_REMOTE_ROLLBACK(test) {
|
||||
return test.setLocalDescription(
|
||||
test.pcRemote,
|
||||
new mozRTCSessionDescription({ type: "rollback", sdp: ""}),
|
||||
STABLE);
|
||||
},
|
||||
|
||||
// Rolling back should shut down gathering
|
||||
function PC_REMOTE_WAIT_FOR_END_OF_TRICKLE(test) {
|
||||
return test.pcRemote.endOfTrickleIce;
|
||||
},
|
||||
|
||||
function PC_REMOTE_SETUP_ICE_HANDLER(test) {
|
||||
test.pcRemote.setupIceCandidateHandler(test);
|
||||
if (test.steeplechase) {
|
||||
test.pcRemote.endOfTrickleIce.then(() => {
|
||||
send_message({"type": "end_of_trickle_ice"});
|
||||
});
|
||||
}
|
||||
},
|
||||
]);
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,66 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="pc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "952145",
|
||||
title: "Rollback remote reoffer"
|
||||
});
|
||||
|
||||
var test;
|
||||
runNetworkTest(function (options) {
|
||||
test = new PeerConnectionTest(options);
|
||||
addRenegotiation(test.chain,
|
||||
[
|
||||
function PC_LOCAL_ADD_SECOND_STREAM(test) {
|
||||
test.setMediaConstraints([{audio: true}, {audio: true}],
|
||||
[{audio: true}]);
|
||||
return test.pcLocal.getAllUserMedia([{audio: true}]);
|
||||
},
|
||||
]
|
||||
);
|
||||
test.chain.replaceAfter('PC_REMOTE_SET_REMOTE_DESCRIPTION',
|
||||
[
|
||||
function PC_LOCAL_SETUP_ICE_HANDLER(test) {
|
||||
test.pcLocal.setupIceCandidateHandler(test);
|
||||
if (test.steeplechase) {
|
||||
test.pcLocal.endOfTrickleIce.then(() => {
|
||||
send_message({"type": "end_of_trickle_ice"});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
function PC_REMOTE_ROLLBACK(test) {
|
||||
return test.setRemoteDescription(
|
||||
test.pcRemote,
|
||||
new mozRTCSessionDescription({ type: "rollback" }),
|
||||
STABLE);
|
||||
},
|
||||
|
||||
function PC_LOCAL_ROLLBACK(test) {
|
||||
return test.setLocalDescription(
|
||||
test.pcLocal,
|
||||
new mozRTCSessionDescription({ type: "rollback", sdp: ""}),
|
||||
STABLE);
|
||||
},
|
||||
|
||||
// Rolling back should shut down gathering
|
||||
function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
|
||||
return test.pcLocal.endOfTrickleIce;
|
||||
},
|
||||
],
|
||||
1 // Second PC_REMOTE_SET_REMOTE_DESCRIPTION
|
||||
);
|
||||
test.chain.append(commandsPeerConnectionOfferAnswer);
|
||||
test.setMediaConstraints([{audio: true}], [{audio: true}]);
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -0,0 +1,46 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<script type="application/javascript" src="pc.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
createHTML({
|
||||
bug: "952145",
|
||||
title: "Rollback remote offer"
|
||||
});
|
||||
|
||||
var test;
|
||||
runNetworkTest(function (options) {
|
||||
test = new PeerConnectionTest(options);
|
||||
test.setMediaConstraints([{audio: true}], [{audio: true}]);
|
||||
test.chain.removeAfter('PC_REMOTE_SET_REMOTE_DESCRIPTION');
|
||||
test.chain.append([
|
||||
function PC_REMOTE_ROLLBACK(test) {
|
||||
return test.setRemoteDescription(
|
||||
test.pcRemote,
|
||||
new mozRTCSessionDescription({ type: "rollback" }),
|
||||
STABLE);
|
||||
},
|
||||
|
||||
function PC_LOCAL_ROLLBACK(test) {
|
||||
return test.setLocalDescription(
|
||||
test.pcLocal,
|
||||
new mozRTCSessionDescription({ type: "rollback", sdp: ""}),
|
||||
STABLE);
|
||||
},
|
||||
|
||||
// Rolling back should shut down gathering
|
||||
function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
|
||||
return test.pcLocal.endOfTrickleIce;
|
||||
},
|
||||
]);
|
||||
test.chain.append(commandsPeerConnectionOfferAnswer);
|
||||
test.run();
|
||||
});
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -10,12 +10,13 @@
|
||||
enum RTCSdpType {
|
||||
"offer",
|
||||
"pranswer",
|
||||
"answer"
|
||||
"answer",
|
||||
"rollback"
|
||||
};
|
||||
|
||||
dictionary RTCSessionDescriptionInit {
|
||||
RTCSdpType? type = null;
|
||||
DOMString? sdp = null;
|
||||
DOMString? sdp = "";
|
||||
};
|
||||
|
||||
[Pref="media.peerconnection.enabled",
|
||||
|
@ -36,6 +36,7 @@ enum JsepSdpType {
|
||||
kJsepSdpOffer,
|
||||
kJsepSdpAnswer,
|
||||
kJsepSdpPranswer,
|
||||
kJsepSdpRollback
|
||||
};
|
||||
|
||||
struct JsepOAOptions {};
|
||||
|
@ -119,7 +119,7 @@ JsepSessionImpl::AddTrack(const RefPtr<JsepTrack>& track)
|
||||
|
||||
JsepSendingTrack strack;
|
||||
strack.mTrack = track;
|
||||
strack.mSetInLocalDescription = false;
|
||||
strack.mNegotiated = false;
|
||||
|
||||
mLocalTracks.push_back(strack);
|
||||
|
||||
@ -400,7 +400,7 @@ JsepSessionImpl::BindTrackToMsection(
|
||||
}
|
||||
msection->SetSending(true);
|
||||
track->mAssignedMLine = Some(msection->GetLevel());
|
||||
track->mSetInLocalDescription = false;
|
||||
track->mNegotiated = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -533,7 +533,7 @@ JsepSessionImpl::SetupBundle(Sdp* sdp) const
|
||||
}
|
||||
|
||||
nsresult
|
||||
JsepSessionImpl::SetupTransportAttributes(Sdp* offer)
|
||||
JsepSessionImpl::FinalizeTransportAttributes(Sdp* offer)
|
||||
{
|
||||
const Sdp* oldAnswer = GetAnswer();
|
||||
|
||||
@ -754,11 +754,10 @@ JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// Get rid of all m-line assignments that have not been executed by a call
|
||||
// to SetLocalDescription.
|
||||
// Get rid of all m-line assignments that have not been negotiated
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
|
||||
if (!i->mSetInLocalDescription) {
|
||||
if (!i->mNegotiated) {
|
||||
i->mAssignedMLine.reset();
|
||||
}
|
||||
}
|
||||
@ -770,7 +769,7 @@ JsepSessionImpl::CreateOffer(const JsepOfferOptions& options,
|
||||
|
||||
SetupBundle(sdp.get());
|
||||
|
||||
rv = SetupTransportAttributes(sdp.get());
|
||||
rv = FinalizeTransportAttributes(sdp.get());
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
*offer = sdp->ToString();
|
||||
@ -1003,9 +1002,8 @@ JsepSessionImpl::CreateAnswer(const JsepAnswerOptions& options,
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get rid of all m-line assignments that have not been executed by a call
|
||||
// to SetLocalDescription.
|
||||
if (!i->mSetInLocalDescription) {
|
||||
// Get rid of all m-line assignments that have not been negotiated
|
||||
if (!i->mNegotiated) {
|
||||
i->mAssignedMLine.reset();
|
||||
continue;
|
||||
}
|
||||
@ -1323,6 +1321,21 @@ JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
|
||||
|
||||
MOZ_MTLOG(ML_DEBUG, "SetLocalDescription type=" << type << "\nSDP=\n"
|
||||
<< sdp);
|
||||
|
||||
if (type == kJsepSdpRollback) {
|
||||
if (mState != kJsepStateHaveLocalOffer) {
|
||||
JSEP_SET_ERROR("Cannot rollback local description in "
|
||||
<< GetStateStr(mState));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mPendingLocalDescription.reset();
|
||||
SetState(kJsepStateStable);
|
||||
mTransports = mOldTransports;
|
||||
mOldTransports.clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
switch (mState) {
|
||||
case kJsepStateStable:
|
||||
if (type != kJsepSdpOffer) {
|
||||
@ -1354,18 +1367,13 @@ JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Create transport objects.
|
||||
size_t numMsections = parsed->GetMediaSectionCount();
|
||||
for (size_t t = 0; t < numMsections; ++t) {
|
||||
if (t < mTransports.size()) {
|
||||
mTransports[t]->mState = JsepTransport::kJsepTransportOffered;
|
||||
continue; // This transport already exists (assume we are renegotiating).
|
||||
mOldTransports = mTransports;
|
||||
for (size_t t = 0; t < parsed->GetMediaSectionCount(); ++t) {
|
||||
if (t >= mTransports.size()) {
|
||||
mTransports.push_back(RefPtr<JsepTransport>(new JsepTransport));
|
||||
}
|
||||
|
||||
RefPtr<JsepTransport> transport;
|
||||
nsresult rv = CreateTransport(parsed->GetMediaSection(t), &transport);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mTransports.push_back(transport);
|
||||
UpdateTransport(parsed->GetMediaSection(t), mTransports[t].get());
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
@ -1376,15 +1384,8 @@ JsepSessionImpl::SetLocalDescription(JsepSdpType type, const std::string& sdp)
|
||||
case kJsepSdpPranswer:
|
||||
rv = SetLocalDescriptionAnswer(type, Move(parsed));
|
||||
break;
|
||||
}
|
||||
|
||||
// Mark all assigned local tracks as added to the local description
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
|
||||
if (i->mAssignedMLine.isSome()) {
|
||||
i->mSetInLocalDescription = true;
|
||||
}
|
||||
}
|
||||
case kJsepSdpRollback:
|
||||
MOZ_CRASH(); // Handled above
|
||||
}
|
||||
|
||||
return rv;
|
||||
@ -1430,6 +1431,21 @@ JsepSessionImpl::SetRemoteDescription(JsepSdpType type, const std::string& sdp)
|
||||
|
||||
MOZ_MTLOG(ML_DEBUG, "SetRemoteDescription type=" << type << "\nSDP=\n"
|
||||
<< sdp);
|
||||
|
||||
if (type == kJsepSdpRollback) {
|
||||
if (mState != kJsepStateHaveRemoteOffer) {
|
||||
JSEP_SET_ERROR("Cannot rollback remote description in "
|
||||
<< GetStateStr(mState));
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
mPendingRemoteDescription.reset();
|
||||
SetState(kJsepStateStable);
|
||||
|
||||
// Update the remote tracks to what they were before the SetRemote
|
||||
return SetRemoteTracksFromDescription(mCurrentRemoteDescription.get());
|
||||
}
|
||||
|
||||
switch (mState) {
|
||||
case kJsepStateStable:
|
||||
if (type != kJsepSdpOffer) {
|
||||
@ -1478,6 +1494,8 @@ JsepSessionImpl::SetRemoteDescription(JsepSdpType type, const std::string& sdp)
|
||||
case kJsepSdpPranswer:
|
||||
rv = SetRemoteDescriptionAnswer(type, Move(parsed));
|
||||
break;
|
||||
case kJsepSdpRollback:
|
||||
MOZ_CRASH(); // Handled above
|
||||
}
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
@ -1507,11 +1525,18 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
|
||||
|
||||
std::vector<JsepTrackPair> trackPairs;
|
||||
|
||||
if (mTransports.size() < local->GetMediaSectionCount()) {
|
||||
JSEP_SET_ERROR("Fewer transports set up than m-lines");
|
||||
MOZ_ASSERT(false);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Now walk through the m-sections, make sure they match, and create
|
||||
// track pairs that describe the media to be set up.
|
||||
for (size_t i = 0; i < local->GetMediaSectionCount(); ++i) {
|
||||
// Skip disabled m-sections.
|
||||
if (answer.GetMediaSection(i).GetPort() == 0) {
|
||||
mTransports[i]->Close();
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1526,20 +1551,16 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
|
||||
if (bundleMids.count(answerMsection.GetAttributeList().GetMid())) {
|
||||
transportLevel = bundleMsection->GetLevel();
|
||||
usingBundle = true;
|
||||
if (i != transportLevel) {
|
||||
mTransports[i]->Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Transports are created in SetLocal.
|
||||
if (mTransports.size() < transportLevel) {
|
||||
JSEP_SET_ERROR("Fewer transports set up than m-lines");
|
||||
MOZ_ASSERT(false);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<JsepTransport> transport = mTransports[transportLevel];
|
||||
|
||||
rv = SetupTransport(
|
||||
rv = FinalizeTransport(
|
||||
remote->GetMediaSection(transportLevel).GetAttributeList(),
|
||||
answer.GetMediaSection(transportLevel).GetAttributeList(),
|
||||
transport);
|
||||
@ -1571,15 +1592,16 @@ JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
|
||||
// stuff for renegotiation.
|
||||
mNegotiatedTrackPairs = trackPairs;
|
||||
|
||||
// Mark transports that weren't accepted as closed
|
||||
for (auto i = mTransports.begin(); i != mTransports.end(); ++i) {
|
||||
if ((*i)->mState != JsepTransport::kJsepTransportAccepted) {
|
||||
(*i)->mState = JsepTransport::kJsepTransportClosed;
|
||||
// Mark all assigned local tracks as negotiated
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
for (auto i = mLocalTracks.begin(); i != mLocalTracks.end(); ++i) {
|
||||
if (i->mAssignedMLine.isSome()) {
|
||||
i->mNegotiated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mGeneratedLocalDescription.reset();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1591,6 +1613,7 @@ JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
|
||||
size_t transportLevel,
|
||||
JsepTrackPair* trackPairOut)
|
||||
{
|
||||
MOZ_ASSERT(transport->mComponents);
|
||||
const SdpMediaSection& answer = mIsOfferer ? remote : local;
|
||||
|
||||
bool sending;
|
||||
@ -1674,20 +1697,11 @@ JsepSessionImpl::MakeNegotiatedTrackPair(const SdpMediaSection& remote,
|
||||
|
||||
trackPairOut->mRtpTransport = transport;
|
||||
|
||||
const SdpAttributeList& remoteAttrs(remote.GetAttributeList());
|
||||
const SdpAttributeList& localAttrs(local.GetAttributeList());
|
||||
|
||||
if (HasRtcp(local.GetProtocol())) {
|
||||
if (transport->mComponents == 2) {
|
||||
// RTCP MUX or not.
|
||||
// TODO(bug 1095743): verify that the PTs are consistent with mux.
|
||||
if (remoteAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute) &&
|
||||
localAttrs.HasAttribute(SdpAttribute::kRtcpMuxAttribute)) {
|
||||
trackPairOut->mRtcpTransport = nullptr; // We agree on mux.
|
||||
MOZ_MTLOG(ML_DEBUG, "RTCP-MUX is on");
|
||||
} else {
|
||||
MOZ_MTLOG(ML_DEBUG, "RTCP-MUX is off");
|
||||
trackPairOut->mRtcpTransport = transport;
|
||||
}
|
||||
MOZ_MTLOG(ML_DEBUG, "RTCP-MUX is off");
|
||||
trackPairOut->mRtcpTransport = transport;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -1765,36 +1779,34 @@ JsepSessionImpl::NegotiateTrack(const SdpMediaSection& remoteMsection,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
JsepSessionImpl::CreateTransport(const SdpMediaSection& msection,
|
||||
RefPtr<JsepTransport>* transport)
|
||||
void
|
||||
JsepSessionImpl::UpdateTransport(const SdpMediaSection& msection,
|
||||
JsepTransport* transport)
|
||||
{
|
||||
size_t components;
|
||||
|
||||
if (HasRtcp(msection.GetProtocol())) {
|
||||
components = 2;
|
||||
} else {
|
||||
components = 1;
|
||||
if (MsectionIsDisabled(msection)) {
|
||||
transport->Close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasRtcp(msection.GetProtocol())) {
|
||||
transport->mComponents = 2;
|
||||
} else {
|
||||
transport->mComponents = 1;
|
||||
}
|
||||
|
||||
std::string id;
|
||||
if (msection.GetAttributeList().HasAttribute(SdpAttribute::kMidAttribute)) {
|
||||
id = msection.GetAttributeList().GetMid();
|
||||
transport->mTransportId = msection.GetAttributeList().GetMid();
|
||||
} else {
|
||||
std::ostringstream os;
|
||||
os << "level_" << msection.GetLevel() << "(no mid)";
|
||||
id = os.str();
|
||||
transport->mTransportId = os.str();
|
||||
}
|
||||
|
||||
*transport = new JsepTransport(id, components);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
JsepSessionImpl::SetupTransport(const SdpAttributeList& remote,
|
||||
const SdpAttributeList& answer,
|
||||
const RefPtr<JsepTransport>& transport)
|
||||
JsepSessionImpl::FinalizeTransport(const SdpAttributeList& remote,
|
||||
const SdpAttributeList& answer,
|
||||
const RefPtr<JsepTransport>& transport)
|
||||
{
|
||||
UniquePtr<JsepIceTransport> ice = MakeUnique<JsepIceTransport>();
|
||||
|
||||
@ -1842,8 +1854,6 @@ JsepSessionImpl::SetupTransport(const SdpAttributeList& remote,
|
||||
transport->mComponents = 1;
|
||||
}
|
||||
|
||||
transport->mState = JsepTransport::kJsepTransportAccepted;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2024,7 +2034,7 @@ JsepSessionImpl::SetRemoteDescriptionOffer(UniquePtr<Sdp> offer)
|
||||
|
||||
// TODO(bug 1095780): Note that we create remote tracks even when
|
||||
// They contain only codecs we can't negotiate or other craziness.
|
||||
nsresult rv = SetRemoteTracksFromDescription(*offer);
|
||||
nsresult rv = SetRemoteTracksFromDescription(offer.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mPendingRemoteDescription = Move(offer);
|
||||
@ -2048,7 +2058,7 @@ JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type,
|
||||
|
||||
// TODO(bug 1095780): Note that this creates remote tracks even if
|
||||
// we offered sendonly and other side offered sendrecv or recvonly.
|
||||
rv = SetRemoteTracksFromDescription(*mPendingRemoteDescription);
|
||||
rv = SetRemoteTracksFromDescription(mPendingRemoteDescription.get());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = HandleNegotiatedSession(mPendingLocalDescription,
|
||||
@ -2063,51 +2073,54 @@ JsepSessionImpl::SetRemoteDescriptionAnswer(JsepSdpType type,
|
||||
}
|
||||
|
||||
nsresult
|
||||
JsepSessionImpl::SetRemoteTracksFromDescription(const Sdp& remoteDescription)
|
||||
JsepSessionImpl::SetRemoteTracksFromDescription(const Sdp* remoteDescription)
|
||||
{
|
||||
// Unassign all remote tracks
|
||||
for (auto i = mRemoteTracks.begin(); i != mRemoteTracks.end(); ++i) {
|
||||
i->mAssignedMLine.reset();
|
||||
}
|
||||
|
||||
size_t numMlines = remoteDescription.GetMediaSectionCount();
|
||||
nsresult rv;
|
||||
// This will not exist if we're rolling back the first remote description
|
||||
if (remoteDescription) {
|
||||
size_t numMlines = remoteDescription->GetMediaSectionCount();
|
||||
nsresult rv;
|
||||
|
||||
// Iterate over the sdp, re-assigning or creating remote tracks as we go
|
||||
for (size_t i = 0; i < numMlines; ++i) {
|
||||
const SdpMediaSection& msection = remoteDescription.GetMediaSection(i);
|
||||
// Iterate over the sdp, re-assigning or creating remote tracks as we go
|
||||
for (size_t i = 0; i < numMlines; ++i) {
|
||||
const SdpMediaSection& msection = remoteDescription->GetMediaSection(i);
|
||||
|
||||
if (MsectionIsDisabled(msection) || !msection.IsSending()) {
|
||||
continue;
|
||||
}
|
||||
if (MsectionIsDisabled(msection) || !msection.IsSending()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<JsepReceivingTrack>::iterator track;
|
||||
std::vector<JsepReceivingTrack>::iterator track;
|
||||
|
||||
if (msection.GetMediaType() == SdpMediaSection::kApplication) {
|
||||
// Datachannel doesn't have msid, just search by type
|
||||
track = FindUnassignedTrackByType(mRemoteTracks,
|
||||
msection.GetMediaType());
|
||||
} else {
|
||||
std::string streamId;
|
||||
std::string trackId;
|
||||
rv = GetRemoteIds(remoteDescription, msection, &streamId, &trackId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (msection.GetMediaType() == SdpMediaSection::kApplication) {
|
||||
// Datachannel doesn't have msid, just search by type
|
||||
track = FindUnassignedTrackByType(mRemoteTracks,
|
||||
msection.GetMediaType());
|
||||
} else {
|
||||
std::string streamId;
|
||||
std::string trackId;
|
||||
rv = GetRemoteIds(*remoteDescription, msection, &streamId, &trackId);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
track = FindTrackByIds(mRemoteTracks, streamId, trackId);
|
||||
}
|
||||
track = FindTrackByIds(mRemoteTracks, streamId, trackId);
|
||||
}
|
||||
|
||||
if (track == mRemoteTracks.end()) {
|
||||
RefPtr<JsepTrack> track;
|
||||
rv = CreateReceivingTrack(i, remoteDescription, msection, &track);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (track == mRemoteTracks.end()) {
|
||||
RefPtr<JsepTrack> track;
|
||||
rv = CreateReceivingTrack(i, *remoteDescription, msection, &track);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
JsepReceivingTrack rtrack;
|
||||
rtrack.mTrack = track;
|
||||
rtrack.mAssignedMLine = Some(i);
|
||||
mRemoteTracks.push_back(rtrack);
|
||||
mRemoteTracksAdded.push_back(rtrack);
|
||||
} else {
|
||||
track->mAssignedMLine = Some(i);
|
||||
JsepReceivingTrack rtrack;
|
||||
rtrack.mTrack = track;
|
||||
rtrack.mAssignedMLine = Some(i);
|
||||
mRemoteTracks.push_back(rtrack);
|
||||
mRemoteTracksAdded.push_back(rtrack);
|
||||
} else {
|
||||
track->mAssignedMLine = Some(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3049,5 +3062,4 @@ JsepSessionImpl::AllLocalTracksAreAssigned() const
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -164,7 +164,7 @@ private:
|
||||
struct JsepSendingTrack {
|
||||
RefPtr<JsepTrack> mTrack;
|
||||
Maybe<size_t> mAssignedMLine;
|
||||
bool mSetInLocalDescription;
|
||||
bool mNegotiated;
|
||||
};
|
||||
|
||||
struct JsepReceivingTrack {
|
||||
@ -204,7 +204,7 @@ private:
|
||||
nsresult ValidateLocalDescription(const Sdp& description);
|
||||
nsresult ValidateRemoteDescription(const Sdp& description);
|
||||
nsresult ValidateAnswer(const Sdp& offer, const Sdp& answer);
|
||||
nsresult SetRemoteTracksFromDescription(const Sdp& remoteDescription);
|
||||
nsresult SetRemoteTracksFromDescription(const Sdp* remoteDescription);
|
||||
// Non-const because we use our Uuid generator
|
||||
nsresult CreateReceivingTrack(size_t mline,
|
||||
const Sdp& sdp,
|
||||
@ -240,7 +240,7 @@ private:
|
||||
const Sdp& oldAnswer,
|
||||
Sdp* newSdp);
|
||||
void SetupBundle(Sdp* sdp) const;
|
||||
nsresult SetupTransportAttributes(Sdp* sdp);
|
||||
nsresult FinalizeTransportAttributes(Sdp* sdp);
|
||||
void SetupMsidSemantic(const std::vector<std::string>& msids, Sdp* sdp) const;
|
||||
nsresult GetIdsFromMsid(const Sdp& sdp,
|
||||
const SdpMediaSection& msection,
|
||||
@ -280,12 +280,12 @@ private:
|
||||
JsepTrack::Direction,
|
||||
RefPtr<JsepTrack>* track);
|
||||
|
||||
nsresult CreateTransport(const SdpMediaSection& msection,
|
||||
RefPtr<JsepTransport>* transport);
|
||||
void UpdateTransport(const SdpMediaSection& msection,
|
||||
JsepTransport* transport);
|
||||
|
||||
nsresult SetupTransport(const SdpAttributeList& remote,
|
||||
const SdpAttributeList& answer,
|
||||
const RefPtr<JsepTransport>& transport);
|
||||
nsresult FinalizeTransport(const SdpAttributeList& remote,
|
||||
const SdpAttributeList& answer,
|
||||
const RefPtr<JsepTransport>& transport);
|
||||
|
||||
nsresult AddCandidateToSdp(Sdp* sdp,
|
||||
const std::string& candidate,
|
||||
@ -325,6 +325,8 @@ private:
|
||||
std::vector<JsepReceivingTrack> mRemoteTracksAdded;
|
||||
std::vector<JsepReceivingTrack> mRemoteTracksRemoved;
|
||||
std::vector<RefPtr<JsepTransport> > mTransports;
|
||||
// So we can rollback
|
||||
std::vector<RefPtr<JsepTransport> > mOldTransports;
|
||||
std::vector<JsepTrackPair> mNegotiatedTrackPairs;
|
||||
|
||||
bool mIsOfferer;
|
||||
|
@ -82,23 +82,22 @@ private:
|
||||
class JsepTransport
|
||||
{
|
||||
public:
|
||||
JsepTransport(const std::string& id, size_t components)
|
||||
: mTransportId(id), mState(kJsepTransportOffered), mComponents(components)
|
||||
JsepTransport()
|
||||
: mComponents(0)
|
||||
{
|
||||
}
|
||||
|
||||
enum State {
|
||||
kJsepTransportOffered,
|
||||
kJsepTransportAccepted,
|
||||
kJsepTransportClosed
|
||||
};
|
||||
void Close()
|
||||
{
|
||||
mComponents = 0;
|
||||
mTransportId.clear();
|
||||
mIce.reset();
|
||||
mDtls.reset();
|
||||
}
|
||||
|
||||
// Unique identifier for this transport within this call. Group?
|
||||
std::string mTransportId;
|
||||
|
||||
// State.
|
||||
State mState;
|
||||
|
||||
// ICE stuff.
|
||||
UniquePtr<JsepIceTransport> mIce;
|
||||
UniquePtr<JsepDtlsTransport> mDtls;
|
||||
|
@ -1491,6 +1491,9 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
|
||||
case IPeerConnection::kActionPRAnswer:
|
||||
sdpType = mozilla::kJsepSdpPranswer;
|
||||
break;
|
||||
case IPeerConnection::kActionRollback:
|
||||
sdpType = mozilla::kJsepSdpRollback;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -1519,7 +1522,7 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
|
||||
pco->OnSetLocalDescriptionSuccess(rv);
|
||||
}
|
||||
|
||||
UpdateSignalingState();
|
||||
UpdateSignalingState(sdpType == mozilla::kJsepSdpRollback);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1587,11 +1590,14 @@ PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
|
||||
case IPeerConnection::kActionPRAnswer:
|
||||
sdpType = mozilla::kJsepSdpPranswer;
|
||||
break;
|
||||
case IPeerConnection::kActionRollback:
|
||||
sdpType = mozilla::kJsepSdpRollback;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
}
|
||||
|
||||
nsresult nrv = mJsepSession->SetRemoteDescription(sdpType,
|
||||
mRemoteRequestedSDP);
|
||||
if (NS_FAILED(nrv)) {
|
||||
@ -1725,7 +1731,7 @@ PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
|
||||
#endif
|
||||
}
|
||||
|
||||
UpdateSignalingState();
|
||||
UpdateSignalingState(sdpType == mozilla::kJsepSdpRollback);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -2409,7 +2415,8 @@ PeerConnectionImpl::destructorSafeDestroyNSSReference()
|
||||
#endif
|
||||
|
||||
void
|
||||
PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
|
||||
PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState,
|
||||
bool rollback)
|
||||
{
|
||||
PC_AUTO_ENTER_API_CALL_NO_CHECK();
|
||||
if (mSignalingState == aSignalingState ||
|
||||
@ -2417,33 +2424,35 @@ PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
|
||||
return;
|
||||
}
|
||||
|
||||
bool restartGathering =
|
||||
aSignalingState == PCImplSignalingState::SignalingHaveLocalOffer ||
|
||||
(aSignalingState == PCImplSignalingState::SignalingStable &&
|
||||
mSignalingState == PCImplSignalingState::SignalingHaveRemoteOffer);
|
||||
if (aSignalingState == PCImplSignalingState::SignalingHaveLocalOffer ||
|
||||
(aSignalingState == PCImplSignalingState::SignalingStable &&
|
||||
mSignalingState == PCImplSignalingState::SignalingHaveRemoteOffer &&
|
||||
!rollback)) {
|
||||
mMedia->EnsureTransports(*mJsepSession);
|
||||
}
|
||||
|
||||
mSignalingState = aSignalingState;
|
||||
|
||||
if (mSignalingState == PCImplSignalingState::SignalingHaveLocalOffer ||
|
||||
mSignalingState == PCImplSignalingState::SignalingStable) {
|
||||
mMedia->UpdateTransports(*mJsepSession, restartGathering);
|
||||
}
|
||||
|
||||
bool fireNegotiationNeeded = false;
|
||||
|
||||
if (mSignalingState == PCImplSignalingState::SignalingStable) {
|
||||
mMedia->UpdateMediaPipelines(*mJsepSession);
|
||||
InitializeDataChannel();
|
||||
mMedia->StartIceChecks(*mJsepSession);
|
||||
mShouldSuppressNegotiationNeeded = false;
|
||||
if (!mJsepSession->AllLocalTracksAreAssigned()) {
|
||||
CSFLogInfo(logTag, "Not all local tracks were assigned to an "
|
||||
"m-section, either because the offerer did not offer"
|
||||
" to receive enough tracks, or because tracks were "
|
||||
"added after CreateOffer/Answer, but before "
|
||||
"offer/answer completed. This requires "
|
||||
"renegotiation.");
|
||||
fireNegotiationNeeded = true;
|
||||
// If we're rolling back a local offer, we might need to remove some
|
||||
// transports, but nothing further needs to be done.
|
||||
mMedia->ActivateOrRemoveTransports(*mJsepSession);
|
||||
if (!rollback) {
|
||||
mMedia->UpdateMediaPipelines(*mJsepSession);
|
||||
InitializeDataChannel();
|
||||
mMedia->StartIceChecks(*mJsepSession);
|
||||
mShouldSuppressNegotiationNeeded = false;
|
||||
if (!mJsepSession->AllLocalTracksAreAssigned()) {
|
||||
CSFLogInfo(logTag, "Not all local tracks were assigned to an "
|
||||
"m-section, either because the offerer did not offer"
|
||||
" to receive enough tracks, or because tracks were "
|
||||
"added after CreateOffer/Answer, but before "
|
||||
"offer/answer completed. This requires "
|
||||
"renegotiation.");
|
||||
fireNegotiationNeeded = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mShouldSuppressNegotiationNeeded = true;
|
||||
@ -2466,7 +2475,7 @@ PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState)
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionImpl::UpdateSignalingState() {
|
||||
PeerConnectionImpl::UpdateSignalingState(bool rollback) {
|
||||
mozilla::JsepSignalingState state =
|
||||
mJsepSession->GetState();
|
||||
|
||||
@ -2495,7 +2504,7 @@ PeerConnectionImpl::UpdateSignalingState() {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
SetSignalingState_m(newState);
|
||||
SetSignalingState_m(newState, rollback);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -3287,6 +3296,10 @@ PeerConnectionImpl::IceStreamReady(NrIceMediaStream *aStream)
|
||||
//Telemetry for when calls start
|
||||
void
|
||||
PeerConnectionImpl::startCallTelem() {
|
||||
if (!mStartTime.IsNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Start time for calls
|
||||
mStartTime = TimeStamp::Now();
|
||||
|
||||
|
@ -572,10 +572,11 @@ public:
|
||||
const std::vector<std::string> &GetSdpParseErrors();
|
||||
|
||||
// Sets the RTC Signaling State
|
||||
void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState);
|
||||
void SetSignalingState_m(mozilla::dom::PCImplSignalingState aSignalingState,
|
||||
bool rollback = false);
|
||||
|
||||
// Updates the RTC signaling state based on the JsepSession state
|
||||
void UpdateSignalingState();
|
||||
void UpdateSignalingState(bool rollback = false);
|
||||
|
||||
bool IsClosed() const;
|
||||
// called when DTLS connects; we only need this once
|
||||
|
@ -348,10 +348,55 @@ nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_serv
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::UpdateTransports(const JsepSession& session,
|
||||
bool restartGathering) {
|
||||
PeerConnectionMedia::EnsureTransports(const JsepSession& aSession)
|
||||
{
|
||||
auto transports = aSession.GetTransports();
|
||||
for (size_t i = 0; i < transports.size(); ++i) {
|
||||
RefPtr<JsepTransport> transport = transports[i];
|
||||
RUN_ON_THREAD(
|
||||
GetSTSThread(),
|
||||
WrapRunnable(RefPtr<PeerConnectionMedia>(this),
|
||||
&PeerConnectionMedia::EnsureTransport_s,
|
||||
i,
|
||||
transport->mComponents),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
auto transports = session.GetTransports();
|
||||
GatherIfReady();
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::EnsureTransport_s(size_t aLevel, size_t aComponentCount)
|
||||
{
|
||||
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aLevel));
|
||||
if (!stream) {
|
||||
CSFLogDebug(logTag, "%s: Creating ICE media stream=%u components=%u",
|
||||
mParentHandle.c_str(),
|
||||
static_cast<unsigned>(aLevel),
|
||||
static_cast<unsigned>(aComponentCount));
|
||||
|
||||
std::ostringstream os;
|
||||
os << mParentName << " aLevel=" << aLevel;
|
||||
RefPtr<NrIceMediaStream> stream = mIceCtx->CreateStream(os.str().c_str(),
|
||||
aComponentCount);
|
||||
|
||||
if (!stream) {
|
||||
CSFLogError(logTag, "Failed to create ICE stream.");
|
||||
return;
|
||||
}
|
||||
|
||||
stream->SetLevel(aLevel);
|
||||
stream->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady_s);
|
||||
stream->SignalCandidate.connect(this,
|
||||
&PeerConnectionMedia::OnCandidateFound_s);
|
||||
mIceCtx->SetStream(aLevel, stream);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::ActivateOrRemoveTransports(const JsepSession& aSession)
|
||||
{
|
||||
auto transports = aSession.GetTransports();
|
||||
for (size_t i = 0; i < transports.size(); ++i) {
|
||||
RefPtr<JsepTransport> transport = transports[i];
|
||||
|
||||
@ -359,31 +404,93 @@ PeerConnectionMedia::UpdateTransports(const JsepSession& session,
|
||||
std::string pwd;
|
||||
std::vector<std::string> candidates;
|
||||
|
||||
bool hasAttrs = false;
|
||||
if (transport->mIce) {
|
||||
CSFLogDebug(logTag, "Transport %u is active",
|
||||
static_cast<unsigned>(i));
|
||||
hasAttrs = true;
|
||||
if (transport->mComponents) {
|
||||
MOZ_ASSERT(transport->mIce);
|
||||
CSFLogDebug(logTag, "Transport %u is active", static_cast<unsigned>(i));
|
||||
ufrag = transport->mIce->GetUfrag();
|
||||
pwd = transport->mIce->GetPassword();
|
||||
candidates = transport->mIce->GetCandidates();
|
||||
} else {
|
||||
CSFLogDebug(logTag, "Transport %u is disabled", static_cast<unsigned>(i));
|
||||
// Make sure the MediaPipelineFactory doesn't try to use these.
|
||||
RemoveTransportFlow(i, false);
|
||||
RemoveTransportFlow(i, true);
|
||||
}
|
||||
|
||||
// Update the transport.
|
||||
RUN_ON_THREAD(GetSTSThread(),
|
||||
WrapRunnable(RefPtr<PeerConnectionMedia>(this),
|
||||
&PeerConnectionMedia::UpdateIceMediaStream_s,
|
||||
i,
|
||||
transport->mComponents,
|
||||
hasAttrs,
|
||||
ufrag,
|
||||
pwd,
|
||||
candidates),
|
||||
NS_DISPATCH_NORMAL);
|
||||
RUN_ON_THREAD(
|
||||
GetSTSThread(),
|
||||
WrapRunnable(RefPtr<PeerConnectionMedia>(this),
|
||||
&PeerConnectionMedia::ActivateOrRemoveTransport_s,
|
||||
i,
|
||||
transport->mComponents,
|
||||
ufrag,
|
||||
pwd,
|
||||
candidates),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
if (restartGathering) {
|
||||
GatherIfReady();
|
||||
// We can have more streams than m-lines due to rollback.
|
||||
RUN_ON_THREAD(
|
||||
GetSTSThread(),
|
||||
WrapRunnable(RefPtr<PeerConnectionMedia>(this),
|
||||
&PeerConnectionMedia::RemoveTransportsAtOrAfter_s,
|
||||
transports.size()),
|
||||
NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::ActivateOrRemoveTransport_s(
|
||||
size_t aMLine,
|
||||
size_t aComponentCount,
|
||||
const std::string& aUfrag,
|
||||
const std::string& aPassword,
|
||||
const std::vector<std::string>& aCandidateList) {
|
||||
|
||||
if (!aComponentCount) {
|
||||
CSFLogDebug(logTag, "%s: Removing ICE media stream=%u",
|
||||
mParentHandle.c_str(),
|
||||
static_cast<unsigned>(aMLine));
|
||||
mIceCtx->SetStream(aMLine, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aMLine));
|
||||
if (!stream) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!stream->HasParsedAttributes()) {
|
||||
CSFLogDebug(logTag, "%s: Activating ICE media stream=%u components=%u",
|
||||
mParentHandle.c_str(),
|
||||
static_cast<unsigned>(aMLine),
|
||||
static_cast<unsigned>(aComponentCount));
|
||||
|
||||
std::vector<std::string> attrs;
|
||||
for (auto i = aCandidateList.begin(); i != aCandidateList.end(); ++i) {
|
||||
attrs.push_back("candidate:" + *i);
|
||||
}
|
||||
attrs.push_back("ice-ufrag:" + aUfrag);
|
||||
attrs.push_back("ice-pwd:" + aPassword);
|
||||
|
||||
nsresult rv = stream->ParseAttributes(attrs);
|
||||
if (NS_FAILED(rv)) {
|
||||
CSFLogError(logTag, "Couldn't parse ICE attributes, rv=%u",
|
||||
static_cast<unsigned>(rv));
|
||||
}
|
||||
|
||||
for (size_t c = aComponentCount; c < stream->components(); ++c) {
|
||||
// components are 1-indexed
|
||||
stream->DisableComponent(c + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::RemoveTransportsAtOrAfter_s(size_t aMLine)
|
||||
{
|
||||
for (size_t i = aMLine; i < mIceCtx->GetStreamCount(); ++i) {
|
||||
mIceCtx->SetStream(i, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -419,36 +526,16 @@ nsresult PeerConnectionMedia::UpdateMediaPipelines(
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::StartIceChecks(const JsepSession& session) {
|
||||
|
||||
std::vector<size_t> numComponentsByLevel;
|
||||
auto transports = session.GetTransports();
|
||||
for (size_t i = 0; i < transports.size(); ++i) {
|
||||
RefPtr<JsepTransport> transport = transports[i];
|
||||
if (transport->mState == JsepTransport::kJsepTransportClosed) {
|
||||
CSFLogDebug(logTag, "Transport %s is disabled",
|
||||
transport->mTransportId.c_str());
|
||||
numComponentsByLevel.push_back(0);
|
||||
// Make sure the MediaPipelineFactory doesn't try to use these.
|
||||
RemoveTransportFlow(i, false);
|
||||
RemoveTransportFlow(i, true);
|
||||
} else {
|
||||
CSFLogDebug(logTag, "Transport %s has %u components",
|
||||
transport->mTransportId.c_str(),
|
||||
static_cast<unsigned>(transport->mComponents));
|
||||
numComponentsByLevel.push_back(transport->mComponents);
|
||||
}
|
||||
}
|
||||
|
||||
PeerConnectionMedia::StartIceChecks(const JsepSession& aSession)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(
|
||||
WrapRunnable(
|
||||
RefPtr<PeerConnectionMedia>(this),
|
||||
&PeerConnectionMedia::StartIceChecks_s,
|
||||
session.IsIceControlling(),
|
||||
session.RemoteIsIceLite(),
|
||||
aSession.IsIceControlling(),
|
||||
aSession.RemoteIsIceLite(),
|
||||
// Copy, just in case API changes to return a ref
|
||||
std::vector<std::string>(session.GetIceOptions()),
|
||||
numComponentsByLevel));
|
||||
std::vector<std::string>(aSession.GetIceOptions())));
|
||||
|
||||
PerformOrEnqueueIceCtxOperation(runnable);
|
||||
}
|
||||
@ -457,8 +544,7 @@ void
|
||||
PeerConnectionMedia::StartIceChecks_s(
|
||||
bool aIsControlling,
|
||||
bool aIsIceLite,
|
||||
const std::vector<std::string>& aIceOptionsList,
|
||||
const std::vector<size_t>& aComponentCountByLevel) {
|
||||
const std::vector<std::string>& aIceOptionsList) {
|
||||
|
||||
CSFLogDebug(logTag, "Starting ICE Checking");
|
||||
|
||||
@ -483,24 +569,6 @@ PeerConnectionMedia::StartIceChecks_s(
|
||||
NrIceCtx::ICE_CONTROLLING :
|
||||
NrIceCtx::ICE_CONTROLLED);
|
||||
|
||||
for (size_t i = 0; i < aComponentCountByLevel.size(); ++i) {
|
||||
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(i));
|
||||
if (!stream) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!aComponentCountByLevel[i]) {
|
||||
// Inactive stream. Remove.
|
||||
mIceCtx->SetStream(i, nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t c = aComponentCountByLevel[i]; c < stream->components(); ++c) {
|
||||
// components are 1-indexed
|
||||
stream->DisableComponent(c + 1);
|
||||
}
|
||||
}
|
||||
|
||||
mIceCtx->StartChecks();
|
||||
}
|
||||
|
||||
@ -579,56 +647,19 @@ PeerConnectionMedia::EnsureIceGathering_s() {
|
||||
if (mProxyServer) {
|
||||
mIceCtx->SetProxyServer(*mProxyServer);
|
||||
}
|
||||
mIceCtx->StartGathering();
|
||||
}
|
||||
|
||||
void
|
||||
PeerConnectionMedia::UpdateIceMediaStream_s(size_t aMLine,
|
||||
size_t aComponentCount,
|
||||
bool aHasAttrs,
|
||||
const std::string& aUfrag,
|
||||
const std::string& aPassword,
|
||||
const std::vector<std::string>&
|
||||
aCandidateList) {
|
||||
RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aMLine));
|
||||
if (!stream) {
|
||||
CSFLogDebug(logTag, "%s: Creating ICE media stream=%u components=%u",
|
||||
mParentHandle.c_str(),
|
||||
static_cast<unsigned>(aMLine),
|
||||
static_cast<unsigned>(aComponentCount));
|
||||
|
||||
std::ostringstream os;
|
||||
os << mParentName << " level=" << aMLine;
|
||||
stream = mIceCtx->CreateStream(os.str().c_str(),
|
||||
aComponentCount);
|
||||
|
||||
if (!stream) {
|
||||
CSFLogError(logTag, "Failed to create ICE stream.");
|
||||
// Start gathering, but only if there are streams
|
||||
for (size_t i = 0; i < mIceCtx->GetStreamCount(); ++i) {
|
||||
if (mIceCtx->GetStream(i)) {
|
||||
mIceCtx->StartGathering();
|
||||
return;
|
||||
}
|
||||
|
||||
stream->SetLevel(aMLine);
|
||||
stream->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady_s);
|
||||
stream->SignalCandidate.connect(this,
|
||||
&PeerConnectionMedia::OnCandidateFound_s);
|
||||
|
||||
mIceCtx->SetStream(aMLine, stream);
|
||||
}
|
||||
|
||||
if (aHasAttrs && !stream->HasParsedAttributes()) {
|
||||
std::vector<std::string> attrs;
|
||||
for (auto i = aCandidateList.begin(); i != aCandidateList.end(); ++i) {
|
||||
attrs.push_back("candidate:" + *i);
|
||||
}
|
||||
attrs.push_back("ice-ufrag:" + aUfrag);
|
||||
attrs.push_back("ice-pwd:" + aPassword);
|
||||
|
||||
nsresult rv = stream->ParseAttributes(attrs);
|
||||
if (NS_FAILED(rv)) {
|
||||
CSFLogError(logTag, "Couldn't parse ICE attributes, rv=%u",
|
||||
static_cast<unsigned>(rv));
|
||||
}
|
||||
}
|
||||
// If there are no streams, we're probably in a situation where we've rolled
|
||||
// back while still waiting for our proxy configuration to come back. Make
|
||||
// sure content knows that the rollback has stuck wrt gathering.
|
||||
IceGatheringStateChange_s(mIceCtx.get(), NrIceCtx::ICE_CTX_GATHER_COMPLETE);
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -245,8 +245,12 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
|
||||
return mIceCtx->GetStreamCount();
|
||||
}
|
||||
|
||||
// Create and modify transports in response to negotiation events.
|
||||
void UpdateTransports(const JsepSession& session, bool restartGathering);
|
||||
// Ensure ICE transports exist that we might need when offer/answer concludes
|
||||
void EnsureTransports(const JsepSession& aSession);
|
||||
|
||||
// Activate or remove ICE transports at the conclusion of offer/answer,
|
||||
// or when rollback occurs.
|
||||
void ActivateOrRemoveTransports(const JsepSession& aSession);
|
||||
|
||||
// Start ICE checks.
|
||||
void StartIceChecks(const JsepSession& session);
|
||||
@ -421,19 +425,22 @@ class PeerConnectionMedia : public sigslot::has_slots<> {
|
||||
void SelfDestruct_m();
|
||||
|
||||
// Manage ICE transports.
|
||||
void UpdateIceMediaStream_s(size_t aMLine, size_t aComponentCount,
|
||||
bool aHasAttrs,
|
||||
const std::string& aUfrag,
|
||||
const std::string& aPassword,
|
||||
const std::vector<std::string>& aCandidateList);
|
||||
void EnsureTransport_s(size_t aLevel, size_t aComponentCount);
|
||||
void ActivateOrRemoveTransport_s(
|
||||
size_t aMLine,
|
||||
size_t aComponentCount,
|
||||
const std::string& aUfrag,
|
||||
const std::string& aPassword,
|
||||
const std::vector<std::string>& aCandidateList);
|
||||
void RemoveTransportsAtOrAfter_s(size_t aMLine);
|
||||
|
||||
void GatherIfReady();
|
||||
void FlushIceCtxOperationQueueIfReady();
|
||||
void PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable);
|
||||
void EnsureIceGathering_s();
|
||||
void StartIceChecks_s(bool aIsControlling,
|
||||
bool aIsIceLite,
|
||||
const std::vector<std::string>& aIceOptionsList,
|
||||
const std::vector<size_t>& aComponentCountByLevel);
|
||||
const std::vector<std::string>& aIceOptionsList);
|
||||
|
||||
// Process a trickle ICE candidate.
|
||||
void AddIceCandidate_s(const std::string& aCandidate, const std::string& aMid,
|
||||
|
@ -1932,10 +1932,8 @@ TEST_P(JsepSessionTest, RenegotiationOffererDisablesBundleTransport)
|
||||
ASSERT_LE(1U, mSessionOff.GetTransports().size());
|
||||
ASSERT_LE(1U, mSessionAns.GetTransports().size());
|
||||
|
||||
ASSERT_EQ(JsepTransport::kJsepTransportClosed,
|
||||
mSessionOff.GetTransports()[0]->mState);
|
||||
ASSERT_EQ(JsepTransport::kJsepTransportClosed,
|
||||
mSessionAns.GetTransports()[0]->mState);
|
||||
ASSERT_EQ(0U, mSessionOff.GetTransports()[0]->mComponents);
|
||||
ASSERT_EQ(0U, mSessionAns.GetTransports()[0]->mComponents);
|
||||
}
|
||||
|
||||
TEST_P(JsepSessionTest, RenegotiationAnswererDisablesBundleTransport)
|
||||
@ -3209,6 +3207,98 @@ TEST_F(JsepSessionTest, LowDynamicPayloadType)
|
||||
ASSERT_EQ("12", codec->mDefaultPt);
|
||||
}
|
||||
|
||||
TEST_P(JsepSessionTest, TestGlareRollback)
|
||||
{
|
||||
AddTracks(mSessionOff);
|
||||
AddTracks(mSessionAns);
|
||||
JsepOfferOptions options;
|
||||
|
||||
std::string offer;
|
||||
ASSERT_EQ(NS_OK, mSessionAns.CreateOffer(options, &offer));
|
||||
ASSERT_EQ(NS_OK,
|
||||
mSessionAns.SetLocalDescription(kJsepSdpOffer, offer));
|
||||
ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionAns.GetState());
|
||||
|
||||
ASSERT_EQ(NS_OK, mSessionOff.CreateOffer(options, &offer));
|
||||
ASSERT_EQ(NS_OK,
|
||||
mSessionOff.SetLocalDescription(kJsepSdpOffer, offer));
|
||||
ASSERT_EQ(kJsepStateHaveLocalOffer, mSessionOff.GetState());
|
||||
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionAns.SetRemoteDescription(kJsepSdpOffer, offer));
|
||||
ASSERT_EQ(NS_OK,
|
||||
mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
|
||||
ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
|
||||
|
||||
SetRemoteOffer(offer);
|
||||
|
||||
std::string answer = CreateAnswer();
|
||||
SetLocalAnswer(answer);
|
||||
SetRemoteAnswer(answer);
|
||||
}
|
||||
|
||||
TEST_P(JsepSessionTest, TestRejectOfferRollback)
|
||||
{
|
||||
AddTracks(mSessionOff);
|
||||
AddTracks(mSessionAns);
|
||||
|
||||
std::string offer = CreateOffer();
|
||||
SetLocalOffer(offer);
|
||||
SetRemoteOffer(offer);
|
||||
|
||||
ASSERT_EQ(NS_OK,
|
||||
mSessionAns.SetRemoteDescription(kJsepSdpRollback, ""));
|
||||
ASSERT_EQ(kJsepStateStable, mSessionAns.GetState());
|
||||
ASSERT_EQ(types.size(), mSessionAns.GetRemoteTracksRemoved().size());
|
||||
|
||||
ASSERT_EQ(NS_OK,
|
||||
mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
|
||||
ASSERT_EQ(kJsepStateStable, mSessionOff.GetState());
|
||||
|
||||
OfferAnswer();
|
||||
}
|
||||
|
||||
TEST_P(JsepSessionTest, TestInvalidRollback)
|
||||
{
|
||||
AddTracks(mSessionOff);
|
||||
AddTracks(mSessionAns);
|
||||
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
|
||||
|
||||
std::string offer = CreateOffer();
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
|
||||
|
||||
SetLocalOffer(offer);
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
|
||||
|
||||
SetRemoteOffer(offer);
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
|
||||
|
||||
std::string answer = CreateAnswer();
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
|
||||
|
||||
SetLocalAnswer(answer);
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionAns.SetLocalDescription(kJsepSdpRollback, ""));
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionAns.SetRemoteDescription(kJsepSdpRollback, ""));
|
||||
|
||||
SetRemoteAnswer(answer);
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionOff.SetLocalDescription(kJsepSdpRollback, ""));
|
||||
ASSERT_EQ(NS_ERROR_UNEXPECTED,
|
||||
mSessionOff.SetRemoteDescription(kJsepSdpRollback, ""));
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
int
|
||||
|
Loading…
Reference in New Issue
Block a user