ggarber / webrtc-intercept

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

WebRTC Interceptor

When using some third-party WebRTC platforms or libraries sometimes we would like to be able to customize the way WebRTC is used by those libraries and it is not allowed with the official API.

In those cases it is easy to override the RTCPeerConnection constructor to be able to create the PeerConnection instances ourselves and to intercept any interesting method in those instances.

Contributions are more than welcomed. Write your ideas as github issues or send a Pull Request to add new examples.

Basic overriding of PeerConnection

This is the basic code you need to copy and paste to intercept the PeerConnections created inside your library/framework/platform.

window.pcs = [];
(function() {
  var origPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
  if (origPeerConnection) {
    var newPeerConnection = function(config, constraints) {
        console.log('PeerConnection created with config', config);

        // Add here the specific logic you need
        // You can see some examples for specific use cases in the next sections of this document

        const pc = new origPeerConnection(config, constraints);
        window.pcs.push(pc);
        return pc;
    };

    ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection'].forEach(function(obj) {
        // Override objects if they exist in the window object
        if (window.hasOwnProperty(obj)) {
            window[obj] = newPeerConnection;
            // Copy the static methods (generateCertificate in this case)
            Object.keys(origPeerConnection).forEach((x) => {
                window[obj][x] = origPeerConnection[x];
            });
            window[obj].prototype = origPeerConnection.prototype;
        }
    });
  }
})();

In most of the cases it is very important that you call this code before loading the third party library. Otherwise it could be too late to intercept the PeerConnection constructor.

Basic overriding of getUserMedia

This is the basic code you need to copy and paste to intercept the getUserMedia created inside your library/framework/platform.

(function() {
  function wrap(gUM) {
    return function() {
      return gUM.apply(this, arguments);
    };
  }

  if (navigator.mediaDevices.getUserMedia) {
    navigator.mediaDevices.getUserMedia = wrap(navigator.mediaDevices.getUserMedia); 
  }
  ['getUserMedia', 'webkitGetUserMedia', 'mozGetUserMedia'].forEach(function(name) {
    navigator[name] = wrap(navigator[name]);
  });
})();

In most of the cases it is very important that you call this code before loading the third party library. Otherwise it could be too late to intercept the getUserMedia calls.

Enable DSCP

One of the use cases I needed at some point is to enable DSCP in WebRTC (available only in Chrome with the proprietary constraint googDscp)

    var newPeerConnection = function(config, constraints) {
        constraints = constraints || {};
        constraints.optional = constraints.optional || [];
        constraints.optional.push({ googDscp: true });
        return new origPeerConnection(config, constraints);
    }

Force relay transports

Other common use case is to force WebRTC to use relay candidates for privacy or route optimization reasons.

    var newPeerConnection = function(config, constraints) {
        config.iceTransportPolicy = 'relay';
        return new origPeerConnection(config, constraints);
    }

Use your own ICE Servers

In some cases maybe you need to override the ICE servers used by default in the library/platform you use. A specific example could be to try to use the TURN servers in your corporate network to traverse a very restrictive enterprise firewall. You can easily do it this way:

    var newPeerConnection = function(config, constraints) {
        config.iceServers = [ {urls: 'turn:10.10.10.10'} ];
        return new origPeerConnection(config, constraints);
    }

Grab extra stats

Sometimes you want to get your own statistics from PeerConnections for analytics or debugging purposes:

    var newPeerConnection = function(config, constraints) {
        var pc = new origPeerConnection(config, constraints);
        pc.getStats().then((stats) => {
            // collect stats
        });
        return pc;
    }

Send DTMF

This is a very strange use case but still a posibility.

    var newPeerConnection = function(config, constraints) {
        var pc = new origPeerConnection(config, constraints);
        // Store it in a global place
        // Be careful if you are using multiple peerconnections at the same time
        window.pc = pc;
        return pc;
    }

    function sendDtmf(tones) {
        window.pc.createDtmfSender().insertDtmf(tones);
    }

Enable OPUS DTX

You want to enable Discontinuous Transmission in OPUS to save bandwidth in audio traffic.

    var newPeerConnection = function(config, constraints) {
        console.log('PeerConnection created with config', config);

        var pc = new origPeerConnection(config, constraints);
        var origSetRemoteDescription = pc.setRemoteDescription.bind(pc);
        pc.setRemoteDescription = (sdp, success, failure) => {
          sdp.sdp = sdp.sdp.replace("useinbandfec=1", "useinbandfec=1;usedtx=1");
          return origSetRemoteDescription(sdp, success, failure);
        };
        return pc;
    }

Store PeerConnections in the global scope

Sometimes you want to expose the PeerConnections in the global/window scope for further inspection. This would be the whole snippet for that:

window.pcs = [];
(function() {
  var origPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
  if (origPeerConnection) {
    var newPeerConnection = function(config, constraints) {
        console.log('PeerConnection created with config', config);

        // Add here the specific logic you need
        // You can see some examples for specific use cases in the next sections of this document

        const pc = new origPeerConnection(config, constraints);
        window.pcs.push(pc);
        return pc;
    };

    ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection'].forEach(function(obj) {
        // Override objects if they exist in the window object
        if (window.hasOwnProperty(obj)) {
            window[obj] = newPeerConnection;
            // Copy the static methods (generateCertificate in this case)
            Object.keys(origPeerConnection).forEach((x) => {
                window[obj][x] = origPeerConnection[x];
            });
            window[obj].prototype = origPeerConnection.prototype;
        }
    });
  }
})();

About

License:MIT License