Unable to change camera / stream for WebRTC call

Tags: , , , ,



Source: https://github.com/anoek/webrtc-group-chat-example/blob/master/client.html

I’m trying to modify this Webrtc example to add the ability of changing camera (Cross-browser support).

Normal usage works perfectly, after changing camera, failed in renegotiation.

1) Get a list of devices via navigator.mediaDevices.enumerateDevices()

2) Change local_media_stream after getting new stream

local_media_stream.getTracks().forEach(function(track) {
    track.stop();
});
local_media_stream = stream; 

3) Trigger renegotiation function (Copied from line 132 of Source code)

function renegotiate(){
    console.log("Creating RTC offer to ", peer_id);
    peer_connection.createOffer(
                    function (local_description) { 
                        console.log("Local offer description is: ", local_description);
                        peer_connection.setLocalDescription(local_description,
            function() { 
                signaling_socket.emit('relaySessionDescription', 
                    {'peer_id': peer_id, 'session_description': local_description});
                console.log("Offer setLocalDescription succeeded"); 
            },
            function() { Alert("Offer setLocalDescription failed!"); }
        );
    },
    function (error) {
        console.log("Error sending offer: ", error);
    });
};

I believe that my approaches are wrong, but I’ve tried many different ways found on google to edit the codes for renegotiation, however I’m not familiar to WebRTC and Socket.io, still can’t make the thing works.

After changing the camera, the video shown on other participant just became a static image from video last frame.

Can anybody please help to point my mistake? Thanks in advance.

Answer

Previously I done it in the following way (an order is important).

Let’s say you list all our available devices:

var devicesIds = [];

navigator.mediaDevices.enumerateDevices().then(function(devices) {
  devices.forEach(function(device) {
     devicesIds.push(device.deviceId);
  });          
});

And now you want to switch:

1) Stop current tracks

localStream.getTracks().forEach(function(track) {
   track.stop();
});

2) Obtain new stream

var constraints = {video: {deviceId: devicesIds[1]}, audio: true};

navigator.mediaDevices.getUserMedia(constraints).then(function(stream) {
  replaceTracks(stream);
}).catch(function(error) {

});

3) Replace tracks:

function replaceTracks(newStream){

  detachMediaStream(elementId);   

  newStream.getTracks().forEach(function(track) {
     localStream.addTrack(track);
  });

  attachMediaStream(elementId, newStream);

  // optionally, if you have active peer connections:
  _replaceTracksForPeer(peerConnection);

  function _replaceTracksForPeer(peer) {
    peer.getSenders().map(function(sender) {
        sender.replaceTrack(newStream.getTracks().find(function(track) {
            return track.kind === sender.track.kind;
        }));
    });
  }
}

function detachMediaStream = function(id) {
  var elem = document.getElementById(id);

  if (elem) {
    elem.pause();

    if (typeof elem.srcObject === 'object') {
        elem.srcObject = null;
    } else {
        elem.src = '';
    }
  }
};

function attachMediaStream = function(id, stream) {
  var elem = document.getElementById(id);

  if (elem) {
    if (typeof elem.srcObject === 'object') {
        elem.srcObject = stream;
    } else {
        elem.src = window.URL.createObjectURL(stream);
    }

    elem.onloadedmetadata = function(e) {
        elem.play();
    };
  } else {
    throw new Error('Unable to attach media stream');
  }
};


Source: stackoverflow