--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -137,16 +137,28 @@ skip-if = toolkit == 'gonk' # B2G emulat
skip-if = toolkit == 'gonk' # B2G emulator is too slow to handle a two-way audio call reliably
[test_peerConnection_offerRequiresReceiveAudio.html]
[test_peerConnection_offerRequiresReceiveVideo.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
[test_peerConnection_offerRequiresReceiveVideoAudio.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_promiseSendOnly.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
+[test_peerConnection_restartIce.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceNoBundle.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceNoBundleNoRtcpMux.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceNoRtcpMux.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceLocalRollback.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
+[test_peerConnection_restartIceLocalAndRemoteRollback.html]
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g (Bug 1059867)
[test_peerConnection_scaleResolution.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18') # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
[test_peerConnection_simulcastOffer.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || android_version # b2g(Bug 960442, video support for WebRTC is disabled on b2g), no simulcast support on android
#[test_peerConnection_relayOnly.html]
#skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
[test_peerConnection_callbacks.html]
skip-if = toolkit == 'gonk' || buildapp == 'mulet' || (android_version == '18' && debug) # b2g(Bug 960442, video support for WebRTC is disabled on b2g), android(Bug 1189784, timeouts on 4.3 emulator)
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -724,16 +724,17 @@ function PeerConnectionWrapper(label, co
this.expectedLocalTrackInfoById = {};
this.expectedRemoteTrackInfoById = {};
this.observedRemoteTrackInfoById = {};
this.disableRtpCountChecking = false;
this.iceCheckingRestartExpected = false;
+ this.iceCheckingIceRollbackExpected = false;
info("Creating " + this);
this._pc = new RTCPeerConnection(this.configuration);
/**
* Setup callback handlers
*/
// This allows test to register their own callbacks for ICE connection state changes
@@ -1209,16 +1210,21 @@ PeerConnectionWrapper.prototype = {
var newstate = this._pc.iceConnectionState;
var oldstate = this.iceConnectionLog[this.iceConnectionLog.length - 1]
if (Object.keys(iceStateTransitions).indexOf(oldstate) != -1) {
if (this.iceCheckingRestartExpected) {
is(newstate, "checking",
"iceconnectionstate event \'" + newstate +
"\' matches expected state \'checking\'");
this.iceCheckingRestartExpected = false;
+ } else if (this.iceCheckingIceRollbackExpected) {
+ is(newstate, "connected",
+ "iceconnectionstate event \'" + newstate +
+ "\' matches expected state \'connected\'");
+ this.iceCheckingIceRollbackExpected = false;
} else {
ok(iceStateTransitions[oldstate].indexOf(newstate) != -1, this + ": legal ICE state transition from " + oldstate + " to " + newstate);
}
} else {
ok(false, this + ": old ICE state " + oldstate + " missing in ICE transition array");
}
this.iceConnectionLog.push(newstate);
};
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIce.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+ createHTML({
+ bug: "906986",
+ title: "Renegotiation: restart ice"
+ });
+
+ var test;
+ runNetworkTest(function (options) {
+ test = new PeerConnectionTest(options);
+
+ addRenegotiation(test.chain,
+ [
+ // causes a full, normal ice restart
+ function PC_LOCAL_SET_OFFER_OPTION(test) {
+ test.setOfferOptions({ iceRestart: true });
+ },
+ function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+ test.pcLocal.iceCheckingRestartExpected = true;
+ },
+ function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+ test.pcRemote.iceCheckingRestartExpected = true;
+ }
+ ]
+ );
+
+ test.setMediaConstraints([{audio: true}, {video: true}],
+ [{audio: true}, {video: true}]);
+ test.run();
+ });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceLocalAndRemoteRollback.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+ createHTML({
+ bug: "906986",
+ title: "Renegotiation: restart ice, local and remote rollback"
+ });
+
+ var test;
+ runNetworkTest(function (options) {
+ test = new PeerConnectionTest(options);
+ var firstNegotiationSize = test.chain.commands.length;
+
+ addRenegotiation(test.chain,
+ [
+ // causes a full, normal ice restart
+ function PC_LOCAL_SET_OFFER_OPTION(test) {
+ test.setOfferOptions({ iceRestart: true });
+ }
+ ]
+ );
+
+ test.chain.replaceAfter('PC_REMOTE_CREATE_ANSWER',
+ [
+ function PC_LOCAL_SETUP_ICE_HANDLER(test) {
+ test.pcLocal.setupIceCandidateHandler(test);
+ if (test.testOptions.steeplechase) {
+ test.pcLocal.endOfTrickleIce.then(() => {
+ send_message({"type": "end_of_trickle_ice"});
+ });
+ }
+ },
+ function PC_REMOTE_SETUP_ICE_HANDLER(test) {
+ test.pcRemote.setupIceCandidateHandler(test);
+ if (test.testOptions.steeplechase) {
+ test.pcRemote.endOfTrickleIce.then(() => {
+ send_message({"type": "end_of_trickle_ice"});
+ });
+ }
+ },
+
+ function PC_LOCAL_EXPECT_ICE_CONNECTED(test) {
+ test.pcLocal.iceCheckingIceRollbackExpected = true;
+ },
+ function PC_REMOTE_EXPECT_ICE_CONNECTED(test) {
+ test.pcRemote.iceCheckingIceRollbackExpected = true;
+ },
+
+ function PC_REMOTE_ROLLBACK(test) {
+ return test.setRemoteDescription(
+ test.pcRemote,
+ new RTCSessionDescription({ type: "rollback" }),
+ STABLE);
+ },
+
+ function PC_LOCAL_ROLLBACK(test) {
+ // We haven't negotiated the new stream yet.
+ test.pcLocal.expectNegotiationNeeded();
+ return test.setLocalDescription(
+ test.pcLocal,
+ new RTCSessionDescription({ type: "rollback", sdp: ""}),
+ STABLE);
+ },
+
+ // Rolling back should shut down gathering
+ function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
+ return test.pcLocal.endOfTrickleIce;
+ },
+ function PC_REMOTE_WAIT_FOR_END_OF_TRICKLE(test) {
+ return test.pcRemote.endOfTrickleIce;
+ },
+
+ function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+ test.pcLocal.iceCheckingRestartExpected = true;
+ },
+ function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+ test.pcRemote.iceCheckingRestartExpected = true;
+ }
+ ],
+ firstNegotiationSize // Replaces after second PC_REMOTE_CREATE_ANSWER
+ );
+ test.chain.append(commandsPeerConnectionOfferAnswer);
+
+ // for now, only use one stream, because rollback doesn't seem to
+ // like multiple streams. See bug 1259465.
+ test.setMediaConstraints([{audio: true}],
+ [{audio: true}]);
+ test.run();
+ });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceLocalRollback.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+ createHTML({
+ bug: "906986",
+ title: "Renegotiation: restart ice, local rollback"
+ });
+
+ var test;
+ runNetworkTest(function (options) {
+ test = new PeerConnectionTest(options);
+
+ addRenegotiation(test.chain,
+ [
+ // causes a full, normal ice restart
+ function PC_LOCAL_SET_OFFER_OPTION(test) {
+ test.setOfferOptions({ iceRestart: true });
+ },
+ // causes an ice restart and then rolls it back
+ // (does not result in sending an offer)
+ function PC_LOCAL_SETUP_ICE_HANDLER(test) {
+ test.pcLocal.setupIceCandidateHandler(test);
+ if (test.testOptions.steeplechase) {
+ test.pcLocal.endOfTrickleIce.then(() => {
+ send_message({"type": "end_of_trickle_ice"});
+ });
+ }
+ },
+ function PC_LOCAL_CREATE_AND_SET_OFFER(test) {
+ return test.createOffer(test.pcLocal).then(offer => {
+ return test.setLocalDescription(test.pcLocal,
+ offer,
+ HAVE_LOCAL_OFFER);
+ });
+ },
+ function PC_LOCAL_EXPECT_ICE_CONNECTED(test) {
+ test.pcLocal.iceCheckingIceRollbackExpected = true;
+ },
+ function PC_LOCAL_ROLLBACK(test) {
+ return test.setLocalDescription(
+ test.pcLocal,
+ new RTCSessionDescription({ type: "rollback",
+ sdp: ""}),
+ STABLE);
+ },
+ // Rolling back should shut down gathering
+ function PC_LOCAL_WAIT_FOR_END_OF_TRICKLE(test) {
+ return test.pcLocal.endOfTrickleIce;
+ },
+ function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+ test.pcLocal.iceCheckingRestartExpected = true;
+ },
+ function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+ test.pcRemote.iceCheckingRestartExpected = true;
+ }
+ ]
+ );
+
+ // for now, only use one stream, because rollback doesn't seem to
+ // like multiple streams. See bug 1259465.
+ test.setMediaConstraints([{audio: true}],
+ [{audio: true}]);
+ test.run();
+ });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceNoBundle.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+ createHTML({
+ bug: "906986",
+ title: "Renegotiation: restart ice, no bundle"
+ });
+
+ var test;
+ runNetworkTest(function (options) {
+ options = options || { };
+ options.bundle = false;
+ test = new PeerConnectionTest(options);
+
+ addRenegotiation(test.chain,
+ [
+ // causes a full, normal ice restart
+ function PC_LOCAL_SET_OFFER_OPTION(test) {
+ test.setOfferOptions({ iceRestart: true });
+ },
+ function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+ test.pcLocal.iceCheckingRestartExpected = true;
+ },
+ function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+ test.pcRemote.iceCheckingRestartExpected = true;
+ }
+ ]
+ );
+
+ test.setMediaConstraints([{audio: true}, {video: true}],
+ [{audio: true}, {video: true}]);
+ test.run();
+ });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceNoBundleNoRtcpMux.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+ createHTML({
+ bug: "906986",
+ title: "Renegotiation: restart ice, no bundle and disabled RTCP-Mux"
+ });
+
+ var test;
+ runNetworkTest(function (options) {
+ options = options || { };
+ options.bundle = false;
+ options.rtcpmux = false;
+ test = new PeerConnectionTest(options);
+
+ addRenegotiation(test.chain,
+ [
+ // causes a full, normal ice restart
+ function PC_LOCAL_SET_OFFER_OPTION(test) {
+ test.setOfferOptions({ iceRestart: true });
+ },
+ function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+ test.pcLocal.iceCheckingRestartExpected = true;
+ },
+ function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+ test.pcRemote.iceCheckingRestartExpected = true;
+ }
+ ]
+ );
+
+ test.setMediaConstraints([{audio: true}, {video: true}],
+ [{audio: true}, {video: true}]);
+ test.run();
+ });
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/media/tests/mochitest/test_peerConnection_restartIceNoRtcpMux.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script type="application/javascript" src="pc.js"></script>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+ createHTML({
+ bug: "906986",
+ title: "Renegotiation: restart ice, with disabled RTCP-Mux"
+ });
+
+ var test;
+ runNetworkTest(function (options) {
+ options = options || { };
+ options.rtcpmux = false;
+ test = new PeerConnectionTest(options);
+
+ addRenegotiation(test.chain,
+ [
+ // causes a full, normal ice restart
+ function PC_LOCAL_SET_OFFER_OPTION(test) {
+ test.setOfferOptions({ iceRestart: true });
+ },
+ function PC_LOCAL_EXPECT_ICE_CHECKING(test) {
+ test.pcLocal.iceCheckingRestartExpected = true;
+ },
+ function PC_REMOTE_EXPECT_ICE_CHECKING(test) {
+ test.pcRemote.iceCheckingRestartExpected = true;
+ }
+ ]
+ );
+
+ test.setMediaConstraints([{audio: true}, {video: true}],
+ [{audio: true}, {video: true}]);
+ test.run();
+ });
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/webidl/RTCPeerConnection.webidl
+++ b/dom/webidl/RTCPeerConnection.webidl
@@ -57,17 +57,17 @@ dictionary RTCOfferAnswerOptions {
};
dictionary RTCAnswerOptions : RTCOfferAnswerOptions {
};
dictionary RTCOfferOptions : RTCOfferAnswerOptions {
long offerToReceiveVideo;
long offerToReceiveAudio;
- // boolean iceRestart = false; // Not implemented (Bug 906986)
+ boolean iceRestart;
// Mozilla proprietary options (at risk: Bug 1196974)
boolean mozDontOfferDataChannel;
boolean mozBundleOnly;
// TODO: Remove old constraint-like RTCOptions support soon (Bug 1064223).
DeprecatedRTCOfferOptionsSet mandatory;
sequence<DeprecatedRTCOfferOptionsSet> _optional;
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -393,19 +393,19 @@ void NrIceCtx::trickle_cb(void *arg, nr_
MOZ_MTLOG(ML_INFO, "NrIceCtx(" << ctx->name_ << "): trickling candidate "
<< candidate_str);
s->SignalCandidate(s, candidate_str);
}
void
-NrIceCtx::InitializeCryptoAndLogging(bool allow_loopback,
- bool tcp_enabled,
- bool allow_link_local) {
+NrIceCtx::InitializeGlobals(bool allow_loopback,
+ bool tcp_enabled,
+ bool allow_link_local) {
// Initialize the crypto callbacks and logging stuff
if (!initialized) {
NR_reg_init(NR_REG_MODE_LOCAL);
nr_crypto_vtbl = &nr_ice_crypto_nss_vtbl;
initialized = true;
// Set the priorites for candidate type preferences.
// These numbers come from RFC 5245 S. 4.1.2.2
@@ -469,57 +469,114 @@ NrIceCtx::InitializeCryptoAndLogging(boo
if (force_net_interface.Length() > 0) {
// Stupid cast.... but needed
const nsCString& flat = PromiseFlatCString(static_cast<nsACString&>(force_net_interface));
NR_reg_set_string((char *)NR_ICE_REG_PREF_FORCE_INTERFACE_NAME, const_cast<char*>(flat.get()));
}
}
}
+std::string
+NrIceCtx::GetNewUfrag()
+{
+ char* ufrag;
+ int r;
+
+ if ((r=nr_ice_get_new_ice_ufrag(&ufrag))) {
+ MOZ_CRASH("Unable to get new ice ufrag");
+ return "";
+ }
+
+ std::string ufragStr = ufrag;
+ RFREE(ufrag);
+
+ return ufragStr;
+}
+
+std::string
+NrIceCtx::GetNewPwd()
+{
+ char* pwd;
+ int r;
+
+ if ((r=nr_ice_get_new_ice_pwd(&pwd))) {
+ MOZ_CRASH("Unable to get new ice pwd");
+ return "";
+ }
+
+ std::string pwdStr = pwd;
+ RFREE(pwd);
+
+ return pwdStr;
+}
+
bool
-NrIceCtx::Initialize(NrIceCtx* ctx,
- bool hide_non_default)
+NrIceCtx::Initialize(bool hide_non_default)
{
+ std::string ufrag = GetNewUfrag();
+ std::string pwd = GetNewPwd();
+
+ return Initialize(hide_non_default,
+ ufrag,
+ pwd);
+}
+
+bool
+NrIceCtx::Initialize(bool hide_non_default,
+ const std::string& ufrag,
+ const std::string& pwd)
+{
+ MOZ_ASSERT(!ufrag.empty());
+ MOZ_ASSERT(!pwd.empty());
+ if (ufrag.empty() || pwd.empty()) {
+ return false;
+ }
+
// Create the ICE context
int r;
- UINT4 flags = ctx->offerer_ ? NR_ICE_CTX_FLAGS_OFFERER:
+ UINT4 flags = offerer_ ? NR_ICE_CTX_FLAGS_OFFERER:
NR_ICE_CTX_FLAGS_ANSWERER;
flags |= NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION;
- if (ctx->policy_ == ICE_POLICY_RELAY) {
+ if (policy_ == ICE_POLICY_RELAY) {
flags |= NR_ICE_CTX_FLAGS_RELAY_ONLY;
}
if (hide_non_default)
flags |= NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS;
- r = nr_ice_ctx_create(const_cast<char *>(ctx->name_.c_str()),
- flags,
- &ctx->ctx_);
+ r = nr_ice_ctx_create_with_credentials(const_cast<char *>(name_.c_str()),
+ flags,
+ const_cast<char *>(ufrag.c_str()),
+ const_cast<char *>(pwd.c_str()),
+ &ctx_);
+ MOZ_ASSERT(ufrag == ctx_->ufrag);
+ MOZ_ASSERT(pwd == ctx_->pwd);
+
if (r) {
- MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << ctx->name_ << "'");
+ MOZ_MTLOG(ML_ERROR, "Couldn't create ICE ctx for '" << name_ << "'");
return false;
}
nr_interface_prioritizer *prioritizer = CreateInterfacePrioritizer();
if (!prioritizer) {
MOZ_MTLOG(LogLevel::Error, "Couldn't create interface prioritizer.");
return false;
}
- r = nr_ice_ctx_set_interface_prioritizer(ctx->ctx_, prioritizer);
+ r = nr_ice_ctx_set_interface_prioritizer(ctx_, prioritizer);
if (r) {
MOZ_MTLOG(LogLevel::Error, "Couldn't set interface prioritizer.");
return false;
}
- if (ctx->generating_trickle()) {
- r = nr_ice_ctx_set_trickle_cb(ctx->ctx_, &NrIceCtx::trickle_cb, ctx);
+ if (generating_trickle()) {
+ r = nr_ice_ctx_set_trickle_cb(ctx_, &NrIceCtx::trickle_cb, this);
if (r) {
- MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << ctx->name_ << "'");
+ MOZ_MTLOG(ML_ERROR, "Couldn't set trickle cb for '" << name_ << "'");
return false;
}
}
char* mapping_type = nullptr;
char* filtering_type = nullptr;
bool block_udp = false;
@@ -546,44 +603,44 @@ NrIceCtx::Initialize(NrIceCtx* ctx,
if (mapping_type && filtering_type) {
MOZ_MTLOG(ML_DEBUG, "NAT filtering type: " << filtering_type);
MOZ_MTLOG(ML_DEBUG, "NAT mapping type: " << mapping_type);
TestNat* test_nat = new TestNat;
test_nat->filtering_type_ = TestNat::ToNatBehavior(filtering_type);
test_nat->mapping_type_ = TestNat::ToNatBehavior(mapping_type);
test_nat->block_udp_ = block_udp;
test_nat->enabled_ = true;
- ctx->SetNat(test_nat);
+ SetNat(test_nat);
}
// Create the handler objects
- ctx->ice_handler_vtbl_ = new nr_ice_handler_vtbl();
- ctx->ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair;
- ctx->ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready;
- ctx->ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed;
- ctx->ice_handler_vtbl_->ice_completed = &NrIceCtx::ice_completed;
- ctx->ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd;
- ctx->ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking;
+ ice_handler_vtbl_ = new nr_ice_handler_vtbl();
+ ice_handler_vtbl_->select_pair = &NrIceCtx::select_pair;
+ ice_handler_vtbl_->stream_ready = &NrIceCtx::stream_ready;
+ ice_handler_vtbl_->stream_failed = &NrIceCtx::stream_failed;
+ ice_handler_vtbl_->ice_completed = &NrIceCtx::ice_completed;
+ ice_handler_vtbl_->msg_recvd = &NrIceCtx::msg_recvd;
+ ice_handler_vtbl_->ice_checking = &NrIceCtx::ice_checking;
- ctx->ice_handler_ = new nr_ice_handler();
- ctx->ice_handler_->vtbl = ctx->ice_handler_vtbl_;
- ctx->ice_handler_->obj = ctx;
+ ice_handler_ = new nr_ice_handler();
+ ice_handler_->vtbl = ice_handler_vtbl_;
+ ice_handler_->obj = this;
// Create the peer ctx. Because we do not support parallel forking, we
// only have one peer ctx.
- std::string peer_name = ctx->name_ + ":default";
- r = nr_ice_peer_ctx_create(ctx->ctx_, ctx->ice_handler_,
+ std::string peer_name = name_ + ":default";
+ r = nr_ice_peer_ctx_create(ctx_, ice_handler_,
const_cast<char *>(peer_name.c_str()),
- &ctx->peer_);
+ &peer_);
if (r) {
- MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << ctx->name_ << "'");
+ MOZ_MTLOG(ML_ERROR, "Couldn't create ICE peer ctx for '" << name_ << "'");
return false;
}
- ctx->sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+ sts_target_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
if (!NS_SUCCEEDED(rv))
return false;
return true;
}
int NrIceCtx::SetNat(const RefPtr<TestNat>& aNat) {
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -187,16 +187,17 @@ class NrIceProxyServer {
std::string host_;
uint16_t port_;
std::string alpn_;
};
class TestNat;
class NrIceCtx {
+ friend class NrIceCtxHandler;
public:
enum ConnectionState { ICE_CTX_INIT,
ICE_CTX_CHECKING,
ICE_CTX_OPEN,
ICE_CTX_FAILED
};
enum GatheringState { ICE_CTX_GATHER_INIT,
@@ -208,22 +209,27 @@ class NrIceCtx {
ICE_CONTROLLED
};
enum Policy { ICE_POLICY_NONE,
ICE_POLICY_RELAY,
ICE_POLICY_ALL
};
- static void InitializeCryptoAndLogging(bool allow_loopback = false,
- bool tcp_enabled = true,
- bool allow_link_local = false);
+ // initialize ICE globals, crypto, and logging
+ static void InitializeGlobals(bool allow_loopback = false,
+ bool tcp_enabled = true,
+ bool allow_link_local = false);
+ static std::string GetNewUfrag();
+ static std::string GetNewPwd();
- static bool Initialize(NrIceCtx* ice_ctx,
- bool hide_non_default);
+ bool Initialize(bool hide_non_default);
+ bool Initialize(bool hide_non_default,
+ const std::string& ufrag,
+ const std::string& pwd);
int SetNat(const RefPtr<TestNat>& aNat);
// Deinitialize all ICE global state. Used only for testing.
static void internal_DeinitializeGlobal();
nr_ice_ctx *ctx() { return ctx_; }
@@ -319,24 +325,23 @@ class NrIceCtx {
sigslot::signal2<NrIceCtx*, NrIceCtx::ConnectionState>
SignalConnectionStateChange;
// The thread to direct method calls to
nsCOMPtr<nsIEventTarget> thread() { return sts_target_; }
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtx)
- protected:
- virtual ~NrIceCtx();
-
+private:
NrIceCtx(const std::string& name,
bool offerer,
Policy policy);
- private:
+ virtual ~NrIceCtx();
+
DISALLOW_COPY_ASSIGN(NrIceCtx);
// Callbacks for nICEr
static void gather_cb(NR_SOCKET s, int h, void *arg); // ICE gather complete
// Handler implementation
static int select_pair(void *obj,nr_ice_media_stream *stream,
int component_id, nr_ice_cand_pair **potentials,
@@ -355,17 +360,16 @@ class NrIceCtx {
RefPtr<NrIceMediaStream> FindStream(nr_ice_media_stream *stream);
// Set the state
void SetConnectionState(ConnectionState state);
// Set the state
void SetGatheringState(GatheringState state);
-protected:
ConnectionState connection_state_;
GatheringState gathering_state_;
const std::string name_;
bool offerer_;
std::vector<RefPtr<NrIceMediaStream> > streams_;
nr_ice_ctx *ctx_;
nr_ice_peer_ctx *peer_;
nr_ice_handler_vtbl* ice_handler_vtbl_; // Must be pointer
--- a/media/mtransport/nricectxhandler.cpp
+++ b/media/mtransport/nricectxhandler.cpp
@@ -1,52 +1,131 @@
#include <sstream>
+#include "logging.h"
+
// nICEr includes
extern "C" {
#include "nr_api.h"
#include "ice_ctx.h"
}
// Local includes
#include "nricectxhandler.h"
#include "nricemediastream.h"
namespace mozilla {
-NrIceCtxHandler::~NrIceCtxHandler()
+MOZ_MTLOG_MODULE("mtransport")
+
+NrIceCtxHandler::NrIceCtxHandler(const std::string& name,
+ bool offerer,
+ NrIceCtx::Policy policy)
+ : current_ctx(new NrIceCtx(name, offerer, policy)),
+ old_ctx(nullptr),
+ restart_count(0)
{
}
-
RefPtr<NrIceCtxHandler>
NrIceCtxHandler::Create(const std::string& name,
bool offerer,
bool allow_loopback,
bool tcp_enabled,
bool allow_link_local,
bool hide_non_default,
- Policy policy)
+ NrIceCtx::Policy policy)
{
- // InitializeCryptoAndLogging only executes once
- NrIceCtx::InitializeCryptoAndLogging(allow_loopback,
- tcp_enabled,
- allow_link_local);
+ // InitializeGlobals only executes once
+ NrIceCtx::InitializeGlobals(allow_loopback, tcp_enabled, allow_link_local);
RefPtr<NrIceCtxHandler> ctx = new NrIceCtxHandler(name, offerer, policy);
- if (!NrIceCtx::Initialize(ctx,
- hide_non_default)) {
+ if (ctx == nullptr ||
+ ctx->current_ctx == nullptr ||
+ !ctx->current_ctx->Initialize(hide_non_default)) {
return nullptr;
}
return ctx;
}
RefPtr<NrIceMediaStream>
NrIceCtxHandler::CreateStream(const std::string& name, int components)
{
- return NrIceMediaStream::Create(this, name, components);
+ // To make tracking NrIceMediaStreams easier during ICE restart
+ // prepend an int to the name that increments with each ICE restart
+ std::ostringstream os;
+ os << restart_count << "-" << name;
+ return NrIceMediaStream::Create(this->current_ctx, os.str(), components);
+}
+
+
+RefPtr<NrIceCtx>
+NrIceCtxHandler::CreateCtx(bool hide_non_default) const
+{
+ return CreateCtx(NrIceCtx::GetNewUfrag(),
+ NrIceCtx::GetNewPwd(),
+ hide_non_default);
+}
+
+
+RefPtr<NrIceCtx>
+NrIceCtxHandler::CreateCtx(const std::string& ufrag,
+ const std::string& pwd,
+ bool hide_non_default) const
+{
+ RefPtr<NrIceCtx> new_ctx = new NrIceCtx(this->current_ctx->name(),
+ true, // offerer (hardcoded per bwc)
+ this->current_ctx->policy());
+ if (new_ctx == nullptr) {
+ return nullptr;
+ }
+
+ if (!new_ctx->Initialize(hide_non_default, ufrag, pwd)) {
+ return nullptr;
+ }
+
+ return new_ctx;
+}
+
+
+bool
+NrIceCtxHandler::BeginIceRestart(RefPtr<NrIceCtx> new_ctx)
+{
+ MOZ_ASSERT(!old_ctx, "existing ice restart in progress");
+ if (old_ctx) {
+ MOZ_MTLOG(ML_ERROR, "Existing ice restart in progress");
+ return false; // ice restart already in progress
+ }
+
+ if (new_ctx == nullptr) {
+ return false;
+ }
+
+ ++restart_count;
+ old_ctx = current_ctx;
+ current_ctx = new_ctx;
+ return true;
+}
+
+
+void
+NrIceCtxHandler::FinalizeIceRestart()
+{
+ // no harm calling this even if we're not in the middle of restarting
+ old_ctx = nullptr;
+}
+
+
+void
+NrIceCtxHandler::RollbackIceRestart()
+{
+ if (old_ctx == nullptr) {
+ return;
+ }
+ current_ctx = old_ctx;
+ old_ctx = nullptr;
}
} // close namespace
--- a/media/mtransport/nricectxhandler.h
+++ b/media/mtransport/nricectxhandler.h
@@ -1,36 +1,54 @@
#ifndef nricectxhandler_h__
#define nricectxhandler_h__
#include "nricectx.h"
namespace mozilla {
-class NrIceCtxHandler : public NrIceCtx {
+class NrIceCtxHandler {
public:
// TODO(ekr@rtfm.com): Too many bools here. Bug 1193437.
static RefPtr<NrIceCtxHandler> Create(const std::string& name,
- bool offerer,
- bool allow_loopback = false,
- bool tcp_enabled = true,
- bool allow_link_local = false,
- bool hide_non_default = false,
- Policy policy = ICE_POLICY_ALL);
+ bool offerer,
+ bool allow_loopback = false,
+ bool tcp_enabled = true,
+ bool allow_link_local = false,
+ bool hide_non_default = false,
+ NrIceCtx::Policy policy =
+ NrIceCtx::ICE_POLICY_ALL);
- // Create a media stream
RefPtr<NrIceMediaStream> CreateStream(const std::string& name,
int components);
+ // CreateCtx is necessary so we can create and initialize the context
+ // on main thread, but begin the ice restart mechanics on STS thread
+ RefPtr<NrIceCtx> CreateCtx(bool hide_non_default = false) const; // for test
+ RefPtr<NrIceCtx> CreateCtx(const std::string& ufrag,
+ const std::string& pwd,
+ bool hide_non_default) const;
-protected:
+ RefPtr<NrIceCtx> ctx() { return current_ctx; }
+
+ bool BeginIceRestart(RefPtr<NrIceCtx> new_ctx);
+ bool IsRestarting() const { return (old_ctx != nullptr); }
+ void FinalizeIceRestart();
+ void RollbackIceRestart();
+
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(NrIceCtxHandler)
+
+private:
NrIceCtxHandler(const std::string& name,
bool offerer,
- Policy policy)
- : NrIceCtx(name, offerer, policy)
- {}
+ NrIceCtx::Policy policy);
+ NrIceCtxHandler() = delete;
+ ~NrIceCtxHandler() {}
+ DISALLOW_COPY_ASSIGN(NrIceCtxHandler);
- NrIceCtxHandler(); // disable
- virtual ~NrIceCtxHandler();
+ RefPtr<NrIceCtx> current_ctx;
+ RefPtr<NrIceCtx> old_ctx; // for while restart is in progress
+ int restart_count; // used to differentiate streams between restarted ctx
};
} // close namespace
#endif // nricectxhandler_h__
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -379,17 +379,18 @@ class IceTestPeer : public sigslot::has_
// TODO(ekr@rtfm.com): Convert to flags when NrIceCtx::Create() does.
// Bug 1193437.
IceTestPeer(const std::string& name, MtransportTestUtils* utils,
bool offerer,
bool allow_loopback = false, bool enable_tcp = true,
bool allow_link_local = false, bool hide_non_default = false) :
name_(name),
ice_ctx_(NrIceCtxHandler::Create(name, offerer, allow_loopback,
- enable_tcp, allow_link_local, hide_non_default)),
+ enable_tcp, allow_link_local,
+ hide_non_default)),
candidates_(),
gathering_complete_(false),
ready_ct_(0),
ice_complete_(false),
ice_reached_checking_(false),
received_(0),
sent_(0),
fake_resolver_(),
@@ -399,24 +400,24 @@ class IceTestPeer : public sigslot::has_
expected_local_type_(NrIceCandidate::ICE_HOST),
expected_local_transport_(kNrIceTransportUdp),
expected_remote_type_(NrIceCandidate::ICE_HOST),
trickle_mode_(TRICKLE_NONE),
trickled_(0),
simulate_ice_lite_(false),
nat_(new TestNat),
test_utils_(utils) {
- ice_ctx_->SignalGatheringStateChange.connect(
+ ice_ctx_->ctx()->SignalGatheringStateChange.connect(
this,
&IceTestPeer::GatheringStateChange);
- ice_ctx_->SignalConnectionStateChange.connect(
+ ice_ctx_->ctx()->SignalConnectionStateChange.connect(
this,
&IceTestPeer::ConnectionStateChange);
- int r = ice_ctx_->SetNat(nat_);
+ int r = ice_ctx_->ctx()->SetNat(nat_);
(void)r;
MOZ_ASSERT(!r);
}
~IceTestPeer() {
test_utils_->sts_target()->Dispatch(WrapRunnable(this,
&IceTestPeer::Shutdown),
NS_DISPATCH_SYNC);
@@ -424,38 +425,38 @@ class IceTestPeer : public sigslot::has_
// Give the ICE destruction callback time to fire before
// we destroy the resolver.
PR_Sleep(1000);
}
void AddStream_s(int components) {
char name[100];
snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(),
- (int)ice_ctx_->GetStreamCount());
+ (int)ice_ctx_->ctx()->GetStreamCount());
RefPtr<NrIceMediaStream> stream =
ice_ctx_->CreateStream(static_cast<char *>(name), components);
- ice_ctx_->SetStream(ice_ctx_->GetStreamCount(), stream);
+ ice_ctx_->ctx()->SetStream(ice_ctx_->ctx()->GetStreamCount(), stream);
ASSERT_TRUE(stream);
stream->SignalCandidate.connect(this, &IceTestPeer::CandidateInitialized);
stream->SignalReady.connect(this, &IceTestPeer::StreamReady);
stream->SignalFailed.connect(this, &IceTestPeer::StreamFailed);
stream->SignalPacketReceived.connect(this, &IceTestPeer::PacketReceived);
}
void AddStream(int components)
{
test_utils_->sts_target()->Dispatch(
WrapRunnable(this, &IceTestPeer::AddStream_s, components),
NS_DISPATCH_SYNC);
}
void RemoveStream_s(size_t index) {
- ice_ctx_->SetStream(index, nullptr);
+ ice_ctx_->ctx()->SetStream(index, nullptr);
}
void RemoveStream(size_t index) {
test_utils_->sts_target()->Dispatch(
WrapRunnable(this, &IceTestPeer::RemoveStream_s, index),
NS_DISPATCH_SYNC);
}
@@ -469,17 +470,17 @@ class IceTestPeer : public sigslot::has_
std::vector<NrIceStunServer> stun_servers;
UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(
addr, port, transport));
stun_servers.push_back(*server);
SetStunServers(stun_servers);
}
void SetStunServers(const std::vector<NrIceStunServer> &servers) {
- ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(servers)));
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetStunServers(servers)));
}
void UseTestStunServer() {
SetStunServer(TestStunServer::GetInstance(AF_INET)->addr(),
TestStunServer::GetInstance(AF_INET)->port());
}
void SetTurnServer(const std::string addr, uint16_t port,
@@ -494,48 +495,48 @@ class IceTestPeer : public sigslot::has_
void SetTurnServer(const std::string addr, uint16_t port,
const std::string username,
const std::vector<unsigned char> password,
const char* transport) {
std::vector<NrIceTurnServer> turn_servers;
UniquePtr<NrIceTurnServer> server(NrIceTurnServer::Create(
addr, port, username, password, transport));
turn_servers.push_back(*server);
- ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetTurnServers(turn_servers)));
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetTurnServers(turn_servers)));
}
void SetTurnServers(const std::vector<NrIceTurnServer> servers) {
- ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetTurnServers(servers)));
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetTurnServers(servers)));
}
void SetFakeResolver(const std::string& ip,
const std::string& fqdn) {
ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init()));
if (!ip.empty() && !fqdn.empty()) {
PRNetAddr addr;
PRStatus status = PR_StringToNetAddr(ip.c_str(), &addr);
addr.inet.port = kDefaultStunServerPort;
ASSERT_EQ(PR_SUCCESS, status);
fake_resolver_.SetAddr(fqdn, addr);
}
- ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetResolver(
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetResolver(
fake_resolver_.AllocateResolver())));
}
void SetDNSResolver() {
ASSERT_TRUE(NS_SUCCEEDED(dns_resolver_->Init()));
- ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetResolver(
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetResolver(
dns_resolver_->AllocateResolver())));
}
void Gather() {
nsresult res;
test_utils_->sts_target()->Dispatch(
- WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::StartGathering),
+ WrapRunnableRet(&res, ice_ctx_->ctx(), &NrIceCtx::StartGathering),
NS_DISPATCH_SYNC);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
void UseNat() {
nat_->enabled_ = true;
}
@@ -552,17 +553,17 @@ class IceTestPeer : public sigslot::has_
void SetBlockUdp(bool block) {
MOZ_ASSERT(!nat_->has_port_mappings());
nat_->block_udp_ = block;
}
// Get various pieces of state
std::vector<std::string> GetGlobalAttributes() {
- std::vector<std::string> attrs(ice_ctx_->GetGlobalAttributes());
+ std::vector<std::string> attrs(ice_ctx_->ctx()->GetGlobalAttributes());
if (simulate_ice_lite_) {
attrs.push_back("ice-lite");
}
return attrs;
}
std::vector<std::string> GetCandidates(size_t stream) {
std::vector<std::string> v;
@@ -579,28 +580,30 @@ class IceTestPeer : public sigslot::has_
return candidate_filter_(candidate);
}
return candidate;
}
std::vector<std::string> GetCandidates_s(size_t stream) {
std::vector<std::string> candidates;
- if (stream >= ice_ctx_->GetStreamCount() || !ice_ctx_->GetStream(stream)) {
+ if (stream >= ice_ctx_->ctx()->GetStreamCount() ||
+ !ice_ctx_->ctx()->GetStream(stream)) {
EXPECT_TRUE(false) << "No such stream " << stream;
return candidates;
}
std::vector<std::string> candidates_in =
- ice_ctx_->GetStream(stream)->GetCandidates();
+ ice_ctx_->ctx()->GetStream(stream)->GetCandidates();
for (size_t i=0; i < candidates_in.size(); i++) {
std::string candidate(FilterCandidate(candidates_in[i]));
if (!candidate.empty()) {
- std::cerr << name_ << " Returning candidate: " << candidate << std::endl;
+ std::cerr << name_ << " Returning candidate: "
+ << candidate << std::endl;
candidates.push_back(candidate);
}
}
return candidates;
}
void SetExpectedTypes(NrIceCandidate::Type local,
@@ -639,81 +642,136 @@ class IceTestPeer : public sigslot::has_
}
}
return host_net;
}
bool gathering_complete() { return gathering_complete_; }
int ready_ct() { return ready_ct_; }
bool is_ready_s(size_t stream) {
- if (!ice_ctx_->GetStream(stream)) {
+ RefPtr<NrIceMediaStream> media_stream = ice_ctx_->ctx()->GetStream(stream);
+ if (!media_stream) {
EXPECT_TRUE(false) << "No such stream " << stream;
return false;
}
- return ice_ctx_->GetStream(stream)->state() == NrIceMediaStream::ICE_OPEN;
+ return media_stream->state() == NrIceMediaStream::ICE_OPEN;
}
bool is_ready(size_t stream)
{
bool result;
test_utils_->sts_target()->Dispatch(
WrapRunnableRet(&result, this, &IceTestPeer::is_ready_s, stream),
NS_DISPATCH_SYNC);
return result;
}
bool ice_complete() { return ice_complete_; }
bool ice_reached_checking() { return ice_reached_checking_; }
size_t received() { return received_; }
size_t sent() { return sent_; }
+
+ void RestartIce() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this,
+ &IceTestPeer::RestartIce_s,
+ ice_ctx_->CreateCtx()),
+ NS_DISPATCH_SYNC);
+ }
+
+
+ void RestartIce_s(RefPtr<NrIceCtx> new_ctx) {
+ ice_ctx_->BeginIceRestart(new_ctx);
+
+ // set signals for the newly restarted ctx
+ ice_ctx_->ctx()->SignalGatheringStateChange.connect(
+ this,
+ &IceTestPeer::GatheringStateChange);
+ ice_ctx_->ctx()->SignalConnectionStateChange.connect(
+ this,
+ &IceTestPeer::ConnectionStateChange);
+
+ // take care of some local bookkeeping
+ ready_ct_ = 0;
+ gathering_complete_ = false;
+ ice_complete_ = false;
+ ice_reached_checking_ = false;
+ remote_ = nullptr;
+ }
+
+
+ void FinalizeIceRestart() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::FinalizeIceRestart_s),
+ NS_DISPATCH_SYNC);
+ }
+
+
+ void FinalizeIceRestart_s() {
+ ice_ctx_->FinalizeIceRestart();
+ }
+
+
+ void RollbackIceRestart() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this, &IceTestPeer::RollbackIceRestart_s),
+ NS_DISPATCH_SYNC);
+ }
+
+
+ void RollbackIceRestart_s() {
+ ice_ctx_->RollbackIceRestart();
+ }
+
+
// Start connecting to another peer
void Connect_s(IceTestPeer *remote, TrickleMode trickle_mode,
bool start = true) {
nsresult res;
remote_ = remote;
trickle_mode_ = trickle_mode;
ice_complete_ = false;
ice_reached_checking_ = false;
- res = ice_ctx_->ParseGlobalAttributes(remote->GetGlobalAttributes());
+ res = ice_ctx_->ctx()->ParseGlobalAttributes(remote->GetGlobalAttributes());
ASSERT_TRUE(NS_SUCCEEDED(res));
if (trickle_mode == TRICKLE_NONE ||
trickle_mode == TRICKLE_REAL) {
- for (size_t i=0; i<ice_ctx_->GetStreamCount(); ++i) {
- RefPtr<NrIceMediaStream> aStream = ice_ctx_->GetStream(i);
+ for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ RefPtr<NrIceMediaStream> aStream = ice_ctx_->ctx()->GetStream(i);
if (!aStream || aStream->HasParsedAttributes()) {
continue;
}
std::vector<std::string> candidates =
remote->GetCandidates(i);
for (size_t j=0; j<candidates.size(); ++j) {
std::cerr << name_ << " Candidate: " + candidates[j] << std::endl;
}
res = aStream->ParseAttributes(candidates);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
} else {
// Parse empty attributes and then trickle them out later
- for (size_t i=0; i<ice_ctx_->GetStreamCount(); ++i) {
- RefPtr<NrIceMediaStream> aStream = ice_ctx_->GetStream(i);
+ for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ RefPtr<NrIceMediaStream> aStream = ice_ctx_->ctx()->GetStream(i);
if (!aStream || aStream->HasParsedAttributes()) {
continue;
}
std::vector<std::string> empty_attrs;
std::cout << "Calling ParseAttributes on stream " << i << std::endl;
res = aStream->ParseAttributes(empty_attrs);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
}
if (start) {
// Now start checks
- res = ice_ctx_->StartChecks();
+ res = ice_ctx_->ctx()->StartChecks();
ASSERT_TRUE(NS_SUCCEEDED(res));
}
}
void Connect(IceTestPeer *remote, TrickleMode trickle_mode,
bool start = true) {
test_utils_->sts_target()->Dispatch(
WrapRunnable(
@@ -721,20 +779,19 @@ class IceTestPeer : public sigslot::has_
NS_DISPATCH_SYNC);
}
void SimulateTrickle(size_t stream) {
std::cerr << name_ << " Doing trickle for stream " << stream << std::endl;
// If we are in trickle deferred mode, now trickle in the candidates
// for |stream|
- // The size of streams is not going to change out from under us, so should
- // be safe here.
- ASSERT_GT(remote_->ice_ctx_->GetStreamCount(), stream);
- ASSERT_TRUE(remote_->ice_ctx_->GetStream(stream).get());
+ // We should be safe here since stream changes happen on STS thread.
+ ASSERT_GT(remote_->ice_ctx_->ctx()->GetStreamCount(), stream);
+ ASSERT_TRUE(remote_->ice_ctx_->ctx()->GetStream(stream).get());
std::vector<SchedulableTrickleCandidate*>& candidates =
ControlTrickle(stream);
for (auto i = candidates.begin(); i != candidates.end(); ++i) {
(*i)->Schedule(0);
}
}
@@ -753,21 +810,21 @@ class IceTestPeer : public sigslot::has_
new SchedulableTrickleCandidate(
this, stream, candidates[j], test_utils_));
}
return controlled_trickle_candidates_[stream];
}
nsresult TrickleCandidate_s(const std::string &candidate, size_t stream) {
- if (!ice_ctx_->GetStream(stream)) {
+ if (!ice_ctx_->ctx()->GetStream(stream)) {
// stream might have gone away before the trickle timer popped
return NS_OK;
}
- return ice_ctx_->GetStream(stream)->ParseTrickleCandidate(candidate);
+ return ice_ctx_->ctx()->GetStream(stream)->ParseTrickleCandidate(candidate);
}
void DumpCandidate(std::string which, const NrIceCandidate& cand) {
std::string type;
std::string tcp_type;
std::string addr;
int port;
@@ -829,28 +886,31 @@ class IceTestPeer : public sigslot::has_
<< tcp_type
<< " codeword="
<< cand.codeword
<< std::endl;
}
void DumpAndCheckActiveCandidates_s() {
std::cerr << name_ << " Active candidates:" << std::endl;
- for (size_t i=0; i < ice_ctx_->GetStreamCount(); ++i) {
- if (!ice_ctx_->GetStream(i)) {
+ for (size_t i=0; i < ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ if (!ice_ctx_->ctx()->GetStream(i)) {
continue;
}
- for (size_t j=0; j < ice_ctx_->GetStream(i)->components(); ++j) {
- std::cerr << name_ << " Stream " << i << " component " << j+1 << std::endl;
+ for (size_t j=0; j < ice_ctx_->ctx()->GetStream(i)->components(); ++j) {
+ std::cerr << name_ << " Stream " << i
+ << " component " << j+1 << std::endl;
UniquePtr<NrIceCandidate> local;
UniquePtr<NrIceCandidate> remote;
- nsresult res = ice_ctx_->GetStream(i)->GetActivePair(j+1, &local, &remote);
+ nsresult res = ice_ctx_->ctx()->GetStream(i)->GetActivePair(j+1,
+ &local,
+ &remote);
if (res == NS_ERROR_NOT_AVAILABLE) {
std::cerr << "Component unpaired or disabled." << std::endl;
} else {
ASSERT_TRUE(NS_SUCCEEDED(res));
DumpCandidate("Local ", *local);
/* Depending on timing, and the whims of the network
* stack/configuration we're running on top of, prflx is always a
* possibility. */
@@ -882,17 +942,17 @@ class IceTestPeer : public sigslot::has_
void DumpAndCheckActiveCandidates() {
test_utils_->sts_target()->Dispatch(
WrapRunnable(this, &IceTestPeer::DumpAndCheckActiveCandidates_s),
NS_DISPATCH_SYNC);
}
void Close() {
test_utils_->sts_target()->Dispatch(
- WrapRunnable(ice_ctx_, &NrIceCtx::destroy_peer_ctx),
+ WrapRunnable(ice_ctx_->ctx(), &NrIceCtx::destroy_peer_ctx),
NS_DISPATCH_SYNC);
}
void Shutdown() {
std::cerr << name_ << " Shutdown" << std::endl;
for (auto s = controlled_trickle_candidates_.begin();
s != controlled_trickle_candidates_.end();
++s) {
@@ -914,43 +974,43 @@ class IceTestPeer : public sigslot::has_
remote_ = nullptr;
}
void StartChecks() {
nsresult res;
// Now start checks
test_utils_->sts_target()->Dispatch(
- WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::StartChecks),
+ WrapRunnableRet(&res, ice_ctx_->ctx(), &NrIceCtx::StartChecks),
NS_DISPATCH_SYNC);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
// Handle events
void GatheringStateChange(NrIceCtx* ctx,
NrIceCtx::GatheringState state) {
(void)ctx;
if (state != NrIceCtx::ICE_CTX_GATHER_COMPLETE) {
return;
}
- std::cerr << "Gathering complete for " << name_ << std::endl;
+ std::cerr << name_ << " Gathering complete" << std::endl;
gathering_complete_ = true;
std::cerr << name_ << " CANDIDATES:" << std::endl;
- for (size_t i=0; i<ice_ctx_->GetStreamCount(); ++i) {
+ for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
std::cerr << "Stream " << name_ << std::endl;
- if (!ice_ctx_->GetStream(i)) {
+ if (!ice_ctx_->ctx()->GetStream(i)) {
std::cerr << "DISABLED" << std::endl;
continue;
}
std::vector<std::string> candidates =
- ice_ctx_->GetStream(i)->GetCandidates();
+ ice_ctx_->ctx()->GetStream(i)->GetCandidates();
for(size_t j=0; j<candidates.size(); ++j) {
std::cerr << candidates[j] << std::endl;
}
}
std::cerr << std::endl;
}
@@ -963,44 +1023,42 @@ class IceTestPeer : public sigslot::has_
std::cerr << "Candidate for stream " << stream->name() << " initialized: "
<< candidate << std::endl;
candidates_[stream->name()].push_back(candidate);
// If we are connected, then try to trickle to the other side.
if (remote_ && remote_->remote_ && (trickle_mode_ != TRICKLE_SIMULATE)) {
// first, find the index of the stream we've been given so
// we can get the corresponding stream on the remote side
- size_t index = -1;
- for (size_t i=0; i<ice_ctx_->GetStreamCount(); ++i) {
- if (ice_ctx_->GetStream(i) == stream) {
- index = i;
- break;
+ for (size_t i=0; i<ice_ctx_->ctx()->GetStreamCount(); ++i) {
+ if (ice_ctx_->ctx()->GetStream(i) == stream) {
+ RefPtr<NrIceCtx> ctx = remote_->ice_ctx_->ctx();
+ ASSERT_GT(ctx->GetStreamCount(), i);
+ nsresult res = ctx->GetStream(i)->ParseTrickleCandidate(candidate);
+ ASSERT_TRUE(NS_SUCCEEDED(res));
+ ++trickled_;
+ return;
}
}
- ASSERT_NE(index, -1u);
-
- ASSERT_GT(remote_->ice_ctx_->GetStreamCount(), index);
- nsresult res = remote_->ice_ctx_->GetStream(index)->ParseTrickleCandidate(
- candidate);
- ASSERT_TRUE(NS_SUCCEEDED(res));
- ++trickled_;
+ ADD_FAILURE() << "No matching stream found for " << stream;
}
}
nsresult GetCandidatePairs_s(size_t stream_index,
std::vector<NrIceCandidatePair>* pairs)
{
MOZ_ASSERT(pairs);
- if (stream_index >= ice_ctx_->GetStreamCount() || !ice_ctx_->GetStream(stream_index)) {
+ if (stream_index >= ice_ctx_->ctx()->GetStreamCount() ||
+ !ice_ctx_->ctx()->GetStream(stream_index)) {
// Is there a better error for "no such index"?
ADD_FAILURE() << "No such media stream index: " << stream_index;
return NS_ERROR_INVALID_ARG;
}
- return ice_ctx_->GetStream(stream_index)->GetCandidatePairs(pairs);
+ return ice_ctx_->ctx()->GetStream(stream_index)->GetCandidatePairs(pairs);
}
nsresult GetCandidatePairs(size_t stream_index,
std::vector<NrIceCandidatePair>* pairs) {
nsresult v;
test_utils_->sts_target()->Dispatch(
WrapRunnableRet(&v, this,
&IceTestPeer::GetCandidatePairs_s,
@@ -1032,21 +1090,21 @@ class IceTestPeer : public sigslot::has_
p != pairs.end(); ++p) {
DumpCandidatePair(*p);
}
std::cerr << "]" << std::endl;
}
void DumpCandidatePairs_s() {
std::cerr << "Dumping candidate pairs for all streams [" << std::endl;
- for (size_t s = 0; s < ice_ctx_->GetStreamCount(); ++s) {
- if (!ice_ctx_->GetStream(s)) {
+ for (size_t s = 0; s < ice_ctx_->ctx()->GetStreamCount(); ++s) {
+ if (!ice_ctx_->ctx()->GetStream(s)) {
continue;
}
- DumpCandidatePairs_s(ice_ctx_->GetStream(s).get());
+ DumpCandidatePairs_s(ice_ctx_->ctx()->GetStream(s).get());
}
std::cerr << "]" << std::endl;
}
bool CandidatePairsPriorityDescending(const std::vector<NrIceCandidatePair>&
pairs) {
// Verify that priority is descending
uint64_t priority = std::numeric_limits<uint64_t>::max();
@@ -1120,89 +1178,94 @@ class IceTestPeer : public sigslot::has_
}
ASSERT_TRUE(removed_pairs.empty()) << "At least one candidate pair has "
"gone missing.";
}
void StreamReady(NrIceMediaStream *stream) {
++ready_ct_;
- std::cerr << "Stream ready for " << stream->name() << " ct=" << ready_ct_ << std::endl;
+ std::cerr << name_ << " Stream ready for " << stream->name()
+ << " ct=" << ready_ct_ << std::endl;
DumpCandidatePairs_s(stream);
}
void StreamFailed(NrIceMediaStream *stream) {
- std::cerr << "Stream failed for " << stream->name() << " ct=" << ready_ct_ << std::endl;
+ std::cerr << name_ << " Stream failed for " << stream->name()
+ << " ct=" << ready_ct_ << std::endl;
DumpCandidatePairs_s(stream);
}
void ConnectionStateChange(NrIceCtx* ctx,
NrIceCtx::ConnectionState state) {
(void)ctx;
switch (state) {
case NrIceCtx::ICE_CTX_INIT:
break;
case NrIceCtx::ICE_CTX_CHECKING:
- std::cerr << "ICE reached checking for " << name_ << std::endl;
+ std::cerr << name_ << " ICE reached checking " << std::endl;
ice_reached_checking_ = true;
break;
case NrIceCtx::ICE_CTX_OPEN:
- std::cerr << "ICE completed for " << name_ << std::endl;
+ std::cerr << name_ << " ICE completed " << std::endl;
ice_complete_ = true;
break;
case NrIceCtx::ICE_CTX_FAILED:
break;
}
}
void PacketReceived(NrIceMediaStream *stream, int component, const unsigned char *data,
int len) {
std::cerr << name_ << ": received " << len << " bytes" << std::endl;
++received_;
}
void SendPacket(int stream, int component, const unsigned char *data,
int len) {
- if (!ice_ctx_->GetStream(stream)) {
+ RefPtr<NrIceMediaStream> media_stream = ice_ctx_->ctx()->GetStream(stream);
+ if (!media_stream) {
ADD_FAILURE() << "No such stream " << stream;
return;
}
- ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->GetStream(stream)->SendPacket(component, data, len)));
+ ASSERT_TRUE(NS_SUCCEEDED(media_stream->SendPacket(component, data, len)));
++sent_;
std::cerr << name_ << ": sent " << len << " bytes" << std::endl;
}
void SetCandidateFilter(CandidateFilter filter) {
candidate_filter_ = filter;
}
void ParseCandidate_s(size_t i, const std::string& candidate) {
- ASSERT_TRUE(ice_ctx_->GetStream(i).get()) << "No such stream " << i;
+ ASSERT_TRUE(ice_ctx_->ctx()->GetStream(i).get()) << "No such stream " << i;
std::vector<std::string> attributes;
attributes.push_back(candidate);
- ice_ctx_->GetStream(i)->ParseAttributes(attributes);
+ ice_ctx_->ctx()->GetStream(i)->ParseAttributes(attributes);
}
void ParseCandidate(size_t i, const std::string& candidate)
{
test_utils_->sts_target()->Dispatch(
WrapRunnable(this,
&IceTestPeer::ParseCandidate_s,
i,
candidate),
NS_DISPATCH_SYNC);
}
void DisableComponent_s(size_t stream, int component_id) {
- ASSERT_LT(stream, ice_ctx_->GetStreamCount());
- ASSERT_TRUE(ice_ctx_->GetStream(stream).get()) << "No such stream " << stream;
- nsresult res = ice_ctx_->GetStream(stream)->DisableComponent(component_id);
+ ASSERT_LT(stream, ice_ctx_->ctx()->GetStreamCount());
+ ASSERT_TRUE(ice_ctx_->ctx()->GetStream(stream).get()) << "No such stream "
+ << stream;
+ nsresult res =
+ ice_ctx_->ctx()->GetStream(stream)->DisableComponent(component_id);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
void DisableComponent(size_t stream, int component_id)
{
test_utils_->sts_target()->Dispatch(
WrapRunnable(this,
&IceTestPeer::DisableComponent_s,
@@ -1211,33 +1274,33 @@ class IceTestPeer : public sigslot::has_
NS_DISPATCH_SYNC);
}
int trickled() { return trickled_; }
void SetControlling(NrIceCtx::Controlling controlling) {
nsresult res;
test_utils_->sts_target()->Dispatch(
- WrapRunnableRet(&res, ice_ctx_,
+ WrapRunnableRet(&res, ice_ctx_->ctx(),
&NrIceCtx::SetControlling,
controlling),
NS_DISPATCH_SYNC);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
void SetTiebreaker(uint64_t tiebreaker) {
test_utils_->sts_target()->Dispatch(
WrapRunnable(this,
&IceTestPeer::SetTiebreaker_s,
tiebreaker),
NS_DISPATCH_SYNC);
}
void SetTiebreaker_s(uint64_t tiebreaker) {
- ice_ctx_->peer()->tiebreaker = tiebreaker;
+ ice_ctx_->ctx()->peer()->tiebreaker = tiebreaker;
}
void SimulateIceLite() {
simulate_ice_lite_ = true;
SetControlling(NrIceCtx::ICE_CONTROLLED);
}
nsresult GetDefaultCandidate(unsigned int stream, NrIceCandidate* cand) {
@@ -1248,17 +1311,17 @@ class IceTestPeer : public sigslot::has_
&IceTestPeer::GetDefaultCandidate_s,
stream, cand),
NS_DISPATCH_SYNC);
return rv;
}
nsresult GetDefaultCandidate_s(unsigned int stream, NrIceCandidate* cand) {
- return ice_ctx_->GetStream(stream)->GetDefaultCandidate(1, cand);
+ return ice_ctx_->ctx()->GetStream(stream)->GetDefaultCandidate(1, cand);
}
private:
std::string name_;
RefPtr<NrIceCtxHandler> ice_ctx_;
std::map<std::string, std::vector<std::string> > candidates_;
// Maps from stream id to list of remote trickle candidates
std::map<size_t, std::vector<SchedulableTrickleCandidate*> >
@@ -1456,17 +1519,17 @@ class WebRtcIceGatherTest : public StunT
protected:
mozilla::UniquePtr<IceTestPeer> peer_;
};
class WebRtcIceConnectTest : public StunTest {
public:
WebRtcIceConnectTest() :
initted_(false),
- test_stun_server_initedd_(false),
+ test_stun_server_inited_(false),
use_nat_(false),
filtering_type_(TestNat::ENDPOINT_INDEPENDENT),
mapping_type_(TestNat::ENDPOINT_INDEPENDENT),
block_udp_(false) {}
void SetUp() override {
StunTest::SetUp();
@@ -1491,46 +1554,46 @@ class WebRtcIceConnectTest : public Stun
void RemoveStream(size_t index) {
p1_->RemoveStream(index);
p2_->RemoveStream(index);
}
void Init(bool allow_loopback,
bool enable_tcp,
bool default_only = false,
- bool setupStunServers = true) {
+ bool setup_stun_servers = true) {
if (initted_) {
return;
}
p1_ = MakeUnique<IceTestPeer>("P1", test_utils_, true, allow_loopback,
enable_tcp, false, default_only);
p2_ = MakeUnique<IceTestPeer>("P2", test_utils_, false, allow_loopback,
enable_tcp, false, default_only);
- InitPeer(p1_.get(), setupStunServers);
- InitPeer(p2_.get(), setupStunServers);
+ InitPeer(p1_.get(), setup_stun_servers);
+ InitPeer(p2_.get(), setup_stun_servers);
initted_ = true;
}
- void InitPeer(IceTestPeer* peer, bool setupStunServers = true) {
+ void InitPeer(IceTestPeer* peer, bool setup_stun_servers = true) {
if (use_nat_) {
// If we enable nat simulation, but still use a real STUN server somewhere
// on the internet, we will see failures if there is a real NAT in
// addition to our simulated one, particularly if it disallows
// hairpinning.
- if (setupStunServers) {
+ if (setup_stun_servers) {
InitTestStunServer();
peer->UseTestStunServer();
}
peer->UseNat();
peer->SetFilteringType(filtering_type_);
peer->SetMappingType(mapping_type_);
peer->SetBlockUdp(block_udp_);
- } else if (setupStunServers) {
+ } else if (setup_stun_servers) {
std::vector<NrIceStunServer> stun_servers;
stun_servers.push_back(*NrIceStunServer::Create(stun_server_address_,
kDefaultStunServerPort, kNrIceTransportUdp));
stun_servers.push_back(*NrIceStunServer::Create(stun_server_address_,
kDefaultStunServerPort, kNrIceTransportTcp));
peer->SetStunServers(stun_servers);
@@ -1574,29 +1637,29 @@ class WebRtcIceConnectTest : public Stun
void SetMappingType(TestNat::NatBehavior type) {
// to be useful, this method should be called before Init
ASSERT_FALSE(initted_);
mapping_type_ = type;
}
void BlockUdp() {
- // to be useful, this method should be called before Init
- ASSERT_FALSE(initted_);
+ // note: |block_udp_| is used only in InitPeer.
+ // Use IceTestPeer::SetBlockUdp to act on the peer directly.
block_udp_ = true;
}
void InitTestStunServer() {
- if (test_stun_server_initedd_) {
+ if (test_stun_server_inited_) {
return;
}
std::cerr << "Resetting TestStunServer" << std::endl;
TestStunServer::GetInstance(AF_INET)->Reset();
- test_stun_server_initedd_ = true;
+ test_stun_server_inited_ = true;
}
void UseTestStunServer() {
InitTestStunServer();
p1_->UseTestStunServer();
p2_->UseTestStunServer();
}
@@ -1616,44 +1679,44 @@ class WebRtcIceConnectTest : public Stun
void SetCandidateFilter(CandidateFilter filter, bool both=true) {
p1_->SetCandidateFilter(filter);
if (both) {
p2_->SetCandidateFilter(filter);
}
}
void Connect() {
- ConnectCallerAndCallee(p2_.get(), p1_.get());
+ ConnectCallerAndCallee(p1_.get(), p2_.get());
}
void ConnectCallerAndCallee(IceTestPeer* caller, IceTestPeer* callee) {
- ASSERT_TRUE(caller->ready_ct() == 0 &&
- caller->ice_complete() == 0 &&
- caller->ice_reached_checking() == 0);
- ASSERT_TRUE(callee->ready_ct() == 0 &&
- callee->ice_complete() == 0 &&
- callee->ice_reached_checking() == 0);
-
- // IceTestPeer::Connect grabs attributes from the first arg, and gives them
- // to |this|, meaning that p2_->Connect(p1_, ...) simulates p1 sending an
- // offer to p2. Order matters here because it determines which peer is
- // controlling.
+ ASSERT_TRUE(caller->ready_ct() == 0);
+ ASSERT_TRUE(caller->ice_complete() == 0);
+ ASSERT_TRUE(caller->ice_reached_checking() == 0);
+ ASSERT_TRUE(callee->ready_ct() == 0);
+ ASSERT_TRUE(callee->ice_complete() == 0);
+ ASSERT_TRUE(callee->ice_reached_checking() == 0);
+
+ // IceTestPeer::Connect grabs attributes from the first arg, and
+ // gives them to |this|, meaning that callee->Connect(caller, ...)
+ // simulates caller sending an offer to callee. Order matters here
+ // because it determines which peer is controlling.
+ callee->Connect(caller, TRICKLE_NONE);
caller->Connect(callee, TRICKLE_NONE);
- callee->Connect(caller, TRICKLE_NONE);
-
- ASSERT_TRUE_WAIT(callee->ready_ct() == 1 && caller->ready_ct() == 1,
+
+ ASSERT_TRUE_WAIT(caller->ready_ct() == 1 && callee->ready_ct() == 1,
kDefaultTimeout);
- ASSERT_TRUE_WAIT(callee->ice_complete() && caller->ice_complete(),
+ ASSERT_TRUE_WAIT(caller->ice_complete() && callee->ice_complete(),
kDefaultTimeout);
+ ASSERT_TRUE(caller->ice_reached_checking());
ASSERT_TRUE(callee->ice_reached_checking());
- ASSERT_TRUE(caller->ice_reached_checking());
-
+
+ caller->DumpAndCheckActiveCandidates();
callee->DumpAndCheckActiveCandidates();
- caller->DumpAndCheckActiveCandidates();
}
void SetExpectedTypes(NrIceCandidate::Type local, NrIceCandidate::Type remote,
std::string transport = kNrIceTransportUdp) {
p1_->SetExpectedTypes(local, remote, transport);
p2_->SetExpectedTypes(local, remote, transport);
}
@@ -1733,39 +1796,43 @@ class WebRtcIceConnectTest : public Stun
}
// default is p1_ sending to p2_
void SendReceive() {
SendReceive(p1_.get(), p2_.get());
}
void SendReceive(IceTestPeer *p1, IceTestPeer *p2,
- bool expectTxFailure = false,
- bool expectRxFailure = false) {
+ bool expect_tx_failure = false,
+ bool expect_rx_failure = false) {
+ size_t previousSent = p1->sent();
+ size_t previousReceived = p2->received();
+
test_utils_->sts_target()->Dispatch(
WrapRunnable(p1,
&IceTestPeer::SendPacket, 0, 1,
reinterpret_cast<const unsigned char *>("TEST"), 4),
NS_DISPATCH_SYNC);
- if (expectTxFailure) {
- ASSERT_NE(1u, p1->sent());
+
+ if (expect_tx_failure) {
+ ASSERT_EQ(previousSent, p1->sent());
} else {
- ASSERT_EQ(1u, p1->sent());
+ ASSERT_EQ(previousSent+1, p1->sent());
}
- if (expectRxFailure) {
+ if (expect_rx_failure) {
usleep(1000);
- ASSERT_TRUE(p2->received() == 0);
+ ASSERT_EQ(previousReceived, p2->received());
} else {
- ASSERT_TRUE_WAIT(p2->received() == 1, 1000);
+ ASSERT_TRUE_WAIT(p2->received() == previousReceived+1, 1000);
}
}
protected:
bool initted_;
- bool test_stun_server_initedd_;
+ bool test_stun_server_inited_;
nsCOMPtr<nsIEventTarget> target_;
mozilla::UniquePtr<IceTestPeer> p1_;
mozilla::UniquePtr<IceTestPeer> p2_;
bool use_nat_;
TestNat::NatBehavior filtering_type_;
TestNat::NatBehavior mapping_type_;
bool block_udp_;
};
@@ -2289,16 +2356,85 @@ TEST_F(WebRtcIceConnectTest, TestGatherA
TEST_F(WebRtcIceConnectTest, TestConnect) {
AddStream("first", 1);
ASSERT_TRUE(Gather());
Connect();
}
+
+TEST_F(WebRtcIceConnectTest, TestConnectRestartIce) {
+ AddStream("first", 1);
+ ASSERT_TRUE(Gather());
+ Connect();
+ SendReceive(p1_.get(), p2_.get());
+
+ p2_->RestartIce();
+ ASSERT_FALSE(p2_->gathering_complete());
+
+ // verify p1 and p2 streams are still connected after restarting ice on p2
+ SendReceive(p1_.get(), p2_.get());
+
+ mozilla::UniquePtr<IceTestPeer> p3_;
+ p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, false,
+ false, false, false);
+ InitPeer(p3_.get());
+ p3_->AddStream(1);
+
+ p2_->AddStream(1);
+ ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get()));
+ std::cout << "-------------------------------------------------" << std::endl;
+ ConnectCallerAndCallee(p3_.get(), p2_.get());
+ SendReceive(p1_.get(), p2_.get()); // p1 and p2 still connected
+ SendReceive(p3_.get(), p2_.get()); // p3 and p2 are now connected
+
+ p2_->FinalizeIceRestart();
+ SendReceive(p3_.get(), p2_.get()); // p3 and p2 are still connected
+
+ SendReceive(p1_.get(), p2_.get(), false, true); // p1 and p2 not connected
+
+ p3_ = nullptr;
+}
+
+
+TEST_F(WebRtcIceConnectTest, TestConnectRestartIceThenAbort) {
+ AddStream("first", 1);
+ ASSERT_TRUE(Gather());
+ Connect();
+ SendReceive(p1_.get(), p2_.get());
+
+ p2_->RestartIce();
+ ASSERT_FALSE(p2_->gathering_complete());
+
+ // verify p1 and p2 streams are still connected after restarting ice on p2
+ SendReceive(p1_.get(), p2_.get());
+
+ mozilla::UniquePtr<IceTestPeer> p3_;
+ p3_ = MakeUnique<IceTestPeer>("P3", test_utils_, true, false,
+ false, false, false);
+ InitPeer(p3_.get());
+ p3_->AddStream(1);
+
+ p2_->AddStream(1);
+ ASSERT_TRUE(GatherCallerAndCallee(p2_.get(), p3_.get()));
+ std::cout << "-------------------------------------------------" << std::endl;
+ ConnectCallerAndCallee(p3_.get(), p2_.get());
+ SendReceive(p1_.get(), p2_.get()); // p1 and p2 still connected
+ SendReceive(p3_.get(), p2_.get()); // p3 and p2 are now connected
+
+ p2_->RollbackIceRestart();
+ SendReceive(p1_.get(), p2_.get()); // p1 and p2 are still connected
+
+ SendReceive(p3_.get(), p2_.get(), false, true); // p3 and p2 not connected
+
+ p3_ = nullptr;
+}
+
+
TEST_F(WebRtcIceConnectTest, TestConnectTcp) {
Init(false, true);
AddStream("first", 1);
ASSERT_TRUE(Gather());
SetCandidateFilter(IsTcpCandidate);
SetExpectedTypes(NrIceCandidate::Type::ICE_HOST,
NrIceCandidate::Type::ICE_HOST, kNrIceTransportTcp);
Connect();
@@ -3096,17 +3232,17 @@ TEST_F(WebRtcIceConnectTest, TestPollCan
ASSERT_NE(0U, pairs.size());
ASSERT_TRUE(p2_->CandidatePairsPriorityDescending(pairs));
ASSERT_TRUE(ContainsSucceededPair(pairs));
}
TEST_F(WebRtcIceConnectTest, TestHostCandPairingFilter) {
Init(false, false, false, false);
AddStream("first", 1);
- ASSERT_TRUE(Gather(kDefaultTimeout));
+ ASSERT_TRUE(Gather());
SetCandidateFilter(IsIpv4Candidate);
int host_net = p1_->GetCandidatesPrivateIpv4Range(0);
if (host_net <= 0) {
// TODO bug 1226838: make this work with multiple private IPs
FAIL() << "This test needs exactly one private IPv4 host candidate to work" << std::endl;
}
@@ -3130,17 +3266,17 @@ TEST_F(WebRtcIceConnectTest, TestHostCan
// TODO Bug 1226838 - See Comment 2 - this test can't work as written
TEST_F(WebRtcIceConnectTest, DISABLED_TestSrflxCandPairingFilter) {
if (stun_server_address_.empty()) {
return;
}
Init(false, false, false, false);
AddStream("first", 1);
- ASSERT_TRUE(Gather(kDefaultTimeout));
+ ASSERT_TRUE(Gather());
SetCandidateFilter(IsSrflxCandidate);
if (p1_->GetCandidatesPrivateIpv4Range(0) <= 0) {
// TODO bug 1226838: make this work with public IP addresses
std::cerr << "Don't run this test at IETF meetings!" << std::endl;
FAIL() << "This test needs one private IPv4 host candidate to work" << std::endl;
}
--- a/media/mtransport/test/multi_tcp_socket_unittest.cpp
+++ b/media/mtransport/test/multi_tcp_socket_unittest.cpp
@@ -104,26 +104,26 @@ class MultiTcpSocketTest : public Mtrans
int r;
if (!stun_server_addr.empty()) {
std::vector<NrIceStunServer> stun_servers;
UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(
stun_server_addr, stun_server_port, kNrIceTransportTcp));
stun_servers.push_back(*server);
- ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
+ ASSERT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetStunServers(stun_servers)));
}
r = 1;
for (int tries=10; tries && r; --tries) {
r = nr_str_port_to_transport_addr(
(char *)"127.0.0.1", EnsureEphemeral(port_s++), IPPROTO_TCP, &local);
ASSERT_EQ(0, r);
- r = nr_socket_multi_tcp_create(ice_ctx_->ctx(),
+ r = nr_socket_multi_tcp_create(ice_ctx_->ctx()->ctx(),
&local, tcp_type, 1, 2048, sock);
}
ASSERT_EQ(0, r);
printf("Creating socket on %s\n", local.as_string);
r = nr_socket_multi_tcp_set_readable_cb(*sock,
&MultiTcpSocketTest::SockReadable, this);
ASSERT_EQ(0, r);
@@ -343,17 +343,17 @@ class MultiTcpSocketTest : public Mtrans
bool IsReadable() const {
return readable;
}
void SetReadable(bool r) {
readable=r;
}
std::vector<nr_socket *> socks;
Atomic<bool> readable;
- RefPtr<NrIceCtx> ice_ctx_;
+ RefPtr<NrIceCtxHandler> ice_ctx_;
};
}
TEST_F(MultiTcpSocketTest, TestListen) {
socks[0] = Create(TCP_TYPE_PASSIVE);
Listen(socks[0]);
}
--- a/media/mtransport/test/transport_unittests.cpp
+++ b/media/mtransport/test/transport_unittests.cpp
@@ -451,17 +451,17 @@ class TransportTestPeer : public sigslot
enabled_cipersuites_(),
disabled_cipersuites_(),
reuse_dhe_key_(false),
test_utils_(utils) {
std::vector<NrIceStunServer> stun_servers;
UniquePtr<NrIceStunServer> server(NrIceStunServer::Create(
std::string((char *)"stun.services.mozilla.com"), 3478));
stun_servers.push_back(*server);
- EXPECT_TRUE(NS_SUCCEEDED(ice_ctx_->SetStunServers(stun_servers)));
+ EXPECT_TRUE(NS_SUCCEEDED(ice_ctx_->ctx()->SetStunServers(stun_servers)));
dtls_->SetIdentity(identity_);
dtls_->SetRole(name == "P2" ?
TransportLayerDtls::CLIENT :
TransportLayerDtls::SERVER);
nsresult res = identity_->ComputeFingerprint("sha-1",
fingerprint_,
@@ -594,38 +594,38 @@ class TransportTestPeer : public sigslot
peer),
NS_DISPATCH_SYNC);
}
void InitIce() {
nsresult res;
// Attach our slots
- ice_ctx_->SignalGatheringStateChange.
+ ice_ctx_->ctx()->SignalGatheringStateChange.
connect(this, &TransportTestPeer::GatheringStateChange);
char name[100];
snprintf(name, sizeof(name), "%s:stream%d", name_.c_str(),
(int)streams_.size());
// Create the media stream
RefPtr<NrIceMediaStream> stream =
ice_ctx_->CreateStream(static_cast<char *>(name), 1);
ASSERT_TRUE(stream != nullptr);
- ice_ctx_->SetStream(streams_.size(), stream);
+ ice_ctx_->ctx()->SetStream(streams_.size(), stream);
streams_.push_back(stream);
// Listen for candidates
stream->SignalCandidate.
connect(this, &TransportTestPeer::GotCandidate);
// Create the transport layer
ice_ = new TransportLayerIce(name);
- ice_->SetParameters(ice_ctx_, stream, 1);
+ ice_->SetParameters(ice_ctx_->ctx(), stream, 1);
// Assemble the stack
nsAutoPtr<std::queue<mozilla::TransportLayer *> > layers(
new std::queue<mozilla::TransportLayer *>);
layers->push(ice_);
layers->push(dtls_);
test_utils_->sts_target()->Dispatch(
@@ -635,17 +635,17 @@ class TransportTestPeer : public sigslot
ASSERT_EQ((nsresult)NS_OK, res);
// Listen for media events
flow_->SignalPacketReceived.connect(this, &TransportTestPeer::PacketReceived);
flow_->SignalStateChange.connect(this, &TransportTestPeer::StateChanged);
// Start gathering
test_utils_->sts_target()->Dispatch(
- WrapRunnableRet(&res, ice_ctx_, &NrIceCtx::StartGathering),
+ WrapRunnableRet(&res, ice_ctx_->ctx(), &NrIceCtx::StartGathering),
NS_DISPATCH_SYNC);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
void ConnectIce(TransportTestPeer *peer) {
peer_ = peer;
// If gathering is already complete, push the candidates over
@@ -675,33 +675,33 @@ class TransportTestPeer : public sigslot
// Don't send to the other side
if (!peer_) {
gathering_complete_ = true;
return;
}
// First send attributes
test_utils_->sts_target()->Dispatch(
- WrapRunnableRet(&res, peer_->ice_ctx_,
+ WrapRunnableRet(&res, peer_->ice_ctx_->ctx(),
&NrIceCtx::ParseGlobalAttributes,
- ice_ctx_->GetGlobalAttributes()),
+ ice_ctx_->ctx()->GetGlobalAttributes()),
NS_DISPATCH_SYNC);
ASSERT_TRUE(NS_SUCCEEDED(res));
for (size_t i=0; i<streams_.size(); ++i) {
test_utils_->sts_target()->Dispatch(
WrapRunnableRet(&res, peer_->streams_[i], &NrIceMediaStream::ParseAttributes,
candidates_[streams_[i]->name()]), NS_DISPATCH_SYNC);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
// Start checks on the other peer.
test_utils_->sts_target()->Dispatch(
- WrapRunnableRet(&res, peer_->ice_ctx_, &NrIceCtx::StartChecks),
+ WrapRunnableRet(&res, peer_->ice_ctx_->ctx(), &NrIceCtx::StartChecks),
NS_DISPATCH_SYNC);
ASSERT_TRUE(NS_SUCCEEDED(res));
}
TransportResult SendPacket(const unsigned char* data, size_t len) {
TransportResult ret;
test_utils_->sts_target()->Dispatch(
WrapRunnableRet(&ret, flow_, &TransportFlow::SendPacket, data, len),
--- a/media/mtransport/test/turn_unittest.cpp
+++ b/media/mtransport/test/turn_unittest.cpp
@@ -97,17 +97,17 @@ class TurnClient : public MtransportTest
received_(0),
protocol_(IPPROTO_UDP) {
}
~TurnClient() {
}
static void SetUpTestCase() {
- NrIceCtx::InitializeCryptoAndLogging(false, false, false);
+ NrIceCtx::InitializeGlobals(false, false, false);
}
void SetTcp() {
protocol_ = IPPROTO_TCP;
}
void Init_s() {
int r;
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.c
@@ -52,16 +52,18 @@ static char *RCSSTRING __UNUSED__="$Id:
#include "stun.h"
#include "ice_ctx.h"
#include "ice_reg.h"
#include "nr_crypto.h"
#include "async_timer.h"
#include "util.h"
#include "nr_socket_local.h"
+#define ICE_UFRAG_LEN 8
+#define ICE_PWD_LEN 32
int LOG_ICE = 0;
static int nr_ice_random_string(char *str, int len);
static int nr_ice_fetch_stun_servers(int ct, nr_ice_stun_server **out);
#ifdef USE_TURN
static int nr_ice_fetch_turn_servers(int ct, nr_ice_turn_server **out);
#endif /* USE_TURN */
@@ -311,38 +313,55 @@ int nr_ice_fetch_turn_servers(int ct, nr
if (_status) RFREE(servers);
return(_status);
}
#endif /* USE_TURN */
#define MAXADDRS 100 /* Ridiculously high */
int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp)
{
+ int r,_status;
+ char *ufrag = 0;
+ char *pwd = 0;
+
+ if (r=nr_ice_get_new_ice_ufrag(&ufrag))
+ ABORT(r);
+ if (r=nr_ice_get_new_ice_pwd(&pwd))
+ ABORT(r);
+
+ if (r=nr_ice_ctx_create_with_credentials(label, flags, ufrag, pwd, ctxp))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ RFREE(ufrag);
+ RFREE(pwd);
+
+ return(_status);
+ }
+
+int nr_ice_ctx_create_with_credentials(char *label, UINT4 flags, char *ufrag, char *pwd, nr_ice_ctx **ctxp)
+ {
nr_ice_ctx *ctx=0;
int r,_status;
- char buf[100];
if(r=r_log_register("ice", &LOG_ICE))
ABORT(r);
if(!(ctx=RCALLOC(sizeof(nr_ice_ctx))))
ABORT(R_NO_MEMORY);
ctx->flags=flags;
if(!(ctx->label=r_strdup(label)))
ABORT(R_NO_MEMORY);
- if(r=nr_ice_random_string(buf,8))
- ABORT(r);
- if(!(ctx->ufrag=r_strdup(buf)))
+ if(!(ctx->ufrag=r_strdup(ufrag)))
ABORT(r);
- if(r=nr_ice_random_string(buf,32))
- ABORT(r);
- if(!(ctx->pwd=r_strdup(buf)))
+ if(!(ctx->pwd=r_strdup(pwd)))
ABORT(r);
/* Get the STUN servers */
if(r=NR_reg_get_child_count(NR_ICE_REG_STUN_SRV_PRFX,
(unsigned int *)&ctx->stun_server_ct)||ctx->stun_server_ct==0) {
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s): No STUN servers specified in nICEr registry", ctx->label);
ctx->stun_server_ct=0;
}
@@ -947,8 +966,47 @@ int nr_ice_ctx_hide_candidate(nr_ice_ctx
if (ctx->flags & NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS) {
if (cand->type == HOST)
return 1;
}
return 0;
}
+
+int nr_ice_get_new_ice_ufrag(char** ufrag)
+ {
+ int r,_status;
+ char buf[ICE_UFRAG_LEN+1];
+
+ if(r=nr_ice_random_string(buf,ICE_UFRAG_LEN))
+ ABORT(r);
+ if(!(*ufrag=r_strdup(buf)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if(_status) {
+ RFREE(*ufrag);
+ *ufrag = 0;
+ }
+ return(_status);
+ }
+
+int nr_ice_get_new_ice_pwd(char** pwd)
+ {
+ int r,_status;
+ char buf[ICE_PWD_LEN+1];
+
+ if(r=nr_ice_random_string(buf,ICE_PWD_LEN))
+ ABORT(r);
+ if(!(*pwd=r_strdup(buf)))
+ ABORT(r);
+
+ _status=0;
+ abort:
+ if(_status) {
+ RFREE(*pwd);
+ *pwd = 0;
+ }
+ return(_status);
+ }
+
--- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h
@@ -150,16 +150,17 @@ struct nr_ice_ctx_ {
nr_ice_trickle_candidate_cb trickle_cb;
void *trickle_cb_arg;
char force_net_interface[MAXIFNAME];
};
int nr_ice_ctx_create(char *label, UINT4 flags, nr_ice_ctx **ctxp);
+int nr_ice_ctx_create_with_credentials(char *label, UINT4 flags, char* ufrag, char* pwd, nr_ice_ctx **ctxp);
#define NR_ICE_CTX_FLAGS_OFFERER 1
#define NR_ICE_CTX_FLAGS_ANSWERER (1<<1)
#define NR_ICE_CTX_FLAGS_AGGRESSIVE_NOMINATION (1<<2)
#define NR_ICE_CTX_FLAGS_LITE (1<<3)
#define NR_ICE_CTX_FLAGS_RELAY_ONLY (1<<4)
#define NR_ICE_CTX_FLAGS_ONLY_DEFAULT_ADDRS (1<<5)
int nr_ice_ctx_destroy(nr_ice_ctx **ctxp);
@@ -176,16 +177,18 @@ int nr_ice_ctx_finalize(nr_ice_ctx *ctx,
int nr_ice_ctx_set_stun_servers(nr_ice_ctx *ctx,nr_ice_stun_server *servers, int ct);
int nr_ice_ctx_set_turn_servers(nr_ice_ctx *ctx,nr_ice_turn_server *servers, int ct);
int nr_ice_ctx_set_resolver(nr_ice_ctx *ctx, nr_resolver *resolver);
int nr_ice_ctx_set_interface_prioritizer(nr_ice_ctx *ctx, nr_interface_prioritizer *prioritizer);
int nr_ice_ctx_set_turn_tcp_socket_wrapper(nr_ice_ctx *ctx, nr_socket_wrapper_factory *wrapper);
void nr_ice_ctx_set_socket_factory(nr_ice_ctx *ctx, nr_socket_factory *factory);
int nr_ice_ctx_set_trickle_cb(nr_ice_ctx *ctx, nr_ice_trickle_candidate_cb cb, void *cb_arg);
int nr_ice_ctx_hide_candidate(nr_ice_ctx *ctx, nr_ice_candidate *cand);
+int nr_ice_get_new_ice_ufrag(char** ufrag);
+int nr_ice_get_new_ice_pwd(char** pwd);
#define NR_ICE_MAX_ATTRIBUTE_SIZE 256
extern int LOG_ICE;
#ifdef __cplusplus
}
#endif /* __cplusplus */
--- a/media/mtransport/transportlayerice.cpp
+++ b/media/mtransport/transportlayerice.cpp
@@ -80,48 +80,106 @@ namespace mozilla {
#ifdef ERROR
#undef ERROR
#endif
MOZ_MTLOG_MODULE("mtransport")
TransportLayerIce::TransportLayerIce(const std::string& name)
- : name_(name), ctx_(nullptr), stream_(nullptr), component_(0) {}
+ : name_(name),
+ ctx_(nullptr), stream_(nullptr), component_(0),
+ old_stream_(nullptr)
+{
+ // setup happens later
+}
TransportLayerIce::~TransportLayerIce() {
// No need to do anything here, since we use smart pointers
}
void TransportLayerIce::SetParameters(RefPtr<NrIceCtx> ctx,
RefPtr<NrIceMediaStream> stream,
int component) {
+ // If SetParameters is called and we already have a stream_, this means
+ // we're handling an ICE restart. We need to hold the old stream until
+ // we know the new stream is working.
+ if (stream_ && !old_stream_) {
+ MOZ_ASSERT(stream_ != stream); // make sure we're getting a different stream
+
+ // Here we leave the old stream's signals connected until we don't need
+ // it anymore. They will be disconnected if ice restart is successful.
+ old_stream_ = stream_;
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "SetParameters save old stream("
+ << old_stream_->name() << ")");
+ }
+
ctx_ = ctx;
stream_ = stream;
component_ = component;
PostSetup();
}
void TransportLayerIce::PostSetup() {
target_ = ctx_->thread();
stream_->SignalReady.connect(this, &TransportLayerIce::IceReady);
stream_->SignalFailed.connect(this, &TransportLayerIce::IceFailed);
stream_->SignalPacketReceived.connect(this,
&TransportLayerIce::IcePacketReceived);
if (stream_->state() == NrIceMediaStream::ICE_OPEN) {
TL_SET_STATE(TS_OPEN);
+ // Reset old ice stream if new stream is good
+ ResetOldStream();
}
}
+void TransportLayerIce::ResetOldStream() {
+ if (old_stream_ == nullptr) {
+ return; // no work to do
+ }
+ // ICE Ready on the new stream, we can forget the old stream now
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "ResetOldStream(" << old_stream_->name()
+ << ")");
+ old_stream_->SignalReady.disconnect(this);
+ old_stream_->SignalFailed.disconnect(this);
+ old_stream_->SignalPacketReceived.disconnect(this);
+ old_stream_ = nullptr;
+}
+
+void TransportLayerIce::RestoreOldStream() {
+ if (old_stream_ == nullptr) {
+ return; // no work to do
+ }
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "RestoreOldStream(" << old_stream_->name()
+ << ")");
+ stream_->SignalReady.disconnect(this);
+ stream_->SignalFailed.disconnect(this);
+ stream_->SignalPacketReceived.disconnect(this);
+ stream_ = old_stream_;
+ old_stream_ = nullptr;
+
+ if (stream_->state() == NrIceMediaStream::ICE_OPEN) {
+ IceReady(stream_);
+ } else if (stream_->state() == NrIceMediaStream::ICE_CLOSED) {
+ IceFailed(stream_);
+ }
+ // No events are fired when the stream is ICE_CONNECTING. If the
+ // restored stream is ICE_CONNECTING, IceReady/IceFailed will fire
+ // later.
+}
+
TransportResult TransportLayerIce::SendPacket(const unsigned char *data,
size_t len) {
CheckThread();
- nsresult res = stream_->SendPacket(component_, data, len);
+ // use old_stream_ until stream_ is ready
+ nsresult res = (old_stream_?old_stream_:stream_)->SendPacket(component_,
+ data,
+ len);
if (!NS_SUCCEEDED(res)) {
return (res == NS_BASE_STREAM_WOULD_BLOCK) ?
TE_WOULDBLOCK : TE_ERROR;
}
MOZ_MTLOG(ML_DEBUG, LAYER_INFO << " SendPacket(" << len << ") succeeded");
@@ -131,21 +189,35 @@ TransportResult TransportLayerIce::SendP
void TransportLayerIce::IceCandidate(NrIceMediaStream *stream,
const std::string&) {
// NO-OP for now
}
void TransportLayerIce::IceReady(NrIceMediaStream *stream) {
CheckThread();
+ // only handle the current stream (not the old stream during restart)
+ if (stream != stream_) {
+ return;
+ }
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "ICE Ready(" << stream->name() << ","
+ << component_ << ")");
TL_SET_STATE(TS_OPEN);
+ // Reset old ice stream if new stream is good after ice restart
+ ResetOldStream();
}
void TransportLayerIce::IceFailed(NrIceMediaStream *stream) {
CheckThread();
+ // only handle the current stream (not the old stream during restart)
+ if (stream != stream_) {
+ return;
+ }
+ MOZ_MTLOG(ML_INFO, LAYER_INFO << "ICE Failed(" << stream->name() << ","
+ << component_ << ")");
TL_SET_STATE(TS_ERROR);
}
void TransportLayerIce::IcePacketReceived(NrIceMediaStream *stream, int component,
const unsigned char *data, int len) {
CheckThread();
// We get packets for both components, so ignore the ones that aren't
// for us.
--- a/media/mtransport/transportlayerice.h
+++ b/media/mtransport/transportlayerice.h
@@ -48,17 +48,22 @@ class TransportLayerIce : public Transpo
void IcePacketReceived(NrIceMediaStream *stream, int component,
const unsigned char *data, int len);
TRANSPORT_LAYER_ID("ice")
private:
DISALLOW_COPY_ASSIGN(TransportLayerIce);
void PostSetup();
+ void ResetOldStream(); // called after successful ice restart
+ void RestoreOldStream(); // called after unsuccessful ice restart
const std::string name_;
RefPtr<NrIceCtx> ctx_;
RefPtr<NrIceMediaStream> stream_;
int component_;
+
+ // used to hold the old stream
+ RefPtr<NrIceMediaStream> old_stream_;
};
} // close namespace
#endif
--- a/media/webrtc/signaling/src/jsep/JsepSession.h
+++ b/media/webrtc/signaling/src/jsep/JsepSession.h
@@ -39,16 +39,17 @@ enum JsepSdpType {
kJsepSdpRollback
};
struct JsepOAOptions {};
struct JsepOfferOptions : public JsepOAOptions {
Maybe<size_t> mOfferToReceiveAudio;
Maybe<size_t> mOfferToReceiveVideo;
Maybe<bool> mDontOfferDataChannel;
+ Maybe<bool> mIceRestart; // currently ignored by JsepSession
};
struct JsepAnswerOptions : public JsepOAOptions {};
enum JsepBundlePolicy {
kBundleBalanced,
kBundleMaxCompat,
kBundleMaxBundle
};
@@ -79,18 +80,21 @@ public:
GetNegotiations() const
{
return mNegotiations;
}
// Set up the ICE And DTLS data.
virtual nsresult SetIceCredentials(const std::string& ufrag,
const std::string& pwd) = 0;
+ virtual const std::string& GetUfrag() const = 0;
+ virtual const std::string& GetPwd() const = 0;
virtual nsresult SetBundlePolicy(JsepBundlePolicy policy) = 0;
virtual bool RemoteIsIceLite() const = 0;
+ virtual bool RemoteIceIsRestarting() const = 0;
virtual std::vector<std::string> GetIceOptions() const = 0;
virtual nsresult AddDtlsFingerprint(const std::string& algorithm,
const std::vector<uint8_t>& value) = 0;
virtual nsresult AddAudioRtpExtension(const std::string& extensionName) = 0;
virtual nsresult AddVideoRtpExtension(const std::string& extensionName) = 0;
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -660,17 +660,20 @@ JsepSessionImpl::CreateOffer(const JsepO
// Ensure that we have all the m-sections we need, and disable extras
rv = SetupOfferMSections(options, sdp.get());
NS_ENSURE_SUCCESS(rv, rv);
SetupBundle(sdp.get());
if (mCurrentLocalDescription) {
- rv = CopyPreviousTransportParams(*GetAnswer(), *sdp, sdp.get());
+ rv = CopyPreviousTransportParams(*GetAnswer(),
+ *mCurrentLocalDescription,
+ *sdp,
+ sdp.get());
NS_ENSURE_SUCCESS(rv,rv);
}
*offer = sdp->ToString();
mGeneratedLocalDescription = Move(sdp);
++mSessionVersion;
return NS_OK;
@@ -798,17 +801,21 @@ JsepSessionImpl::CreateAnswer(const Jsep
for (size_t i = 0; i < numMsections; ++i) {
const SdpMediaSection& remoteMsection = offer.GetMediaSection(i);
rv = CreateAnswerMSection(options, i, remoteMsection, sdp.get());
NS_ENSURE_SUCCESS(rv, rv);
}
if (mCurrentLocalDescription) {
- rv = CopyPreviousTransportParams(*GetAnswer(), *sdp, sdp.get());
+ // per discussion with bwc, 3rd parm here should be offer, not *sdp. (mjf)
+ rv = CopyPreviousTransportParams(*GetAnswer(),
+ *mCurrentRemoteDescription,
+ offer,
+ sdp.get());
NS_ENSURE_SUCCESS(rv,rv);
}
*answer = sdp->ToString();
mGeneratedLocalDescription = Move(sdp);
return NS_OK;
}
@@ -1203,16 +1210,38 @@ JsepSessionImpl::SetRemoteDescription(Js
NS_ENSURE_SUCCESS(rv, rv);
rv = ValidateRemoteDescription(*parsed);
NS_ENSURE_SUCCESS(rv, rv);
bool iceLite =
parsed->GetAttributeList().HasAttribute(SdpAttribute::kIceLiteAttribute);
+ // check for mismatch ufrag/pwd indicating ice restart
+ // can't just check the first one because it might be disabled
+ bool iceRestarting = false;
+ if (mCurrentRemoteDescription.get()) {
+ for (size_t i = 0;
+ !iceRestarting &&
+ i < mCurrentRemoteDescription->GetMediaSectionCount();
+ ++i) {
+
+ const SdpMediaSection& newMsection = parsed->GetMediaSection(i);
+ const SdpMediaSection& oldMsection =
+ mCurrentRemoteDescription->GetMediaSection(i);
+
+ if (mSdpHelper.MsectionIsDisabled(newMsection) ||
+ mSdpHelper.MsectionIsDisabled(oldMsection)) {
+ continue;
+ }
+
+ iceRestarting = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
+ }
+ }
+
std::vector<std::string> iceOptions;
if (parsed->GetAttributeList().HasAttribute(
SdpAttribute::kIceOptionsAttribute)) {
iceOptions = parsed->GetAttributeList().GetIceOptions().mValues;
}
switch (type) {
case kJsepSdpOffer:
@@ -1224,16 +1253,17 @@ JsepSessionImpl::SetRemoteDescription(Js
break;
case kJsepSdpRollback:
MOZ_CRASH(); // Handled above
}
if (NS_SUCCEEDED(rv)) {
mRemoteIsIceLite = iceLite;
mIceOptions = iceOptions;
+ mRemoteIceIsRestarting = iceRestarting;
}
return rv;
}
nsresult
JsepSessionImpl::HandleNegotiatedSession(const UniquePtr<Sdp>& local,
const UniquePtr<Sdp>& remote)
@@ -1515,22 +1545,28 @@ JsepSessionImpl::AddTransportAttributes(
msection->GetAttributeList().SetAttribute(new SdpSetupAttribute(dtlsRole));
return NS_OK;
}
nsresult
JsepSessionImpl::CopyPreviousTransportParams(const Sdp& oldAnswer,
+ const Sdp& offerersPreviousSdp,
const Sdp& newOffer,
Sdp* newLocal)
{
for (size_t i = 0; i < oldAnswer.GetMediaSectionCount(); ++i) {
if (!mSdpHelper.MsectionIsDisabled(newLocal->GetMediaSection(i)) &&
- mSdpHelper.AreOldTransportParamsValid(oldAnswer, newOffer, i)) {
+ mSdpHelper.AreOldTransportParamsValid(oldAnswer,
+ offerersPreviousSdp,
+ newOffer,
+ i) &&
+ !mRemoteIceIsRestarting
+ ) {
// If newLocal is an offer, this will be the number of components we used
// last time, and if it is an answer, this will be the number of
// components we've decided we're using now.
size_t numComponents = mTransports[i]->mComponents;
nsresult rv = mSdpHelper.CopyTransportParams(
numComponents,
mCurrentLocalDescription->GetMediaSection(i),
&newLocal->GetMediaSection(i));
@@ -1836,45 +1872,47 @@ JsepSessionImpl::ValidateRemoteDescripti
SdpHelper::BundledMids bundledMids;
nsresult rv = GetNegotiatedBundledMids(&bundledMids);
NS_ENSURE_SUCCESS(rv, rv);
SdpHelper::BundledMids newBundledMids;
rv = mSdpHelper.GetBundledMids(description, &newBundledMids);
NS_ENSURE_SUCCESS(rv, rv);
+ // check for partial ice restart, which is not supported
+ Maybe<bool> iceCredsDiffer;
for (size_t i = 0;
i < mCurrentRemoteDescription->GetMediaSectionCount();
++i) {
- if (mSdpHelper.MsectionIsDisabled(description.GetMediaSection(i)) ||
- mSdpHelper.MsectionIsDisabled(mCurrentRemoteDescription->GetMediaSection(i))) {
+
+ const SdpMediaSection& newMsection = description.GetMediaSection(i);
+ const SdpMediaSection& oldMsection =
+ mCurrentRemoteDescription->GetMediaSection(i);
+
+ if (mSdpHelper.MsectionIsDisabled(newMsection) ||
+ mSdpHelper.MsectionIsDisabled(oldMsection)) {
continue;
}
- if (mCurrentRemoteDescription->GetMediaSection(i).GetMediaType() !=
- description.GetMediaSection(i).GetMediaType()) {
+ if (oldMsection.GetMediaType() != newMsection.GetMediaType()) {
JSEP_SET_ERROR("Remote description changes the media type of m-line "
<< i);
return NS_ERROR_INVALID_ARG;
}
- const SdpAttributeList& newAttrs(
- description.GetMediaSection(i).GetAttributeList());
- const SdpAttributeList& oldAttrs(
- mCurrentRemoteDescription->GetMediaSection(i).GetAttributeList());
-
- if ((newAttrs.GetIceUfrag() != oldAttrs.GetIceUfrag()) ||
- (newAttrs.GetIcePwd() != oldAttrs.GetIcePwd())) {
- JSEP_SET_ERROR("ICE restart is unsupported at this time "
+ bool differ = mSdpHelper.IceCredentialsDiffer(newMsection, oldMsection);
+ // Detect whether all the creds are the same or all are different
+ if (!iceCredsDiffer.isSome()) {
+ // for the first msection capture whether creds are different or same
+ iceCredsDiffer = mozilla::Some(differ);
+ } else if (iceCredsDiffer.isSome() && *iceCredsDiffer != differ) {
+ // subsequent msections must match the first sections
+ JSEP_SET_ERROR("Partial ICE restart is unsupported at this time "
"(new remote description changes either the ice-ufrag "
- "or ice-pwd)" <<
- "ice-ufrag (old): " << oldAttrs.GetIceUfrag() <<
- "ice-ufrag (new): " << newAttrs.GetIceUfrag() <<
- "ice-pwd (old): " << oldAttrs.GetIcePwd() <<
- "ice-pwd (new): " << newAttrs.GetIcePwd());
+ "or ice-pwd on fewer than all msections)");
return NS_ERROR_INVALID_ARG;
}
}
return NS_OK;
}
nsresult
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.h
@@ -30,16 +30,17 @@ class JsepSessionImpl : public JsepSessi
{
public:
JsepSessionImpl(const std::string& name, UniquePtr<JsepUuidGenerator> uuidgen)
: JsepSession(name),
mIsOfferer(false),
mWasOffererLastTime(false),
mIceControlling(false),
mRemoteIsIceLite(false),
+ mRemoteIceIsRestarting(false),
mBundlePolicy(kBundleBalanced),
mSessionId(0),
mSessionVersion(0),
mUuidGen(Move(uuidgen)),
mSdpHelper(&mLastError)
{
}
@@ -48,24 +49,32 @@ public:
virtual nsresult AddTrack(const RefPtr<JsepTrack>& track) override;
virtual nsresult RemoveTrack(const std::string& streamId,
const std::string& trackId) override;
virtual nsresult SetIceCredentials(const std::string& ufrag,
const std::string& pwd) override;
+ virtual const std::string& GetUfrag() const override { return mIceUfrag; }
+ virtual const std::string& GetPwd() const override { return mIcePwd; }
nsresult SetBundlePolicy(JsepBundlePolicy policy) override;
virtual bool
RemoteIsIceLite() const override
{
return mRemoteIsIceLite;
}
+ virtual bool
+ RemoteIceIsRestarting() const override
+ {
+ return mRemoteIceIsRestarting;
+ }
+
virtual std::vector<std::string>
GetIceOptions() const override
{
return mIceOptions;
}
virtual nsresult AddDtlsFingerprint(const std::string& algorithm,
const std::vector<uint8_t>& value) override;
@@ -217,16 +226,17 @@ private:
const Sdp& sdp,
const SdpMediaSection& msection,
RefPtr<JsepTrack>* track);
nsresult HandleNegotiatedSession(const UniquePtr<Sdp>& local,
const UniquePtr<Sdp>& remote);
nsresult AddTransportAttributes(SdpMediaSection* msection,
SdpSetupAttribute::Role dtlsRole);
nsresult CopyPreviousTransportParams(const Sdp& oldAnswer,
+ const Sdp& offerersPreviousSdp,
const Sdp& newOffer,
Sdp* newLocal);
nsresult SetupOfferMSections(const JsepOfferOptions& options, Sdp* sdp);
// Non-const so it can assign m-line index to tracks
nsresult SetupOfferMSectionsByType(SdpMediaSection::MediaType type,
Maybe<size_t> offerToReceive,
Sdp* sdp);
nsresult BindLocalTracks(SdpMediaSection::MediaType mediatype,
@@ -297,16 +307,17 @@ private:
std::vector<JsepTrackPair> mNegotiatedTrackPairs;
bool mIsOfferer;
bool mWasOffererLastTime;
bool mIceControlling;
std::string mIceUfrag;
std::string mIcePwd;
bool mRemoteIsIceLite;
+ bool mRemoteIceIsRestarting;
std::vector<std::string> mIceOptions;
JsepBundlePolicy mBundlePolicy;
std::vector<JsepDtlsFingerprint> mDtlsFingerprints;
uint64_t mSessionId;
uint64_t mSessionVersion;
std::vector<SdpExtmapAttributeList::Extmap> mAudioRtpExtensions;
std::vector<SdpExtmapAttributeList::Extmap> mVideoRtpExtensions;
UniquePtr<JsepUuidGenerator> mUuidGen;
--- a/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
+++ b/media/webrtc/signaling/src/peerconnection/MediaPipelineFactory.cpp
@@ -183,40 +183,69 @@ NegotiatedDetailsToVideoCodecConfigs(con
static void
FinalizeTransportFlow_s(RefPtr<PeerConnectionMedia> aPCMedia,
RefPtr<TransportFlow> aFlow, size_t aLevel,
bool aIsRtcp,
nsAutoPtr<PtrVector<TransportLayer> > aLayerList)
{
TransportLayerIce* ice =
static_cast<TransportLayerIce*>(aLayerList->values.front());
- ice->SetParameters(
- aPCMedia->ice_ctx(), aPCMedia->ice_media_stream(aLevel), aIsRtcp ? 2 : 1);
+ ice->SetParameters(aPCMedia->ice_ctx(),
+ aPCMedia->ice_media_stream(aLevel),
+ aIsRtcp ? 2 : 1);
nsAutoPtr<std::queue<TransportLayer*> > layerQueue(
new std::queue<TransportLayer*>);
for (auto i = aLayerList->values.begin(); i != aLayerList->values.end();
++i) {
layerQueue->push(*i);
}
aLayerList->values.clear();
(void)aFlow->PushLayers(layerQueue); // TODO(bug 854518): Process errors.
}
+static void
+AddNewIceStreamForRestart_s(RefPtr<PeerConnectionMedia> aPCMedia,
+ RefPtr<TransportFlow> aFlow,
+ size_t aLevel,
+ bool aIsRtcp)
+{
+ TransportLayerIce* ice =
+ static_cast<TransportLayerIce*>(aFlow->GetLayer("ice"));
+ ice->SetParameters(aPCMedia->ice_ctx(),
+ aPCMedia->ice_media_stream(aLevel),
+ aIsRtcp ? 2 : 1);
+}
+
nsresult
MediaPipelineFactory::CreateOrGetTransportFlow(
size_t aLevel,
bool aIsRtcp,
const JsepTransport& aTransport,
RefPtr<TransportFlow>* aFlowOutparam)
{
nsresult rv;
RefPtr<TransportFlow> flow;
flow = mPCMedia->GetTransportFlow(aLevel, aIsRtcp);
if (flow) {
+ if (mPCMedia->ice_ctx_hdlr()->IsRestarting()) {
+ MOZ_MTLOG(ML_INFO, "Flow[" << flow->id() << "]: "
+ << "detected ICE restart - level: "
+ << aLevel << " rtcp: " << aIsRtcp);
+
+ rv = mPCMedia->GetSTSThread()->Dispatch(
+ WrapRunnableNM(AddNewIceStreamForRestart_s,
+ mPCMedia, flow, aLevel, aIsRtcp),
+ NS_DISPATCH_NORMAL);
+ if (NS_FAILED(rv)) {
+ MOZ_MTLOG(ML_ERROR, "Failed to dispatch AddNewIceStreamForRestart_s");
+ return rv;
+ }
+ }
+
*aFlowOutparam = flow;
return NS_OK;
}
std::ostringstream osId;
osId << mPC->GetHandle() << ":" << aLevel << ","
<< (aIsRtcp ? "rtcp" : "rtp");
flow = new TransportFlow(osId.str());
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -1518,26 +1518,32 @@ PeerConnectionImpl::NotifyDataChannel(al
#endif
}
NS_IMETHODIMP
PeerConnectionImpl::CreateOffer(const RTCOfferOptions& aOptions)
{
JsepOfferOptions options;
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ // convert the RTCOfferOptions to JsepOfferOptions
if (aOptions.mOfferToReceiveAudio.WasPassed()) {
options.mOfferToReceiveAudio =
mozilla::Some(size_t(aOptions.mOfferToReceiveAudio.Value()));
}
if (aOptions.mOfferToReceiveVideo.WasPassed()) {
options.mOfferToReceiveVideo =
mozilla::Some(size_t(aOptions.mOfferToReceiveVideo.Value()));
}
+ if (aOptions.mIceRestart.WasPassed()) {
+ options.mIceRestart =
+ mozilla::Some(aOptions.mIceRestart.Value());
+ }
+
if (aOptions.mMozDontOfferDataChannel.WasPassed()) {
options.mDontOfferDataChannel =
mozilla::Some(aOptions.mMozDontOfferDataChannel.Value());
}
#endif
return CreateOffer(options);
}
@@ -1554,33 +1560,50 @@ static void DeferredCreateOffer(const st
}
}
// Used by unit tests and the IDL CreateOffer.
NS_IMETHODIMP
PeerConnectionImpl::CreateOffer(const JsepOfferOptions& aOptions)
{
PC_AUTO_ENTER_API_CALL(true);
+ bool restartIce = aOptions.mIceRestart.isSome() && *(aOptions.mIceRestart);
+ if (!restartIce && mMedia->ice_ctx_hdlr()->IsRestarting()) {
+ RollbackIceRestart();
+ }
RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
if (!pco) {
return NS_OK;
}
if (!PeerConnectionCtx::GetInstance()->isReady()) {
// Uh oh. We're not ready yet. Enqueue this operation.
PeerConnectionCtx::GetInstance()->queueJSEPOperation(
WrapRunnableNM(DeferredCreateOffer, mHandle, aOptions));
STAMP_TIMECARD(mTimeCard, "Deferring CreateOffer (not ready)");
return NS_OK;
}
CSFLogDebug(logTag, "CreateOffer()");
- nsresult nrv = ConfigureJsepSessionCodecs();
+ nsresult nrv;
+ if (aOptions.mIceRestart.isSome() && *(aOptions.mIceRestart) &&
+ !mMedia->ice_ctx_hdlr()->IsRestarting()) {
+ CSFLogInfo(logTag, "Offerer restarting ice");
+ nrv = SetupIceRestart();
+ if (NS_FAILED(nrv)) {
+ CSFLogError(logTag, "%s: SetupIceRestart failed, res=%u",
+ __FUNCTION__,
+ static_cast<unsigned>(nrv));
+ return nrv;
+ }
+ }
+
+ nrv = ConfigureJsepSessionCodecs();
if (NS_FAILED(nrv)) {
CSFLogError(logTag, "Failed to configure codecs");
return nrv;
}
STAMP_TIMECARD(mTimeCard, "Create Offer");
std::string offer;
@@ -1615,23 +1638,36 @@ PeerConnectionImpl::CreateAnswer()
PC_AUTO_ENTER_API_CALL(true);
RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver);
if (!pco) {
return NS_OK;
}
CSFLogDebug(logTag, "CreateAnswer()");
+
+ nsresult nrv;
+ if (mJsepSession->RemoteIceIsRestarting()) {
+ CSFLogInfo(logTag, "Answerer restarting ice");
+ nrv = SetupIceRestart();
+ if (NS_FAILED(nrv)) {
+ CSFLogError(logTag, "%s: SetupIceRestart failed, res=%u",
+ __FUNCTION__,
+ static_cast<unsigned>(nrv));
+ return nrv;
+ }
+ }
+
STAMP_TIMECARD(mTimeCard, "Create Answer");
// TODO(bug 1098015): Once RTCAnswerOptions is standardized, we'll need to
// add it as a param to CreateAnswer, and convert it here.
JsepAnswerOptions options;
std::string answer;
- nsresult nrv = mJsepSession->CreateAnswer(options, &answer);
+ nrv = mJsepSession->CreateAnswer(options, &answer);
JSErrorResult rv;
if (NS_FAILED(nrv)) {
Error error;
switch (nrv) {
case NS_ERROR_UNEXPECTED:
error = kInvalidState;
break;
default:
@@ -1646,16 +1682,69 @@ PeerConnectionImpl::CreateAnswer()
pco->OnCreateAnswerSuccess(ObString(answer.c_str()), rv);
}
UpdateSignalingState();
return NS_OK;
}
+nsresult
+PeerConnectionImpl::SetupIceRestart()
+{
+ if (mMedia->ice_ctx_hdlr()->IsRestarting()) {
+ CSFLogError(logTag, "%s: ICE already restarting",
+ __FUNCTION__);
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ std::string ufrag = mMedia->ice_ctx()->GetNewUfrag();
+ std::string pwd = mMedia->ice_ctx()->GetNewPwd();
+ if (ufrag.empty() || pwd.empty()) {
+ CSFLogError(logTag, "%s: Bad ICE credentials (ufrag:'%s'/pwd:'%s')",
+ __FUNCTION__,
+ ufrag.c_str(), pwd.c_str());
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // hold on to the current ice creds in case of rollback
+ mPreviousIceUfrag = mJsepSession->GetUfrag();
+ mPreviousIcePwd = mJsepSession->GetPwd();
+ mMedia->BeginIceRestart(ufrag, pwd);
+
+ nsresult nrv = mJsepSession->SetIceCredentials(ufrag, pwd);
+ if (NS_FAILED(nrv)) {
+ CSFLogError(logTag, "%s: Couldn't set ICE credentials, res=%u",
+ __FUNCTION__,
+ static_cast<unsigned>(nrv));
+ return nrv;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+PeerConnectionImpl::RollbackIceRestart()
+{
+ mMedia->RollbackIceRestart();
+ // put back the previous ice creds
+ nsresult nrv = mJsepSession->SetIceCredentials(mPreviousIceUfrag,
+ mPreviousIcePwd);
+ if (NS_FAILED(nrv)) {
+ CSFLogError(logTag, "%s: Couldn't set ICE credentials, res=%u",
+ __FUNCTION__,
+ static_cast<unsigned>(nrv));
+ return nrv;
+ }
+ mPreviousIceUfrag = "";
+ mPreviousIcePwd = "";
+
+ return NS_OK;
+}
+
NS_IMETHODIMP
PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
{
PC_AUTO_ENTER_API_CALL(true);
if (!aSDP) {
CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
return NS_ERROR_FAILURE;
@@ -2779,16 +2868,20 @@ PeerConnectionImpl::SetSignalingState_m(
!rollback)) {
mMedia->EnsureTransports(*mJsepSession);
}
mSignalingState = aSignalingState;
bool fireNegotiationNeeded = false;
if (mSignalingState == PCImplSignalingState::SignalingStable) {
+ if (rollback && mMedia->ice_ctx_hdlr()->IsRestarting()) {
+ RollbackIceRestart();
+ }
+
// Either negotiation is done, or we've rolled back. In either case, we
// need to re-evaluate whether further negotiation is required.
mNegotiationNeeded = false;
// 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);
@@ -3070,16 +3163,27 @@ void PeerConnectionImpl::IceConnectionSt
Telemetry::WEBRTC_ICE_ADD_CANDIDATE_ERRORS_GIVEN_FAILURE,
mAddCandidateErrorCount);
}
}
#endif
mIceConnectionState = domState;
+ if (mIceConnectionState == PCImplIceConnectionState::Connected ||
+ mIceConnectionState == PCImplIceConnectionState::Completed ||
+ mIceConnectionState == PCImplIceConnectionState::Failed) {
+ if (mMedia->ice_ctx_hdlr()->IsRestarting()) {
+ mMedia->FinalizeIceRestart();
+ // clear the previous ice creds since they are no longer needed
+ mPreviousIceUfrag = "";
+ mPreviousIcePwd = "";
+ }
+ }
+
// Would be nice if we had a means of converting one of these dom enums
// to a string that wasn't almost as much text as this switch statement...
switch (mIceConnectionState) {
case PCImplIceConnectionState::New:
STAMP_TIMECARD(mTimeCard, "Ice state: new");
break;
case PCImplIceConnectionState::Checking:
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -706,16 +706,19 @@ private:
SdpMediaSection::MediaType type,
const std::string& streamId,
const std::string& trackId);
nsresult AddTrackToJsepSession(SdpMediaSection::MediaType type,
const std::string& streamId,
const std::string& trackId);
+ nsresult SetupIceRestart();
+ nsresult RollbackIceRestart();
+
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
static void GetStatsForPCObserver_s(
const std::string& pcHandle,
nsAutoPtr<RTCStatsQuery> query);
// Sends an RTCStatsReport to JS. Must run on main thread.
static void DeliverStatsReportToPCObserver_m(
const std::string& pcHandle,
@@ -798,16 +801,18 @@ private:
bool mAllowIceLoopback;
bool mAllowIceLinkLocal;
RefPtr<PeerConnectionMedia> mMedia;
// The JSEP negotiation session.
mozilla::UniquePtr<PCUuidGenerator> mUuidGen;
mozilla::UniquePtr<mozilla::JsepSession> mJsepSession;
+ std::string mPreviousIceUfrag; // used during rollback of ice restart
+ std::string mPreviousIcePwd; // used during rollback of ice restart
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
// Start time of ICE, used for telemetry
mozilla::TimeStamp mIceStartTime;
// Start time of call used for Telemetry
mozilla::TimeStamp mStartTime;
#endif
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -195,17 +195,17 @@ PeerConnectionMedia::ProtocolProxyQueryH
}
rv = proxyinfo.GetPort(&httpsProxyPort);
if (NS_FAILED(rv)) {
CSFLogError(logTag, "%s: Failed to get proxy server port", __FUNCTION__);
return;
}
- if (pcm_->mIceCtx.get()) {
+ if (pcm_->mIceCtxHdlr.get()) {
assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16));
// Note that this could check if PrivacyRequested() is set on the PC and
// remove "webrtc" from the ALPN list. But that would only work if the PC
// was constructed with a peerIdentity constraint, not when isolated
// streams are added. If we ever need to signal to the proxy that the
// media is isolated, then we would need to restructure this code.
pcm_->mProxyServer.reset(
new NrIceProxyServer(httpsProxyHost.get(),
@@ -218,17 +218,17 @@ PeerConnectionMedia::ProtocolProxyQueryH
}
NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler, nsIProtocolProxyCallback)
PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent)
: mParent(parent),
mParentHandle(parent->GetHandle()),
mParentName(parent->GetName()),
- mIceCtx(nullptr),
+ mIceCtxHdlr(nullptr),
mDNSResolver(new NrIceResolver()),
mUuidGen(MakeUnique<PCUuidGenerator>()),
mMainThread(mParent->GetMainThread()),
mSTSThread(mParent->GetSTSThread()),
mProxyResolveCompleted(false) {
}
nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers,
@@ -293,70 +293,63 @@ nsresult PeerConnectionMedia::Init(const
}
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false);
if (!XRE_IsParentProcess()) {
CSFLogError(logTag, "%s: ICE TCP not support on e10s", __FUNCTION__);
ice_tcp = false;
}
- bool default_address_only = Preferences::GetBool(
- "media.peerconnection.ice.default_address_only", false);
#else
bool ice_tcp = false;
- bool default_address_only = false;
#endif
-
+ bool default_address_only = GetPrefDefaultAddressOnly();
// TODO(ekr@rtfm.com): need some way to set not offerer later
// Looks like a bug in the NrIceCtx API.
- mIceCtx = NrIceCtxHandler::Create("PC:" + mParentName,
- true, // Offerer
- mParent->GetAllowIceLoopback(),
- ice_tcp,
- mParent->GetAllowIceLinkLocal(),
- default_address_only,
- policy);
- if(!mIceCtx) {
+ mIceCtxHdlr = NrIceCtxHandler::Create("PC:" + mParentName,
+ true, // Offerer
+ mParent->GetAllowIceLoopback(),
+ ice_tcp,
+ mParent->GetAllowIceLinkLocal(),
+ default_address_only,
+ policy);
+ if(!mIceCtxHdlr) {
CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__);
return NS_ERROR_FAILURE;
}
- if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) {
+ if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetStunServers(stun_servers))) {
CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__);
return rv;
}
// Give us a way to globally turn off TURN support
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
bool disabled = Preferences::GetBool("media.peerconnection.turn.disable", false);
#else
bool disabled = false;
#endif
if (!disabled) {
- if (NS_FAILED(rv = mIceCtx->SetTurnServers(turn_servers))) {
+ if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetTurnServers(turn_servers))) {
CSFLogError(logTag, "%s: Failed to set turn servers", __FUNCTION__);
return rv;
}
} else if (turn_servers.size() != 0) {
CSFLogError(logTag, "%s: Setting turn servers disabled", __FUNCTION__);
}
if (NS_FAILED(rv = mDNSResolver->Init())) {
CSFLogError(logTag, "%s: Failed to initialize dns resolver", __FUNCTION__);
return rv;
}
- if (NS_FAILED(rv = mIceCtx->SetResolver(mDNSResolver->AllocateResolver()))) {
+ if (NS_FAILED(rv =
+ mIceCtxHdlr->ctx()->SetResolver(mDNSResolver->AllocateResolver()))) {
CSFLogError(logTag, "%s: Failed to get dns resolver", __FUNCTION__);
return rv;
}
- mIceCtx->SignalGatheringStateChange.connect(
- this,
- &PeerConnectionMedia::IceGatheringStateChange_s);
- mIceCtx->SignalConnectionStateChange.connect(
- this,
- &PeerConnectionMedia::IceConnectionStateChange_s);
+ ConnectSignals(mIceCtxHdlr->ctx().get());
return NS_OK;
}
void
PeerConnectionMedia::EnsureTransports(const JsepSession& aSession)
{
auto transports = aSession.GetTransports();
@@ -372,38 +365,39 @@ PeerConnectionMedia::EnsureTransports(co
}
GatherIfReady();
}
void
PeerConnectionMedia::EnsureTransport_s(size_t aLevel, size_t aComponentCount)
{
- RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aLevel));
+ RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->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);
+ RefPtr<NrIceMediaStream> stream =
+ mIceCtxHdlr->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);
+ mIceCtxHdlr->ctx()->SetStream(aLevel, stream);
}
}
void
PeerConnectionMedia::ActivateOrRemoveTransports(const JsepSession& aSession)
{
auto transports = aSession.GetTransports();
for (size_t i = 0; i < transports.size(); ++i) {
@@ -454,21 +448,21 @@ PeerConnectionMedia::ActivateOrRemoveTra
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);
+ mIceCtxHdlr->ctx()->SetStream(aMLine, nullptr);
return;
}
- RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aMLine));
+ RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aMLine));
if (!stream) {
MOZ_ASSERT(false);
return;
}
if (!stream->HasParsedAttributes()) {
CSFLogDebug(logTag, "%s: Activating ICE media stream=%u components=%u",
mParentHandle.c_str(),
@@ -493,18 +487,18 @@ PeerConnectionMedia::ActivateOrRemoveTra
stream->DisableComponent(c + 1);
}
}
}
void
PeerConnectionMedia::RemoveTransportsAtOrAfter_s(size_t aMLine)
{
- for (size_t i = aMLine; i < mIceCtx->GetStreamCount(); ++i) {
- mIceCtx->SetStream(i, nullptr);
+ for (size_t i = aMLine; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
+ mIceCtxHdlr->ctx()->SetStream(i, nullptr);
}
}
nsresult PeerConnectionMedia::UpdateMediaPipelines(
const JsepSession& session) {
auto trackPairs = session.GetNegotiatedTrackPairs();
MediaPipelineFactory factory(this);
nsresult rv;
@@ -564,26 +558,151 @@ PeerConnectionMedia::StartIceChecks_s(
if (!aIceOptionsList.empty()) {
attributes.push_back("ice-options:");
for (auto i = aIceOptionsList.begin(); i != aIceOptionsList.end(); ++i) {
attributes.back() += *i + ' ';
}
}
- nsresult rv = mIceCtx->ParseGlobalAttributes(attributes);
+ nsresult rv = mIceCtxHdlr->ctx()->ParseGlobalAttributes(attributes);
if (NS_FAILED(rv)) {
CSFLogError(logTag, "%s: couldn't parse global parameters", __FUNCTION__ );
}
- mIceCtx->SetControlling(aIsControlling ?
- NrIceCtx::ICE_CONTROLLING :
- NrIceCtx::ICE_CONTROLLED);
+ mIceCtxHdlr->ctx()->SetControlling(aIsControlling ?
+ NrIceCtx::ICE_CONTROLLING :
+ NrIceCtx::ICE_CONTROLLED);
+
+ mIceCtxHdlr->ctx()->StartChecks();
+}
+
+void
+PeerConnectionMedia::BeginIceRestart(const std::string& ufrag,
+ const std::string& pwd)
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ bool default_address_only = GetPrefDefaultAddressOnly();
+ RefPtr<NrIceCtx> new_ctx = mIceCtxHdlr->CreateCtx(ufrag,
+ pwd,
+ default_address_only);
+
+ RUN_ON_THREAD(GetSTSThread(),
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::BeginIceRestart_s,
+ new_ctx),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx)
+{
+ ASSERT_ON_THREAD(mSTSThread);
+
+ // hold the original context so we can disconnect signals if needed
+ RefPtr<NrIceCtx> originalCtx = mIceCtxHdlr->ctx();
+
+ mIceCtxHdlr->BeginIceRestart(new_ctx);
+ if (mIceCtxHdlr->IsRestarting()) {
+ ConnectSignals(mIceCtxHdlr->ctx().get(), originalCtx.get());
+ }
+}
+
+void
+PeerConnectionMedia::FinalizeIceRestart()
+{
+ ASSERT_ON_THREAD(mMainThread);
+
+ RUN_ON_THREAD(GetSTSThread(),
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::FinalizeIceRestart_s),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::FinalizeIceRestart_s()
+{
+ ASSERT_ON_THREAD(mSTSThread);
+
+ mIceCtxHdlr->FinalizeIceRestart();
+}
+
+void
+PeerConnectionMedia::RollbackIceRestart()
+{
+ ASSERT_ON_THREAD(mMainThread);
- mIceCtx->StartChecks();
+ RUN_ON_THREAD(GetSTSThread(),
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::RollbackIceRestart_s),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::RollbackIceRestart_s()
+{
+ ASSERT_ON_THREAD(mSTSThread);
+ if (!mIceCtxHdlr->IsRestarting()) {
+ return;
+ }
+
+ // hold the restart context so we can disconnect signals
+ RefPtr<NrIceCtx> restartCtx = mIceCtxHdlr->ctx();
+
+ mIceCtxHdlr->RollbackIceRestart();
+ ConnectSignals(mIceCtxHdlr->ctx().get(), restartCtx.get());
+}
+
+bool
+PeerConnectionMedia::GetPrefDefaultAddressOnly() const
+{
+ ASSERT_ON_THREAD(mMainThread); // will crash on STS thread
+
+#if !defined(MOZILLA_EXTERNAL_LINKAGE)
+ bool default_address_only = Preferences::GetBool(
+ "media.peerconnection.ice.default_address_only", false);
+#else
+ bool default_address_only = false;
+#endif
+ return default_address_only;
+}
+
+void
+PeerConnectionMedia::ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx)
+{
+ aCtx->SignalGatheringStateChange.connect(
+ this,
+ &PeerConnectionMedia::IceGatheringStateChange_s);
+ aCtx->SignalConnectionStateChange.connect(
+ this,
+ &PeerConnectionMedia::IceConnectionStateChange_s);
+
+ if (aOldCtx) {
+ MOZ_ASSERT(aCtx != aOldCtx);
+ aOldCtx->SignalGatheringStateChange.disconnect(this);
+ aOldCtx->SignalConnectionStateChange.disconnect(this);
+
+ // if the old and new connection state and/or gathering state is
+ // different fire the state update. Note: we don't fire the update
+ // if the state is *INIT since updates for the INIT state aren't
+ // sent during the normal flow. (mjf)
+ if (aOldCtx->connection_state() != aCtx->connection_state() &&
+ aCtx->connection_state() != NrIceCtx::ICE_CTX_INIT) {
+ aCtx->SignalConnectionStateChange(aCtx, aCtx->connection_state());
+ }
+
+ if (aOldCtx->gathering_state() != aCtx->gathering_state() &&
+ aCtx->gathering_state() != NrIceCtx::ICE_CTX_GATHER_INIT) {
+ aCtx->SignalGatheringStateChange(aCtx, aCtx->gathering_state());
+ }
+ }
}
void
PeerConnectionMedia::AddIceCandidate(const std::string& candidate,
const std::string& mid,
uint32_t aMLine) {
RUN_ON_THREAD(GetSTSThread(),
WrapRunnable(
@@ -593,17 +712,17 @@ PeerConnectionMedia::AddIceCandidate(con
std::string(mid),
aMLine),
NS_DISPATCH_NORMAL);
}
void
PeerConnectionMedia::AddIceCandidate_s(const std::string& aCandidate,
const std::string& aMid,
uint32_t aMLine) {
- RefPtr<NrIceMediaStream> stream(mIceCtx->GetStream(aMLine));
+ RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aMLine));
if (!stream) {
CSFLogError(logTag, "No ICE stream for candidate at level %u: %s",
static_cast<unsigned>(aMLine), aCandidate.c_str());
return;
}
nsresult rv = stream->ParseTrickleCandidate(aCandidate);
if (NS_FAILED(rv)) {
@@ -649,31 +768,32 @@ PeerConnectionMedia::GatherIfReady() {
&PeerConnectionMedia::EnsureIceGathering_s));
PerformOrEnqueueIceCtxOperation(runnable);
}
void
PeerConnectionMedia::EnsureIceGathering_s() {
if (mProxyServer) {
- mIceCtx->SetProxyServer(*mProxyServer);
+ mIceCtxHdlr->ctx()->SetProxyServer(*mProxyServer);
}
// Start gathering, but only if there are streams
- for (size_t i = 0; i < mIceCtx->GetStreamCount(); ++i) {
- if (mIceCtx->GetStream(i)) {
- mIceCtx->StartGathering();
+ for (size_t i = 0; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
+ if (mIceCtxHdlr->ctx()->GetStream(i)) {
+ mIceCtxHdlr->ctx()->StartGathering();
return;
}
}
// 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);
+ IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
+ NrIceCtx::ICE_CTX_GATHER_COMPLETE);
}
nsresult
PeerConnectionMedia::AddTrack(DOMMediaStream* aMediaStream,
const std::string& streamId,
const std::string& trackId)
{
ASSERT_ON_THREAD(mMainThread);
@@ -819,17 +939,17 @@ PeerConnectionMedia::ShutdownMediaTransp
}
for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
mRemoteSourceStreams[i]->DetachTransport_s();
}
disconnect_all();
mTransportFlows.clear();
- mIceCtx = nullptr;
+ mIceCtxHdlr = nullptr;
mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
NS_DISPATCH_NORMAL);
}
LocalSourceStreamInfo*
PeerConnectionMedia::GetLocalStreamByIndex(int aIndex)
{
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -229,36 +229,45 @@ class PeerConnectionMedia : public sigsl
PeerConnectionImpl* GetPC() { return mParent; }
nsresult Init(const std::vector<NrIceStunServer>& stun_servers,
const std::vector<NrIceTurnServer>& turn_servers,
NrIceCtx::Policy policy);
// WARNING: This destroys the object!
void SelfDestruct();
- RefPtr<NrIceCtxHandler> ice_ctx() const { return mIceCtx; }
+ RefPtr<NrIceCtxHandler> ice_ctx_hdlr() const { return mIceCtxHdlr; }
+ RefPtr<NrIceCtx> ice_ctx() const { return mIceCtxHdlr->ctx(); }
RefPtr<NrIceMediaStream> ice_media_stream(size_t i) const {
- return mIceCtx->GetStream(i);
+ return mIceCtxHdlr->ctx()->GetStream(i);
}
size_t num_ice_media_streams() const {
- return mIceCtx->GetStreamCount();
+ return mIceCtxHdlr->ctx()->GetStreamCount();
}
// 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);
+ // Begin ICE restart
+ void BeginIceRestart(const std::string& ufrag,
+ const std::string& pwd);
+ // Finalize ICE restart
+ void FinalizeIceRestart();
+ // Abort ICE restart
+ void RollbackIceRestart();
+
// Process a trickle ICE candidate.
void AddIceCandidate(const std::string& candidate, const std::string& mid,
uint32_t aMLine);
// Handle complete media pipelines.
nsresult UpdateMediaPipelines(const JsepSession& session);
// Add a track (main thread only)
@@ -436,16 +445,23 @@ class PeerConnectionMedia : public sigsl
void GatherIfReady();
void FlushIceCtxOperationQueueIfReady();
void PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable);
void EnsureIceGathering_s();
void StartIceChecks_s(bool aIsControlling,
bool aIsIceLite,
const std::vector<std::string>& aIceOptionsList);
+ void BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx);
+ void FinalizeIceRestart_s();
+ void RollbackIceRestart_s();
+ bool GetPrefDefaultAddressOnly() const;
+
+ void ConnectSignals(NrIceCtx *aCtx, NrIceCtx *aOldCtx=nullptr);
+
// Process a trickle ICE candidate.
void AddIceCandidate_s(const std::string& aCandidate, const std::string& aMid,
uint32_t aMLine);
// ICE events
void IceGatheringStateChange_s(NrIceCtx* ctx,
NrIceCtx::GatheringState state);
@@ -494,17 +510,17 @@ class PeerConnectionMedia : public sigsl
// A list of streams provided by the other side
// This is only accessed on the main thread (with one special exception)
nsTArray<RefPtr<RemoteSourceStreamInfo> > mRemoteSourceStreams;
std::map<size_t, std::pair<bool, RefPtr<MediaSessionConduit>>> mConduits;
// ICE objects
- RefPtr<NrIceCtxHandler> mIceCtx;
+ RefPtr<NrIceCtxHandler> mIceCtxHdlr;
// DNS
RefPtr<NrIceResolver> mDNSResolver;
// Transport flows: even is RTP, odd is RTCP
std::map<int, RefPtr<TransportFlow> > mTransportFlows;
// UUID Generator
--- a/media/webrtc/signaling/src/sdp/SdpHelper.cpp
+++ b/media/webrtc/signaling/src/sdp/SdpHelper.cpp
@@ -62,16 +62,17 @@ SdpHelper::CopyTransportParams(size_t nu
newLocalAttrs.SetAttribute(new SdpRtcpAttribute(oldLocalAttrs.GetRtcp()));
}
return NS_OK;
}
bool
SdpHelper::AreOldTransportParamsValid(const Sdp& oldAnswer,
+ const Sdp& offerersPreviousSdp,
const Sdp& newOffer,
size_t level)
{
if (MsectionIsDisabled(oldAnswer.GetMediaSection(level)) ||
MsectionIsDisabled(newOffer.GetMediaSection(level))) {
// Obvious
return false;
}
@@ -85,22 +86,39 @@ SdpHelper::AreOldTransportParamsValid(co
if (newOffer.GetMediaSection(level).GetAttributeList().HasAttribute(
SdpAttribute::kBundleOnlyAttribute) &&
IsBundleSlave(newOffer, level)) {
// It never makes sense to put transport attributes in a bundle-only
// m-section
return false;
}
- // TODO(bug 906986): Check for ICE restart (will need to pass the offerer's
- // old SDP to compare it against |newOffer|)
+ if (IceCredentialsDiffer(newOffer.GetMediaSection(level),
+ offerersPreviousSdp.GetMediaSection(level))) {
+ return false;
+ }
return true;
}
+bool
+SdpHelper::IceCredentialsDiffer(const SdpMediaSection& msection1,
+ const SdpMediaSection& msection2)
+{
+ const SdpAttributeList& attrs1(msection1.GetAttributeList());
+ const SdpAttributeList& attrs2(msection2.GetAttributeList());
+
+ if ((attrs1.GetIceUfrag() != attrs2.GetIceUfrag()) ||
+ (attrs1.GetIcePwd() != attrs2.GetIcePwd())) {
+ return true;
+ }
+
+ return false;
+}
+
nsresult
SdpHelper::GetComponent(const std::string& candidate, size_t* component)
{
unsigned int temp;
int32_t result = PR_sscanf(candidate.c_str(), "%*s %u", &temp);
if (result == 1) {
*component = temp;
return NS_OK;
--- a/media/webrtc/signaling/src/sdp/SdpHelper.h
+++ b/media/webrtc/signaling/src/sdp/SdpHelper.h
@@ -33,18 +33,21 @@ class SdpHelper {
explicit SdpHelper(std::string* errorDest) : mLastError(*errorDest) {}
~SdpHelper() {}
nsresult GetComponent(const std::string& candidate, size_t* component);
nsresult CopyTransportParams(size_t numComponents,
const SdpMediaSection& source,
SdpMediaSection* dest);
bool AreOldTransportParamsValid(const Sdp& oldAnswer,
+ const Sdp& offerersPreviousSdp,
const Sdp& newOffer,
size_t level);
+ bool IceCredentialsDiffer(const SdpMediaSection& msection1,
+ const SdpMediaSection& msection2);
bool MsectionIsDisabled(const SdpMediaSection& msection) const;
static void DisableMsection(Sdp* sdp, SdpMediaSection* msection);
// Maps each mid to the m-section that is the master of its bundle.
// Mids that do not appear in an a=group:BUNDLE do not appear here.
typedef std::map<std::string, const SdpMediaSection*> BundledMids;
--- a/media/webrtc/signaling/test/signaling_unittests.cpp
+++ b/media/webrtc/signaling/test/signaling_unittests.cpp
@@ -86,16 +86,21 @@ class OfferOptions : public mozilla::Jse
public:
void setInt32Option(const char *namePtr, size_t value) {
if (!strcmp(namePtr, "OfferToReceiveAudio")) {
mOfferToReceiveAudio = mozilla::Some(value);
} else if (!strcmp(namePtr, "OfferToReceiveVideo")) {
mOfferToReceiveVideo = mozilla::Some(value);
}
}
+ void setBoolOption(const char* namePtr, bool value) {
+ if (!strcmp(namePtr, "IceRestart")) {
+ mIceRestart = mozilla::Some(value);
+ }
+ }
private:
};
using namespace mozilla;
using namespace mozilla::dom;
// XXX Workaround for bug 998092 to maintain the existing broken semantics
template<>
@@ -532,17 +537,17 @@ class ParsedSDP {
}
}
void DeleteLine(const std::string &objType)
{
DeleteLines(objType, 1);
}
- // Replaces the first instance of objType in the SDP with
+ // Replaces the index-th instance of objType in the SDP with
// a new string.
// If content is an empty string then the line will be removed
void ReplaceLine(const std::string &objType,
const std::string &content,
size_t index = 0)
{
auto it = FindLine(objType, index);
if(it != sdp_lines_.end()) {
@@ -2528,22 +2533,16 @@ TEST_P(SignalingTest, RenegotiationAnswe
// ANSWER_AUDIO causes a new audio track to be added
OfferAnswer(options, ANSWER_AUDIO);
CloseStreams();
}
TEST_P(SignalingTest, BundleRenegotiation)
{
- if (UseBundle()) {
- // We don't support ICE restart, which is a prereq for renegotiating bundle
- // off.
- return;
- }
-
OfferOptions options;
OfferAnswer(options, OFFER_AV | ANSWER_AV);
// If we did bundle before, turn it off, if not, turn it on
if (a1_->mBundleEnabled && a2_->mBundleEnabled) {
a1_->SetBundleEnabled(false);
} else {
a1_->SetBundleEnabled(true);
@@ -3437,16 +3436,27 @@ TEST_P(SignalingTest, AudioOnlyG722Rejec
ASSERT_EQ(a2_->getLocalDescription().find("a=rtpmap:9 G722/8000"), std::string::npos);
CheckPipelines();
CheckStreams();
CloseStreams();
}
+TEST_P(SignalingTest, RestartIce)
+{
+ OfferOptions options;
+ OfferAnswer(options, OFFER_AV | ANSWER_AV);
+
+ options.setBoolOption("IceRestart", true);
+ OfferAnswer(options, OFFER_NONE);
+
+ CloseStreams();
+}
+
TEST_P(SignalingTest, FullCallAudioNoMuxVideoMux)
{
if (UseBundle()) {
// This test doesn't make sense for bundle
return;
}
EnsureInit();