- 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