- From: Justin Uberti <juberti@google.com>
- Date: Thu, 26 Jan 2012 23:24:54 -0500
- To: Adam Bergkvist <adam.bergkvist@ericsson.com>
- Cc: "public-webrtc@w3.org" <public-webrtc@w3.org>
- Message-ID: <CAOJ7v-1G6aD1Gxxip2TOKBLF9UHrvZCYyxMhq-3LkpL3pfopug@mail.gmail.com>
On Thu, Jan 26, 2012 at 7:41 AM, Adam Bergkvist <adam.bergkvist@ericsson.com > wrote: > > 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. > Right, this is what we acknowledged as a tradeoff of JSEP; by pushing more control into JS, more code is needed. But as you can see, the amount of code is not that large, and this could easily be placed into a JS library. > > 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. > Good point. I'd be interested in knowing whether anyone would need this. > >
Received on Friday, 27 January 2012 04:25:49 UTC