--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -177,29 +177,21 @@ GlobalPCList.prototype = {
let winID = subject.QueryInterface(Ci.nsISupportsPRUint64).data;
cleanupWinId(this._list, winID);
if (this._lifecycleobservers.hasOwnProperty(winID)) {
delete this._lifecycleobservers[winID];
}
} else if (topic == "profile-change-net-teardown" ||
topic == "network:offline-about-to-go-offline") {
- // Delete all peerconnections on shutdown - mostly synchronously (we
- // need them to be done deleting transports and streams before we
- // return)! All socket operations must be queued to STS thread
- // before we return to here.
- // Also kill them if "Work Offline" is selected - more can be created
- // while offline, but attempts to connect them should fail.
- for (let winId in this._list) {
- cleanupWinId(this._list, winId);
- }
+ // As Necko doesn't prevent us from accessing the network we still need to
+ // monitor the network offline/online state here. See bug 1326483
this._networkdown = true;
} else if (topic == "network:offline-status-changed") {
if (data == "offline") {
- // this._list shold be empty here
this._networkdown = true;
} else if (data == "online") {
this._networkdown = false;
}
} else if (topic == "gmp-plugin-crash") {
if (subject instanceof Ci.nsIWritablePropertyBag2) {
let pluginID = subject.getPropertyAsUint32("pluginID");
let pluginName = subject.getPropertyAsAString("pluginName");
@@ -1206,19 +1198,21 @@ RTCPeerConnection.prototype = {
changeIceGatheringState: function(state) {
this._iceGatheringState = state;
_globalPCList.notifyLifecycleObservers(this, "icegatheringstatechange");
this.dispatchEvent(new this._win.Event("icegatheringstatechange"));
},
changeIceConnectionState: function(state) {
- this._iceConnectionState = state;
- _globalPCList.notifyLifecycleObservers(this, "iceconnectionstatechange");
- this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
+ if (state != this._iceConnectionState) {
+ this._iceConnectionState = state;
+ _globalPCList.notifyLifecycleObservers(this, "iceconnectionstatechange");
+ this.dispatchEvent(new this._win.Event("iceconnectionstatechange"));
+ }
},
getStats: function(selector, onSucc, onErr) {
if (typeof onSucc == "function" &&
this._warnDeprecatedStatsCallbacksNullable.warn) {
this._warnDeprecatedStatsCallbacksNullable.warn();
this._warnDeprecatedStatsCallbacksNullable.warn = null;
}
@@ -1365,45 +1359,35 @@ PeerConnectionObserver.prototype = {
},
// This method is primarily responsible for updating iceConnectionState.
// This state is defined in the WebRTC specification as follows:
//
// iceConnectionState:
// -------------------
- // new The ICE Agent is gathering addresses and/or waiting for
- // remote candidates to be supplied.
- //
- // checking The ICE Agent has received remote candidates on at least
- // one component, and is checking candidate pairs but has not
- // yet found a connection. In addition to checking, it may
- // also still be gathering.
+ // new Any of the RTCIceTransports are in the new state and none
+ // of them are in the checking, failed or disconnected state.
//
- // connected The ICE Agent has found a usable connection for all
- // components but is still checking other candidate pairs to
- // see if there is a better connection. It may also still be
- // gathering.
+ // checking Any of the RTCIceTransports are in the checking state and
+ // none of them are in the failed or disconnected state.
//
- // completed The ICE Agent has finished gathering and checking and found
- // a connection for all components. Open issue: it is not
- // clear how the non controlling ICE side knows it is in the
+ // connected All RTCIceTransports are in the connected, completed or
+ // closed state and at least one of them is in the connected
// state.
//
- // failed The ICE Agent is finished checking all candidate pairs and
- // failed to find a connection for at least one component.
- // Connections may have been found for some components.
+ // completed All RTCIceTransports are in the completed or closed state
+ // and at least one of them is in the completed state.
+ //
+ // failed Any of the RTCIceTransports are in the failed state.
//
- // disconnected Liveness checks have failed for one or more components.
- // This is more aggressive than failed, and may trigger
- // intermittently (and resolve itself without action) on a
- // flaky network.
+ // disconnected Any of the RTCIceTransports are in the disconnected state
+ // and none of them are in the failed state.
//
- // closed The ICE Agent has shut down and is no longer responding to
- // STUN requests.
+ // closed All of the RTCIceTransports are in the closed state.
handleIceConnectionStateChange: function(iceConnectionState) {
let pc = this._dompc;
if (pc.iceConnectionState === iceConnectionState) {
return;
}
if (pc.iceConnectionState === 'new') {
var checking_histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_CHECKING_RATE");
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -966,16 +966,26 @@ nsresult NrIceCtx::Finalize() {
MOZ_MTLOG(ML_ERROR, "Couldn't finalize "
<< name_ << "'");
return NS_ERROR_FAILURE;
}
return NS_OK;
}
+void NrIceCtx::UpdateNetworkState(bool online) {
+ MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): updating network state to " <<
+ (online ? "online" : "offline"));
+ if (online) {
+ nr_ice_peer_ctx_refresh_consent_all_streams(peer_);
+ } else {
+ nr_ice_peer_ctx_disconnect_all_streams(peer_);
+ }
+}
+
void NrIceCtx::SetConnectionState(ConnectionState state) {
if (state == connection_state_)
return;
MOZ_MTLOG(ML_INFO, "NrIceCtx(" << name_ << "): state " <<
connection_state_ << "->" << state);
connection_state_ = state;
--- a/media/mtransport/nricectx.h
+++ b/media/mtransport/nricectx.h
@@ -309,16 +309,19 @@ class NrIceCtx {
nsresult SetProxyServer(const NrIceProxyServer& proxy_server);
// Start ICE gathering
nsresult StartGathering(bool default_route_only, bool proxy_only);
// Start checking
nsresult StartChecks();
+ // Notify that the network has gone online/offline
+ void UpdateNetworkState(bool online);
+
// Finalize the ICE negotiation. I.e., there will be no
// more forking.
nsresult Finalize();
// Are we trickling?
bool generating_trickle() const { return trickle_; }
// Signals to indicate events. API users can (and should)
--- a/media/mtransport/test/ice_unittest.cpp
+++ b/media/mtransport/test/ice_unittest.cpp
@@ -1361,16 +1361,36 @@ class IceTestPeer : public sigslot::has_
WrapRunnable(this,
&IceTestPeer::AssertConsentRefresh_s,
0,
1,
status),
NS_DISPATCH_SYNC);
}
+ void ChangeNetworkState_s(bool online) {
+ ice_ctx_->ctx()->UpdateNetworkState(online);
+ }
+
+ void ChangeNetworkStateToOffline() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this,
+ &IceTestPeer::ChangeNetworkState_s,
+ false),
+ NS_DISPATCH_SYNC);
+ }
+
+ void ChangeNetworkStateToOnline() {
+ test_utils_->sts_target()->Dispatch(
+ WrapRunnable(this,
+ &IceTestPeer::ChangeNetworkState_s,
+ true),
+ NS_DISPATCH_SYNC);
+ }
+
int trickled() { return trickled_; }
void SetControlling(NrIceCtx::Controlling controlling) {
nsresult res;
test_utils_->sts_target()->Dispatch(
WrapRunnableRet(&res, ice_ctx_->ctx(),
&NrIceCtx::SetControlling,
controlling),
@@ -3345,16 +3365,59 @@ TEST_F(WebRtcIceConnectTest, TestConsent
request are ignored. */
p1_->SetStunResponseDelay(300);
p2_->SetStunResponseDelay(300);
PR_Sleep(1000);
AssertConsentRefresh();
SendReceive();
}
+TEST_F(WebRtcIceConnectTest, TestNetworkForcedOfflineAndRecovery) {
+ AddStream(1);
+ SetupAndCheckConsent();
+ p1_->ChangeNetworkStateToOffline();
+ ASSERT_TRUE_WAIT(p1_->ice_connected() == 0, kDefaultTimeout);
+ // Next round of consent check should switch it back to online
+ ASSERT_TRUE_WAIT(p1_->ice_connected(), kDefaultTimeout);
+}
+
+TEST_F(WebRtcIceConnectTest, TestNetworkForcedOfflineTwice) {
+ AddStream(1);
+ SetupAndCheckConsent();
+ p2_->ChangeNetworkStateToOffline();
+ ASSERT_TRUE_WAIT(p2_->ice_connected() == 0, kDefaultTimeout);
+ p2_->ChangeNetworkStateToOffline();
+ ASSERT_TRUE_WAIT(p2_->ice_connected() == 0, kDefaultTimeout);
+}
+
+TEST_F(WebRtcIceConnectTest, TestNetworkOnlineDoesntChangeState) {
+ AddStream(1);
+ SetupAndCheckConsent();
+ p2_->ChangeNetworkStateToOnline();
+ ASSERT_TRUE(p2_->ice_connected());
+ PR_Sleep(1500);
+ p2_->ChangeNetworkStateToOnline();
+ ASSERT_TRUE(p2_->ice_connected());
+}
+
+TEST_F(WebRtcIceConnectTest, TestNetworkOnlineTriggersConsent) {
+ // Let's emulate audio + video w/o rtcp-mux
+ AddStream(2);
+ AddStream(2);
+ SetupAndCheckConsent();
+ p1_->ChangeNetworkStateToOffline();
+ p1_->SetBlockStun(true);
+ ASSERT_TRUE_WAIT(p1_->ice_connected() == 0, kDefaultTimeout);
+ PR_Sleep(1500);
+ ASSERT_TRUE(p1_->ice_connected() == 0);
+ p1_->SetBlockStun(false);
+ p1_->ChangeNetworkStateToOnline();
+ ASSERT_TRUE_WAIT(p1_->ice_connected(), 500);
+}
+
TEST_F(WebRtcIceConnectTest, TestConnectTurn) {
if (turn_server_.empty())
return;
AddStream(1);
SetTurnServer(turn_server_, kDefaultStunServerPort,
turn_user_, turn_password_);
ASSERT_TRUE(Gather());
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.c
@@ -1237,39 +1237,50 @@ static void nr_ice_component_consent_tim
comp->consent_timeout = 0;
r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): Consent refresh final time out",
comp->ctx->label, comp->stream->label, comp->component_id);
nr_ice_component_consent_failed(comp);
}
-static void nr_ice_component_consent_request_timed_out(nr_ice_component *comp)
+void nr_ice_component_disconnected(nr_ice_component *comp)
{
if (!comp->can_send) {
return;
}
- nr_ice_peer_ctx_disconnected(comp->stream->pctx);
+ if (comp->disconnected) {
+ return;
+ }
+
+ r_log(LOG_ICE,LOG_WARNING,"ICE(%s)/STREAM(%s)/COMP(%d): component disconnected",
+ comp->ctx->label, comp->stream->label, comp->component_id);
+ comp->disconnected = 1;
+
+ /* a single disconnected component disconnects the stream */
+ nr_ice_media_stream_set_disconnected(comp->stream, NR_ICE_MEDIA_STREAM_DISCONNECTED);
}
static void nr_ice_component_consent_refreshed(nr_ice_component *comp)
{
uint16_t tval;
if (!comp->can_send) {
return;
}
gettimeofday(&comp->consent_last_seen, 0);
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): consent_last_seen is now %lu",
comp->ctx->label, comp->stream->label, comp->component_id,
comp->consent_last_seen.tv_sec);
- nr_ice_peer_ctx_connected(comp->stream->pctx);
+ comp->disconnected = 0;
+
+ nr_ice_media_stream_check_if_connected(comp->stream);
if (comp->consent_timeout)
NR_async_timer_cancel(comp->consent_timeout);
tval = NR_ICE_CONSENT_TIMEOUT_DEFAULT;
if (comp->ctx->test_timer_divider)
tval = tval / comp->ctx->test_timer_divider;
@@ -1292,17 +1303,17 @@ static void nr_ice_component_refresh_con
case NR_STUN_CLIENT_STATE_DONE:
r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): Consent refreshed",
comp->ctx->label, comp->stream->label, comp->component_id);
nr_ice_component_consent_refreshed(comp);
break;
case NR_STUN_CLIENT_STATE_TIMED_OUT:
r_log(LOG_ICE, LOG_INFO, "ICE(%s)/STREAM(%s)/COMP(%d): A single consent refresh request timed out",
comp->ctx->label, comp->stream->label, comp->component_id);
- nr_ice_component_consent_request_timed_out(comp);
+ nr_ice_component_disconnected(comp);
break;
default:
break;
}
}
int nr_ice_component_refresh_consent(nr_stun_client_ctx *ctx, NR_async_cb finished_cb, void *cb_arg)
{
@@ -1335,16 +1346,19 @@ void nr_ice_component_consent_calc_conse
comp->consent_ctx->maximum_transmits_timeout_ms = tval;
}
static void nr_ice_component_consent_timer_cb(NR_SOCKET s, int how, void *cb_arg)
{
nr_ice_component *comp=cb_arg;
int r;
+ if (comp->consent_timer) {
+ NR_async_timer_cancel(comp->consent_timer);
+ }
comp->consent_timer = 0;
comp->consent_ctx->params.ice_binding_request.username =
comp->stream->l2r_user;
comp->consent_ctx->params.ice_binding_request.password =
comp->stream->l2r_pass;
comp->consent_ctx->params.ice_binding_request.control =
comp->stream->pctx->controlling?
@@ -1356,22 +1370,16 @@ static void nr_ice_component_consent_tim
nr_ice_component_consent_calc_consent_timer(comp);
if (r=nr_ice_component_refresh_consent(comp->consent_ctx,
nr_ice_component_refresh_consent_cb,
comp)) {
r_log(LOG_ICE,LOG_ERR,"ICE(%s)/STREAM(%s)/COMP(%d): Refresh consent failed with %d",
comp->ctx->label, comp->stream->label, comp->component_id, r);
- /* In case our attempt to send the refresh binding request reports an
- * error we don't have to wait for timeouts, but declare this connection
- * dead right away. */
- if (r != R_WOULDBLOCK) {
- nr_ice_component_consent_failed(comp);
- }
}
nr_ice_component_consent_schedule_consent_timer(comp);
}
void nr_ice_component_consent_schedule_consent_timer(nr_ice_component *comp)
{
@@ -1379,16 +1387,21 @@ void nr_ice_component_consent_schedule_c
return;
}
NR_ASYNC_TIMER_SET(comp->consent_ctx->maximum_transmits_timeout_ms,
nr_ice_component_consent_timer_cb, comp,
&comp->consent_timer);
}
+void nr_ice_component_refresh_consent_now(nr_ice_component *comp)
+ {
+ nr_ice_component_consent_timer_cb(0, 0, comp);
+ }
+
void nr_ice_component_consent_destroy(nr_ice_component *comp)
{
if (comp->consent_timer) {
NR_async_timer_cancel(comp->consent_timer);
comp->consent_timer = 0;
}
if (comp->consent_timeout) {
NR_async_timer_cancel(comp->consent_timeout);
@@ -1396,38 +1409,42 @@ void nr_ice_component_consent_destroy(nr
}
if (comp->consent_handle) {
nr_ice_socket_deregister(comp->active->local->isock,
comp->consent_handle);
comp->consent_handle = 0;
}
if (comp->consent_ctx) {
nr_stun_client_ctx_destroy(&comp->consent_ctx);
+ comp->consent_ctx = 0;
}
}
int nr_ice_component_setup_consent(nr_ice_component *comp)
{
int r,_status;
r_log(LOG_ICE,LOG_DEBUG,"ICE(%s)/STREAM(%s)/COMP(%d): Setting up refresh consent",
comp->ctx->label, comp->stream->label, comp->component_id);
+ nr_ice_component_consent_destroy(comp);
+
if (r=nr_stun_client_ctx_create("consent", comp->active->local->osock,
&comp->active->remote->addr, 0,
&comp->consent_ctx))
ABORT(r);
/* Consent request get send only once. */
comp->consent_ctx->maximum_transmits = 1;
if (r=nr_ice_socket_register_stun_client(comp->active->local->isock,
comp->consent_ctx, &comp->consent_handle))
ABORT(r);
comp->can_send = 1;
+ comp->disconnected = 0;
nr_ice_component_consent_refreshed(comp);
nr_ice_component_consent_calc_consent_timer(comp);
nr_ice_component_consent_schedule_consent_timer(comp);
_status=0;
abort:
return(_status);
--- a/media/mtransport/third_party/nICEr/src/ice/ice_component.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_component.h
@@ -70,16 +70,17 @@ struct nr_ice_component_ {
struct nr_ice_cand_pair_ *nominated; /* Highest priority nomninated pair */
struct nr_ice_cand_pair_ *active;
nr_stun_client_ctx *consent_ctx;
void *consent_timer;
void *consent_timeout;
void *consent_handle;
int can_send;
+ int disconnected;
struct timeval consent_last_seen;
STAILQ_ENTRY(nr_ice_component_)entry;
};
typedef STAILQ_HEAD(nr_ice_component_head_,nr_ice_component_) nr_ice_component_head;
int nr_ice_component_create(struct nr_ice_media_stream_ *stream, int component_id, nr_ice_component **componentp);
@@ -92,13 +93,15 @@ int nr_ice_component_service_pre_answer_
int nr_ice_component_nominated_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
int nr_ice_component_failed_pair(nr_ice_component *comp, nr_ice_cand_pair *pair);
int nr_ice_component_check_if_failed(nr_ice_component *comp);
int nr_ice_component_select_pair(nr_ice_peer_ctx *pctx, nr_ice_component *comp);
int nr_ice_component_set_failed(nr_ice_component *comp);
int nr_ice_component_finalize(nr_ice_component *lcomp, nr_ice_component *rcomp);
int nr_ice_component_insert_pair(nr_ice_component *pcomp, nr_ice_cand_pair *pair);
int nr_ice_component_get_default_candidate(nr_ice_component *comp, nr_ice_candidate **candp, int ip_version);
+void nr_ice_component_refresh_consent_now(nr_ice_component *comp);
+void nr_ice_component_disconnected(nr_ice_component *comp);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
--- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.c
@@ -38,17 +38,17 @@ static char *RCSSTRING __UNUSED__="$Id:
#include <assert.h>
#include <nr_api.h>
#include <r_assoc.h>
#include <async_timer.h>
#include "ice_util.h"
#include "ice_ctx.h"
static char *nr_ice_media_stream_states[]={"INVALID",
- "UNPAIRED","FROZEN","ACTIVE","COMPLETED","FAILED"
+ "UNPAIRED","FROZEN","ACTIVE","CONNECTED","FAILED"
};
int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
int nr_ice_media_stream_create(nr_ice_ctx *ctx,char *label,int components, nr_ice_media_stream **streamp)
{
int r,_status;
nr_ice_media_stream *stream=0;
@@ -69,16 +69,17 @@ int nr_ice_media_stream_create(nr_ice_ct
if(r=nr_ice_component_create(stream, i+1, &comp))
ABORT(r);
}
TAILQ_INIT(&stream->check_list);
TAILQ_INIT(&stream->trigger_check_queue);
+ stream->disconnected = 0;
stream->component_ct=components;
stream->ice_state = NR_ICE_MEDIA_STREAM_UNPAIRED;
*streamp=stream;
_status=0;
abort:
if(_status){
nr_ice_media_stream_destroy(&stream);
@@ -334,17 +335,17 @@ static void nr_ice_media_stream_check_ti
/* Remove the pair from he trigger check queue */
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): Removing pair from trigger check queue %s",stream->pctx->label,pair->as_string);
TAILQ_REMOVE(&stream->trigger_check_queue,pair,triggered_check_queue_entry);
break;
}
pair=TAILQ_NEXT(pair,triggered_check_queue_entry);
}
- if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) {
+ if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) {
if(!pair){
/* Find the highest priority WAITING check and move it to RUNNING */
pair=TAILQ_FIRST(&stream->check_list);
while(pair){
if(pair->state==NR_ICE_PAIR_STATE_WAITING)
break;
pair=TAILQ_NEXT(pair,check_queue_entry);
}
@@ -387,17 +388,17 @@ int nr_ice_media_stream_start_checks(nr_
if (stream->ice_state == NR_ICE_MEDIA_STREAM_CHECKS_FAILED) {
assert(0);
ABORT(R_INTERNAL);
}
/* Even if the stream is completed already remote can still create a new
* triggered check request which needs to fire, but not change our stream
* state. */
- if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED) {
+ if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) {
if(r=nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE)) {
ABORT(r);
}
}
if (!stream->timer) {
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): Starting check timer for stream.",pctx->label,stream->label);
nr_ice_media_stream_check_timer_cb(0,0,stream);
@@ -579,16 +580,90 @@ int nr_ice_media_stream_set_state(nr_ice
r_log(LOG_ICE,LOG_DEBUG,"ICE-PEER(%s): %d active streams",
str->pctx->label, str->pctx->active_streams);
str->ice_state=state;
return(0);
}
+
+void nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream *stream)
+ {
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if((comp->state != NR_ICE_COMPONENT_DISABLED) &&
+ (comp->local_component->state != NR_ICE_COMPONENT_DISABLED) &&
+ comp->disconnected) {
+ nr_ice_component_refresh_consent_now(comp);
+ }
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ }
+
+void nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream *stream)
+ {
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if((comp->state != NR_ICE_COMPONENT_DISABLED) &&
+ (comp->local_component->state != NR_ICE_COMPONENT_DISABLED)) {
+ comp->disconnected = 1;
+ }
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+ }
+
+void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disconnected)
+ {
+ if (stream->disconnected == disconnected) {
+ return;
+ }
+
+ if (stream->ice_state != NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED) {
+ return;
+ }
+ stream->disconnected = disconnected;
+
+ if (disconnected == NR_ICE_MEDIA_STREAM_DISCONNECTED) {
+ nr_ice_peer_ctx_disconnected(stream->pctx);
+ } else {
+ nr_ice_peer_ctx_check_if_connected(stream->pctx);
+ }
+ }
+
+int nr_ice_media_stream_check_if_connected(nr_ice_media_stream *stream)
+ {
+ nr_ice_component *comp;
+
+ comp=STAILQ_FIRST(&stream->components);
+ while(comp){
+ if((comp->state != NR_ICE_COMPONENT_DISABLED) &&
+ (comp->local_component->state != NR_ICE_COMPONENT_DISABLED) &&
+ comp->disconnected)
+ break;
+
+ comp=STAILQ_NEXT(comp,entry);
+ }
+
+ /* At least one disconnected component */
+ if(comp)
+ goto done;
+
+ nr_ice_media_stream_set_disconnected(stream, NR_ICE_MEDIA_STREAM_CONNECTED);
+
+ done:
+ return(0);
+ }
+
/* S OK, this component has a nominated. If every component has a nominated,
the stream is ready */
int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component)
{
int r,_status;
nr_ice_component *comp;
comp=STAILQ_FIRST(&stream->components);
@@ -602,17 +677,17 @@ int nr_ice_media_stream_component_nomina
}
/* At least one un-nominated component */
if(comp)
goto done;
/* All done... */
r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s)/ICE-STREAM(%s): all active components have nominated candidate pairs",stream->pctx->label,stream->label);
- nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED);
+ nr_ice_media_stream_set_state(stream,NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED);
/* Cancel our timer */
if(stream->timer){
NR_async_timer_cancel(stream->timer);
stream->timer=0;
}
if (stream->pctx->handler) {
@@ -742,18 +817,22 @@ int nr_ice_media_stream_send(nr_ice_peer
if(!comp->can_send)
ABORT(R_FAILED);
/* OK, write to that pair, which means:
1. Use the socket on our local side.
2. Use the address on the remote side
*/
if(r=nr_socket_sendto(comp->active->local->osock,data,len,0,
- &comp->active->remote->addr))
+ &comp->active->remote->addr)) {
+ if ((r==R_IO_ERROR) || (r==R_EOD)) {
+ nr_ice_component_disconnected(comp);
+ }
ABORT(r);
+ }
_status=0;
abort:
return(_status);
}
/* Returns R_REJECTED if the component is unpaired or has been disabled. */
int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote)
--- a/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_media_stream.h
@@ -54,19 +54,24 @@ struct nr_ice_media_stream_ {
char *l2r_user; /* The username for outgoing requests */
Data r2l_pass; /* The password for incoming requests */
Data l2r_pass; /* The password for outcoming requests */
int ice_state;
#define NR_ICE_MEDIA_STREAM_UNPAIRED 1
#define NR_ICE_MEDIA_STREAM_CHECKS_FROZEN 2
#define NR_ICE_MEDIA_STREAM_CHECKS_ACTIVE 3
-#define NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED 4
+#define NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED 4
#define NR_ICE_MEDIA_STREAM_CHECKS_FAILED 5
+ int disconnected;
+
+#define NR_ICE_MEDIA_STREAM_CONNECTED 0
+#define NR_ICE_MEDIA_STREAM_DISCONNECTED 1
+
nr_ice_cand_pair_head check_list;
nr_ice_cand_pair_head trigger_check_queue;
void *timer; /* Check list periodic timer */
/* nr_ice_cand_pair_head valid_list; */
STAILQ_ENTRY(nr_ice_media_stream_) entry;
};
@@ -82,16 +87,20 @@ int nr_ice_media_stream_get_default_cand
int nr_ice_media_stream_pair_candidates(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream);
int nr_ice_media_stream_start_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
int nr_ice_media_stream_service_pre_answer_requests(nr_ice_peer_ctx *pctx,nr_ice_media_stream *lstream,nr_ice_media_stream *pstream, int *serviced);
int nr_ice_media_stream_unfreeze_pairs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
int nr_ice_media_stream_unfreeze_pairs_foundation(nr_ice_media_stream *stream, char *foundation);
int nr_ice_media_stream_dump_state(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream,FILE *out);
int nr_ice_media_stream_component_nominated(nr_ice_media_stream *stream,nr_ice_component *component);
int nr_ice_media_stream_component_failed(nr_ice_media_stream *stream,nr_ice_component *component);
+void nr_ice_media_stream_refresh_consent_all(nr_ice_media_stream *stream);
+void nr_ice_media_stream_disconnect_all_components(nr_ice_media_stream *stream);
+void nr_ice_media_stream_set_disconnected(nr_ice_media_stream *stream, int disconnected);
+int nr_ice_media_stream_check_if_connected(nr_ice_media_stream *stream);
int nr_ice_media_stream_set_state(nr_ice_media_stream *str, int state);
int nr_ice_media_stream_get_best_candidate(nr_ice_media_stream *str, int component, nr_ice_candidate **candp);
int nr_ice_media_stream_send(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, UCHAR *data, int len);
int nr_ice_media_stream_get_active(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_ice_candidate **local, nr_ice_candidate **remote);
int nr_ice_media_stream_find_component(nr_ice_media_stream *str, int comp_id, nr_ice_component **compp);
int nr_ice_media_stream_find_pair(nr_ice_media_stream *str, nr_ice_candidate *local, nr_ice_candidate *remote, nr_ice_cand_pair **pair);
int nr_ice_media_stream_addrs(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component, nr_transport_addr *local, nr_transport_addr *remote);
int
--- a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.c
@@ -643,22 +643,55 @@ int nr_ice_peer_ctx_dump_state(nr_ice_pe
fprintf(out,"==========================================\n");
_status=0;
abort:
return(_status);
}
#endif
+void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx)
+ {
+ nr_ice_media_stream *str;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): refreshing consent on all streams",pctx->label);
+
+ str=STAILQ_FIRST(&pctx->peer_streams);
+ while(str) {
+ nr_ice_media_stream_refresh_consent_all(str);
+ str=STAILQ_NEXT(str,entry);
+ }
+ }
+
void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx)
{
if (pctx->reported_connected &&
pctx->handler &&
pctx->handler->vtbl->ice_disconnected) {
pctx->handler->vtbl->ice_disconnected(pctx->handler->obj, pctx);
+
+ pctx->reported_connected = 0;
+ }
+ }
+
+void nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx *pctx)
+ {
+ nr_ice_media_stream *str;
+
+ r_log(LOG_ICE,LOG_INFO,"ICE-PEER(%s): disconnecting all streams",pctx->label);
+
+ str=STAILQ_FIRST(&pctx->peer_streams);
+ while(str) {
+ nr_ice_media_stream_disconnect_all_components(str);
+
+ /* The first stream to be disconnected will cause the peer ctx to signal
+ the disconnect up. */
+ nr_ice_media_stream_set_disconnected(str, NR_ICE_MEDIA_STREAM_DISCONNECTED);
+
+ str=STAILQ_NEXT(str,entry);
}
}
void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx)
{
/* Fire the handler callback to say we're done */
if (pctx->reported_connected &&
pctx->handler &&
@@ -682,17 +715,17 @@ int nr_ice_peer_ctx_check_if_connected(n
{
int _status;
nr_ice_media_stream *str;
int failed=0;
int succeeded=0;
str=STAILQ_FIRST(&pctx->peer_streams);
while(str){
- if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_COMPLETED){
+ if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_CONNECTED){
succeeded++;
}
else if(str->ice_state==NR_ICE_MEDIA_STREAM_CHECKS_FAILED){
failed++;
}
else{
break;
}
@@ -711,17 +744,16 @@ int nr_ice_peer_ctx_check_if_connected(n
if (!pctx->reported_connected) {
pctx->reported_connected = 1;
assert(!pctx->connected_cb_timer);
NR_ASYNC_TIMER_SET(0,nr_ice_peer_ctx_fire_connected,pctx,&pctx->connected_cb_timer);
}
done:
_status=0;
-// abort:
return(_status);
}
/* Given a component in the main ICE ctx, find the relevant component in
the peer_ctx */
int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp)
{
--- a/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/ice/ice_peer_ctx.h
@@ -78,16 +78,18 @@ int nr_ice_peer_ctx_find_pstream(nr_ice_
int nr_ice_peer_ctx_remove_pstream(nr_ice_peer_ctx *pctx, nr_ice_media_stream **pstreamp);
int nr_ice_peer_ctx_parse_trickle_candidate(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream, char *cand);
int nr_ice_peer_ctx_pair_candidates(nr_ice_peer_ctx *pctx);
int nr_ice_peer_ctx_parse_global_attributes(nr_ice_peer_ctx *pctx, char **attrs, int attr_ct);
int nr_ice_peer_ctx_start_checks(nr_ice_peer_ctx *pctx);
int nr_ice_peer_ctx_start_checks2(nr_ice_peer_ctx *pctx, int allow_non_first);
void nr_ice_peer_ctx_stream_started_checks(nr_ice_peer_ctx *pctx, nr_ice_media_stream *stream);
+void nr_ice_peer_ctx_refresh_consent_all_streams(nr_ice_peer_ctx *pctx);
+void nr_ice_peer_ctx_disconnect_all_streams(nr_ice_peer_ctx *pctx);
void nr_ice_peer_ctx_disconnected(nr_ice_peer_ctx *pctx);
void nr_ice_peer_ctx_connected(nr_ice_peer_ctx *pctx);
int nr_ice_peer_ctx_dump_state(nr_ice_peer_ctx *pctx,FILE *out);
int nr_ice_peer_ctx_log_state(nr_ice_peer_ctx *pctx);
int nr_ice_peer_ctx_check_if_connected(nr_ice_peer_ctx *pctx);
int nr_ice_peer_ctx_find_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *str, int component_id, nr_ice_component **compp);
int nr_ice_peer_ctx_deliver_packet_maybe(nr_ice_peer_ctx *pctx, nr_ice_component *comp, nr_transport_addr *source_addr, UCHAR *data, int len);
int nr_ice_peer_ctx_disable_component(nr_ice_peer_ctx *pctx, nr_ice_media_stream *lstream, int component_id);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -17,93 +17,118 @@
#include "mozilla/Preferences.h"
#include <mozilla/Types.h>
#endif
#include "nsNetCID.h" // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
#include "nsServiceManagerUtils.h" // do_GetService
#include "nsIObserverService.h"
#include "nsIObserver.h"
+#include "nsIIOService.h" // NS_IOSERVICE_*
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
+#include "nsCRTGlue.h"
+
#include "gmp-video-decode.h" // GMP_API_VIDEO_DECODER
#include "gmp-video-encode.h" // GMP_API_VIDEO_ENCODER
static const char* logTag = "PeerConnectionCtx";
namespace mozilla {
using namespace dom;
-class PeerConnectionCtxShutdown : public nsIObserver
+class PeerConnectionCtxObserver : public nsIObserver
{
public:
NS_DECL_ISUPPORTS
- PeerConnectionCtxShutdown() {}
+ PeerConnectionCtxObserver() {}
void Init()
{
nsCOMPtr<nsIObserverService> observerService =
services::GetObserverService();
if (!observerService)
return;
nsresult rv = NS_OK;
#ifdef MOZILLA_INTERNAL_API
rv = observerService->AddObserver(this,
NS_XPCOM_SHUTDOWN_OBSERVER_ID,
false);
MOZ_ALWAYS_SUCCEEDS(rv);
+ rv = observerService->AddObserver(this,
+ NS_IOSERVICE_OFFLINE_STATUS_TOPIC,
+ false);
+ MOZ_ALWAYS_SUCCEEDS(rv);
#endif
(void) rv;
}
NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) override {
if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
CSFLogDebug(logTag, "Shutting down PeerConnectionCtx");
PeerConnectionCtx::Destroy();
nsCOMPtr<nsIObserverService> observerService =
services::GetObserverService();
if (!observerService)
return NS_ERROR_FAILURE;
nsresult rv = observerService->RemoveObserver(this,
- NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+ NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
+ MOZ_ALWAYS_SUCCEEDS(rv);
+ rv = observerService->RemoveObserver(this,
+ NS_XPCOM_SHUTDOWN_OBSERVER_ID);
MOZ_ALWAYS_SUCCEEDS(rv);
// Make sure we're not deleted while still inside ::Observe()
- RefPtr<PeerConnectionCtxShutdown> kungFuDeathGrip(this);
- PeerConnectionCtx::gPeerConnectionCtxShutdown = nullptr;
+ RefPtr<PeerConnectionCtxObserver> kungFuDeathGrip(this);
+ PeerConnectionCtx::gPeerConnectionCtxObserver = nullptr;
+ }
+ if (strcmp(aTopic, NS_IOSERVICE_OFFLINE_STATUS_TOPIC) == 0) {
+ const nsLiteralString onlineString(u"" NS_IOSERVICE_ONLINE);
+ const nsLiteralString offlineString(u"" NS_IOSERVICE_OFFLINE);
+ if (NS_strcmp(aData, offlineString.get()) == 0) {
+ CSFLogDebug(logTag, "Updating network state to offline");
+ PeerConnectionCtx::UpdateNetworkState(false);
+ } else if(NS_strcmp(aData, onlineString.get()) == 0) {
+ CSFLogDebug(logTag, "Updating network state to online");
+ PeerConnectionCtx::UpdateNetworkState(true);
+ } else {
+ CSFLogDebug(logTag, "Received unsupported network state event");
+ MOZ_CRASH();
+ }
}
return NS_OK;
}
private:
- virtual ~PeerConnectionCtxShutdown()
+ virtual ~PeerConnectionCtxObserver()
{
nsCOMPtr<nsIObserverService> observerService =
services::GetObserverService();
if (observerService)
+ observerService->RemoveObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC);
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
}
};
-NS_IMPL_ISUPPORTS(PeerConnectionCtxShutdown, nsIObserver);
+NS_IMPL_ISUPPORTS(PeerConnectionCtxObserver, nsIObserver);
}
namespace mozilla {
PeerConnectionCtx* PeerConnectionCtx::gInstance;
nsIThread* PeerConnectionCtx::gMainThread;
-StaticRefPtr<PeerConnectionCtxShutdown> PeerConnectionCtx::gPeerConnectionCtxShutdown;
+StaticRefPtr<PeerConnectionCtxObserver> PeerConnectionCtx::gPeerConnectionCtxObserver;
const std::map<const std::string, PeerConnectionImpl *>&
PeerConnectionCtx::mGetPeerConnections()
{
return mPeerConnections;
}
nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread,
@@ -124,19 +149,19 @@ nsresult PeerConnectionCtx::InitializeGl
res = ctx->Initialize();
PR_ASSERT(NS_SUCCEEDED(res));
if (!NS_SUCCEEDED(res))
return res;
gInstance = ctx;
- if (!PeerConnectionCtx::gPeerConnectionCtxShutdown) {
- PeerConnectionCtx::gPeerConnectionCtxShutdown = new PeerConnectionCtxShutdown();
- PeerConnectionCtx::gPeerConnectionCtxShutdown->Init();
+ if (!PeerConnectionCtx::gPeerConnectionCtxObserver) {
+ PeerConnectionCtx::gPeerConnectionCtxObserver = new PeerConnectionCtxObserver();
+ PeerConnectionCtx::gPeerConnectionCtxObserver->Init();
}
}
EnableWebRtcLog();
return NS_OK;
}
PeerConnectionCtx* PeerConnectionCtx::GetInstance() {
@@ -329,16 +354,27 @@ PeerConnectionCtx::EverySecondTelemetryC
rv = RUN_ON_THREAD(stsThread,
WrapRunnableNM(&EverySecondTelemetryCallback_s, queries),
NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS_VOID(rv);
}
}
#endif
+void
+PeerConnectionCtx::UpdateNetworkState(bool online) {
+ auto ctx = GetInstance();
+ if (ctx->mPeerConnections.empty()) {
+ return;
+ }
+ for (auto pc : ctx->mPeerConnections) {
+ pc.second->UpdateNetworkState(online);
+ }
+}
+
nsresult PeerConnectionCtx::Initialize() {
initGMP();
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
mTelemetryTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
MOZ_ASSERT(mTelemetryTimer);
nsresult rv = mTelemetryTimer->SetTarget(gMainThread);
NS_ENSURE_SUCCESS(rv, rv);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.h
@@ -13,17 +13,17 @@
#include "mozilla/Attributes.h"
#include "mozilla/StaticPtr.h"
#include "PeerConnectionImpl.h"
#include "mozIGeckoMediaPluginService.h"
#include "nsIRunnable.h"
namespace mozilla {
-class PeerConnectionCtxShutdown;
+class PeerConnectionCtxObserver;
namespace dom {
class WebrtcGlobalInformation;
}
// A class to hold some of the singleton objects we need:
// * The global PeerConnectionImpl table and its associated lock.
// * Stats report objects for PCs that are gone
@@ -43,16 +43,18 @@ class PeerConnectionCtx {
return true;
}
void queueJSEPOperation(nsIRunnable* aJSEPOperation);
void onGMPReady();
bool gmpHasH264();
+ static void UpdateNetworkState(bool online);
+
// Make these classes friend so that they can access mPeerconnections.
friend class PeerConnectionImpl;
friend class PeerConnectionWrapper;
friend class mozilla::dom::WebrtcGlobalInformation;
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
// WebrtcGlobalInformation uses this; we put it here so we don't need to
// create another shutdown observer class.
@@ -96,14 +98,14 @@ private:
// ready to go, since blocking on this init is just begging for deadlock.
nsCOMPtr<mozIGeckoMediaPluginService> mGMPService;
bool mGMPReady;
nsTArray<nsCOMPtr<nsIRunnable>> mQueuedJSEPOperations;
static PeerConnectionCtx *gInstance;
public:
static nsIThread *gMainThread;
- static mozilla::StaticRefPtr<mozilla::PeerConnectionCtxShutdown> gPeerConnectionCtxShutdown;
+ static mozilla::StaticRefPtr<mozilla::PeerConnectionCtxObserver> gPeerConnectionCtxObserver;
};
} // namespace mozilla
#endif
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -2292,20 +2292,27 @@ PeerConnectionImpl::AddIceCandidate(cons
static_cast<unsigned>(res),
aCandidate,
static_cast<unsigned>(aLevel),
errorString.c_str());
pco->OnAddIceCandidateError(error, ObString(errorString.c_str()), rv);
}
- UpdateSignalingState();
return NS_OK;
}
+void
+PeerConnectionImpl::UpdateNetworkState(bool online) {
+ if (!mMedia) {
+ return;
+ }
+ mMedia->UpdateNetworkState(online);
+}
+
NS_IMETHODIMP
PeerConnectionImpl::CloseStreams() {
PC_AUTO_ENTER_API_CALL(false);
return NS_OK;
}
#if !defined(MOZILLA_EXTERNAL_LINKAGE)
@@ -3359,18 +3366,16 @@ PeerConnectionImpl::CandidateReady(const
candidate.c_str(),
static_cast<unsigned>(level));
return;
}
CSFLogDebug(logTag, "Passing local candidate to content: %s",
candidate.c_str());
SendLocalIceCandidateToContent(level, mid, candidate);
-
- UpdateSignalingState();
}
static void
SendLocalIceCandidateToContentImpl(nsWeakPtr weakPCObserver,
uint16_t level,
const std::string& mid,
const std::string& candidate) {
RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(weakPCObserver);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -409,16 +409,18 @@ public:
void AddIceCandidate(const nsAString& aCandidate, const nsAString& aMid,
unsigned short aLevel, ErrorResult &rv)
{
rv = AddIceCandidate(NS_ConvertUTF16toUTF8(aCandidate).get(),
NS_ConvertUTF16toUTF8(aMid).get(), aLevel);
}
+ void UpdateNetworkState(bool online);
+
NS_IMETHODIMP CloseStreams();
void CloseStreams(ErrorResult &rv)
{
rv = CloseStreams();
}
NS_IMETHODIMP_TO_ERRORRESULT(AddTrack, ErrorResult &rv,
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -853,16 +853,17 @@ PeerConnectionMedia::AddIceCandidate(con
WrapRunnable(
RefPtr<PeerConnectionMedia>(this),
&PeerConnectionMedia::AddIceCandidate_s,
std::string(candidate), // Make copies.
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(mIceCtxHdlr->ctx()->GetStream(aMLine));
if (!stream) {
CSFLogError(logTag, "No ICE stream for candidate at level %u: %s",
static_cast<unsigned>(aMLine), aCandidate.c_str());
@@ -873,16 +874,31 @@ PeerConnectionMedia::AddIceCandidate_s(c
if (NS_FAILED(rv)) {
CSFLogError(logTag, "Couldn't process ICE candidate at level %u",
static_cast<unsigned>(aMLine));
return;
}
}
void
+PeerConnectionMedia::UpdateNetworkState(bool online) {
+ RUN_ON_THREAD(GetSTSThread(),
+ WrapRunnable(
+ RefPtr<PeerConnectionMedia>(this),
+ &PeerConnectionMedia::UpdateNetworkState_s,
+ online),
+ NS_DISPATCH_NORMAL);
+}
+
+void
+PeerConnectionMedia::UpdateNetworkState_s(bool online) {
+ mIceCtxHdlr->ctx()->UpdateNetworkState(online);
+}
+
+void
PeerConnectionMedia::FlushIceCtxOperationQueueIfReady()
{
ASSERT_ON_THREAD(mMainThread);
if (IsIceCtxReady()) {
for (auto i = mQueuedIceCtxOperations.begin();
i != mQueuedIceCtxOperations.end();
++i) {
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h
@@ -287,16 +287,19 @@ class PeerConnectionMedia : public sigsl
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 notifications of network online/offline events.
+ void UpdateNetworkState(bool online);
+
// Handle complete media pipelines.
nsresult UpdateMediaPipelines(const JsepSession& session);
// Add a track (main thread only)
nsresult AddTrack(DOMMediaStream& aMediaStream,
const std::string& streamId,
dom::MediaStreamTrack& aTrack,
const std::string& trackId);
@@ -486,16 +489,17 @@ class PeerConnectionMedia : public sigsl
bool GetPrefProxyOnly() 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);
+ void UpdateNetworkState_s(bool online);
// ICE events
void IceGatheringStateChange_s(NrIceCtx* ctx,
NrIceCtx::GatheringState state);
void IceConnectionStateChange_s(NrIceCtx* ctx,
NrIceCtx::ConnectionState state);
void IceStreamReady_s(NrIceMediaStream *aStream);
void OnCandidateFound_s(NrIceMediaStream *aStream,