- From: Adam Bergkvist <adam.bergkvist@ericsson.com>
- Date: Thu, 26 Jan 2012 13:41:52 +0100
- To: "public-webrtc@w3.org" <public-webrtc@w3.org>
Hi I've done some JS coding to compare the API changes introduces by JSEP with the existing API (with e.g. ROAP under the hood. Basically all the work is done in the configurePeer() method. There are two versions of that method in the code below; on for the old API and then a version with JSEP. (The version with the old API runs in the Ericsson prototype if the first argument getUserMedia is changed to a string.) The basic flow of the application is: 1. configurePeer() is called either with or without an initial offer; if there's an initial offer we're being called, otherwise we're initiating a call. 2. configurePeer() creates a PeerConnection object and sets up the necessary event listeners. 3. The signaling channel is re-configured (from accepting a call) to handle incoming signaling messages. 4. If we're the caller the initiate a new call, otherwise we handle an incoming call. <script> // shared code var pc; var addButton; var selfView; var remoteView; var signalingChannel; window.onload = function () { document.getElementById("call_button").onclick = initiateCall; addButton = document.getElementById("add_button"); selfView = document.getElementById("self_view"); remoteView = document.getElementById("remote_view"); signalingChannel = new SignalingChannel(); // setup message handler to handle an incoming call signalingChannel.onmessage = function (evt) { configurePeer(evt.data); }; }; function initiateCall() { configurePeer(); } // old API (with ROAP) function configurePeer(initialOffer) { navigator.webkitGetUserMedia({"audio": true, "video": true}, function (localStream) { // create a new PeerConnection object and direct all generated signaling messages // to the other peer via the signaling channel pc = new webkitPeerConnection("", signalingChannel.send); // reset message handler to feed incoming signaling messages to PeerConnection signalingChannel.onmessage = function (evt) { pc.processSignalingMessage(evt.data); }; // once remote stream arrives, show it in the remote view video element pc.onaddstream = function (evt) { remoteView.src = webkitURL.createObjectURL(evt.stream); }; // once the PeerConnection is open, prepare for adding a new stream to call pc.onopen = function () { addButton.onclick = function () { navigator.webkitGetUserMedia({"audio": true, "video": true}, function (localStream) { // show the new stream in the self-view selfView.src = webkitURL.createObjectURL(localStream); // add the new local stream to be sent pc.addStream(localStream); }); }; }; // if we have a initial offer (we're being called), process it if (initialOffer) pc.processSignalingMessage(initialOffer); // show local stream as self-view selfView.src = webkitURL.createObjectURL(localStream); // add local stream to be sent pc.addStream(localStream); }); } // JSEP function configurePeer(initialOffer) { navigator.webkitGetUserMedia({"audio": true, "video": true}, function (localStream) { var updateOffer; var hasOutstandingOffer = false; var hasPendingStreamsToOffer = false; var calleeIceStarted = false; // create a new PeerConnection object and direct all generated ice candidate messages // to the other peer via the signaling channel pc = new webkitPeerConnection("", function (candidate) { signalingChannel.send(JSON.stringify({ "type": "candidate", "candidate": candidate })); }); function createAndSendUpdateOffer() { // create a new updated sdp offer and send it to the other peer via the signaling channel // (we can't call setLocalDescription() rigth away so we store the sdp offer in local variable) updateOffer = pc.createOffer(); signalingChannel.send(JSON.stringify({ "type": "offer", "sdp": updateOffer })); // mark that we have an outstanding offer hasOutstandingOffer = true; } // reset message handler to handle incoming signaling messages signalingChannel.onmessage = function (evt) { var msg = JSON.parse(evt.data); if (msg.type == "candidate") { // feed any incoming candidates to our PeerConnection pc.processIceMessage(msg.candidate); // time for callee to start ice processing if (initialOffer && !calleeIceStarted) { pc.connect(); calleeIceStarted = true; } } else if (msg.type == "offer") { // feed the sdp offer into PeerConnection pc.setRemoteDescription(PeerConnection.SDP_OFFER, msg.sdp); // create an sdp answer based on the offer and set it as our local description var answer = pc.createAnswer(msg.sdp); pc.setLocalDescription(PeerConnection.SDP_ANSWER, answer); // send the answer via the signaling channel signalingChannel.send(JSON.stringify({ "type": "answer", "sdp": answer })); } else if (msg.type == "answer") { // if the answer corresponds to an updated offer (i.e. not the initial offer), // we need to process both the updated offer as well as the received answer if (!msg.initialAnswer) pc.setLocalDescription(PeerConnection.SDP_OFFER, updateOffer); pc.setRemoteDescription(PeerConnection.SDP_ANSWER, msg.sdp); // mark that we have received an answer and no longer has an outstanding offer hasOutstandingOffer = false; // offer any pending streams added while we had an outstanding offer if (hasPendingStreamsToOffer) { createAndSendUpdateOffer(); hasPendingStreamsToOffer = false; } } }; // once remote stream arrives, show it in the remote view video element pc.onaddstream = function (evt) { remoteView.src = webkitURL.createObjectURL(evt.stream); }; // once the PeerConnection is open, prepare for adding a new stream to call pc.onopen = function () { addButton.onclick = function () { navigator.webkitGetUserMedia({"audio": true, "video": true}, function (localStream) { // show the new stream in the self-view selfView.src = webkitURL.createObjectURL(localStream); // add the new local stream to be sent pc.addStream(localStream); // check if we have an outstanding offer (i.e. we can't send a new one until // we have received an answer) if (hasOutstandingOffer) { hasPendingStreamsToOffer = true; return; } createAndSendUpdateOffer(); }); }; }; // show local stream as self-view selfView.src = webkitURL.createObjectURL(localStream); // if we have an initial offer, we're being called; otherwise we're initiating a call if (initialOffer) { // parse the incoming offer var offer = JSON.parse(initialOffer); // feed the sdp offer into PeerConnection pc.setRemoteDescription(PeerConnection.SDP_OFFER, offer.sdp); // add the reply stream to be sent pc.addStream(localStream); // create an sdp answer based on the offer and set it as our local description var answer = pc.createAnswer(offer.sdp); pc.setLocalDescription(PeerConnection.SDP_ANSWER, answer); // send the answer via the signaling channel (mark it as an anser to the initial offer) signalingChannel.send(JSON.stringify({ "type": "answer", "initialAnswer": true, "sdp": answer })); } else { // we're initiating a call // add the stream to be sent pc.addStream(localStream); // create the initial sdp offer and set it as our local description var offer = pc.createOffer(); pc.setLocalDescription(PeerConnection.SDP_OFFER, offer); // send the offer to the other peer via the signaling channel signalingChannel.send(JSON.stringify({ "type": "offer", "sdp": offer })); // start gathering candidates pc.connect(); } }); } </script> Some comments: The API changes that JSEP introduces makes it quite a bit more complicated to work with compared to the existing API (with e.g. ROAP under the hood). For example: - More code for a simple example (e.g. to be presented as an example in the spec). - JSEP has five ways to feed signaling information to PeerConnection compared to one in the ROAP case. You really need to know what you're getting on the signaling channel. - Special cases such as glare and multiple offers have to be handled in JavaScript. A simple example with the existing API (and ROAP) is quite powerful sice special cases are handled under the hood. It's also a question if PeerConnection.createOffer() and PeerConnection.createAnswer() actually can have a return value? If the browser needs to reach down into the platform to gather information these methods may need to be async and use a callback. ---- BR Adam
Received on Thursday, 26 January 2012 12:42:18 UTC