Bug 921656 - Fix ICE state change handling and onicecandidate callback r=jib
authorAdam Roach [:abr] <adam@nostrum.com>
Wed, 02 Oct 2013 11:32:59 -0500
changeset 149716 f169382a0216f9d4f2bac3fbb3a54d87e343c177
parent 149715 559ba710d7c7efe8cf166c500389458389d3a564
child 149717 d1382e5fe60fa82acb66ba5fe0ae00380ee77e79
push id25401
push userphilringnalda@gmail.com
push dateThu, 03 Oct 2013 14:59:30 +0000
treeherdermozilla-central@51b36c5fd45f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib
bugs921656
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 921656 - Fix ICE state change handling and onicecandidate callback r=jib
dom/media/PeerConnection.js
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -891,17 +891,17 @@ PeerConnectionObserver.prototype = {
     this._dompc._executeNext();
   },
 
   onSetLocalDescriptionSuccess: function() {
     this._dompc._localType = this._dompc._pendingType;
     this._dompc._pendingType = null;
     this.callCB(this._dompc._onSetLocalDescriptionSuccess);
 
-    if (this._iceGatheringState == "complete") {
+    if (this._dompc._iceGatheringState == "complete") {
         // If we are not trickling or we completed gathering prior
         // to setLocal, then trigger a call of onicecandidate here.
         this.foundIceCandidate(null);
     }
 
     this._dompc._executeNext();
   },
 
@@ -943,59 +943,116 @@ PeerConnectionObserver.prototype = {
         {
             candidate: candidate,
             sdpMid: mid,
             sdpMLineIndex: level
         }
     ));
   },
 
+
+  // This method is primarily responsible for updating two attributes:
+  // iceGatheringState and iceConnectionState. These states are defined
+  // in the WebRTC specification as follows:
+  //
+  // iceGatheringState:
+  // ------------------
+  //   new        The object was just created, and no networking has occurred
+  //              yet.
+  //
+  //   gathering  The ICE engine is in the process of gathering candidates for
+  //              this RTCPeerConnection.
+  //
+  //   complete   The ICE engine has completed gathering. Events such as adding
+  //              a new interface or a new TURN server will cause the state to
+  //              go back to gathering.
+  //
+  // 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.
+  //
+  //   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.
+  //
+  //   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
+  //                 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.
+  //
+  //   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.
+  //
+  //   closed        The ICE Agent has shut down and is no longer responding to
+  //                 STUN requests.
+
   handleIceStateChanges: function(iceState) {
     var histogram = Services.telemetry.getHistogramById("WEBRTC_ICE_SUCCESS_RATE");
-    switch (iceState) {
-      case Ci.IPeerConnection.kIceWaiting:
-        this._dompc.changeIceConnectionState("new");
-        this.callCB(this._dompc.ongatheringchange, "complete");
-        this.callCB(this._onicechange, "starting");
+
+    const STATE_MAP = [
+      // Ci.IPeerConnection.kIceGathering:
+        { gathering: "gathering" },
+      // Ci.IPeerConnection.kIceWaiting:
+        { connection: "new",  gathering: "complete", legacy: "starting" },
+      // Ci.IPeerConnection.kIceChecking:
+        { connection: "checking", legacy: "checking" },
+      // Ci.IPeerConnection.kIceConnected:
+        { connection: "connected", legacy: "connected", success: true },
+      // Ci.IPeerConnection.kIceFailed:
+        { connection: "failed", legacy: "failed", success: false }
+    ];
+
+    if (iceState < 0 || iceState > STATE_MAP.length) {
+      this._dompc.reportWarning("Unhandled ice state: " + iceState, null, 0);
+      return;
+    }
+
+    let transitions = STATE_MAP[iceState];
 
-        if (!this._dompc._trickleIce) {
-          // If we are not trickling, then the queue is in a pending state
-          // waiting for ICE gathering and executeNext frees it
-          this._dompc._executeNext();
-        }
-        else if (this.localDescription) {
-          // If we are trickling but we have already done setLocal,
-          // then we need to send a final foundIceCandidate(null) to indicate
-          // that we are done gathering.
-          this.foundIceCandidate(null);
-        }
-        break;
-      case Ci.IPeerConnection.kIceChecking:
-        this._dompc.changeIceConnectionState("checking");
-        this.callCB(this._onicechange, "checking");
-        break;
-      case Ci.IPeerConnection.kIceGathering:
-        this._dompc.changeIceGatheringState("gathering");
-        this.callCB(this._ongatheringchange, "gathering");
-        break;
-      case Ci.IPeerConnection.kIceConnected:
-        // ICE gathering complete.
-        histogram.add(true);
-        this._dompc.changeIceConnectionState("connected");
-        this.callCB(this._onicechange, "connected");
-        break;
-      case Ci.IPeerConnection.kIceFailed:
-        histogram.add(false);
-        this._dompc.changeIceConnectionState("failed");
-        this.callCB(this._onicechange, "failed");
-        break;
-      default:
-        // Unknown ICE state!
-        this._dompc.reportWarning("Unhandled ice state: " + iceState, null, 0);
-        break;
+    if ("connection" in transitions) {
+        this._dompc.changeIceConnectionState(transitions.connection);
+    }
+    if ("gathering" in transitions) {
+      this._dompc.changeIceGatheringState(transitions.gathering);
+      // Handle (old, deprecated) "ongatheringchange" callback
+      this.callCB(this._dompc.ongatheringchange, transitions.gathering);
+    }
+    // Handle deprecated "onicechange" callback
+    if ("legacy" in transitions) {
+      this.callCB(this._onicechange, transitions.legacy);
+    }
+    if ("success" in transitions) {
+      histogram.add(transitions.success);
+    }
+
+    if (iceState == Ci.IPeerConnection.kIceWaiting) {
+      if (!this._dompc._trickleIce) {
+        // If we are not trickling, then the queue is in a pending state
+        // waiting for ICE gathering and executeNext frees it
+        this._dompc._executeNext();
+      }
+      else if (this.localDescription) {
+        // If we are trickling but we have already done setLocal,
+        // then we need to send a final foundIceCandidate(null) to indicate
+        // that we are done gathering.
+        this.foundIceCandidate(null);
+      }
     }
   },
 
   onStateChange: function(state) {
     switch (state) {
       case Ci.IPeerConnectionObserver.kSignalingState:
         this.callCB(this._dompc.onsignalingstatechange,
                     this._dompc.signalingState);
@@ -1004,16 +1061,20 @@ PeerConnectionObserver.prototype = {
       case Ci.IPeerConnectionObserver.kIceState:
         this.handleIceStateChanges(this._dompc._pc.iceState);
         break;
 
       case Ci.IPeerConnectionObserver.kSdpState:
         // No-op
         break;
 
+      case Ci.IPeerConnectionObserver.kReadyState:
+        // No-op
+        break;
+
       case Ci.IPeerConnectionObserver.kSipccState:
         // No-op
         break;
 
       default:
         this._dompc.reportWarning("Unhandled state type: " + state, null, 0);
         break;
     }
@@ -1024,23 +1085,19 @@ PeerConnectionObserver.prototype = {
                                                              { stream: stream }));
   },
 
   onRemoveStream: function(stream, type) {
     this.dispatchEvent(new this._dompc._win.MediaStreamEvent("removestream",
                                                              { stream: stream }));
   },
 
-  foundIceCandidate: function(cand, mid, line) {
+  foundIceCandidate: function(cand) {
     this.dispatchEvent(new this._dompc._win.RTCPeerConnectionIceEvent("icecandidate",
-                                                                      {
-                                                                          candidate: cand,
-                                                                          sdpMid: mid,
-                                                                          sdpMLineIndex: line
-                                                                      }));
+                                                                      { candidate: cand } ));
   },
 
   notifyDataChannel: function(channel) {
     this.dispatchEvent(new this._dompc._win.RTCDataChannelEvent("datachannel",
                                                                 { channel: channel }));
   },
 
   notifyConnection: function() {