Re: Our simple PeerConnection example with Futures/Promises

On 6/6/13 7:54 AM, Adam Bergkvist wrote:
> Hi
>
> I played around a bit with slightlyoff's Future/Promise polyfill, 
> redefined some of the methods on PeerConnection and tested it in 
> chrome. Here's how our current simple example from the spec could look 
> with futures. It's not the simplest re-write (method(success, error) 
> -> method().then(success, error)); it uses some chaining.
>
> An alternative would be to not listen for the negotiationneeded event 
> and chain offer generation with the call to getUserMedia().

I was going to say, at the Boston interim I thought we agreed that 
keying off onnegotiationneeded was unusual. Didn't someone get an action 
item to "align" the example to not steer people this way? - If this has 
changed, please let me know, as this doesn't work in Firefox currently.

> Here's the code if someone is interested.

Great! Comments below.

> /Adam
>
> var remotePeer = new SignalingChannel();
> var configuration = { "iceServers":
>         [{ "url": "stun:stun.example.org" }] };
> var pc;
>
> // call start() to initiate
> function start() {
>     pc = new RTCPeerConnection(configuration);
>
>     // send any ice candidates to the other peer
>     pc.onicecandidate = function (evt) {
>         if (evt.candidate)
>             remotePeer.send(JSON.stringify(
>                     { "candidate": evt.candidate }));
>     };
>
>     // let the "negotiationneeded" event trigger offer generation
>     pc.onnegotiationneeded = function () {
>         pc.createOffer().then(function (offer) {
>             // set the newly created offer
>             return pc.setLocalDescription(offer);
>         }, logError)
>         .then(function () {
>             // send the offer to the other peer
>             remotePeer.send(JSON.stringify(
>                     { "sdp": pc.localDescription }));
>         }, logError);
>     };

I believe Futures is optimized for chains that take the single result of 
the previous step (the single success-callback arg), so you should be 
able to collapse:

     // let the "negotiationneeded" event trigger offer generation
     pc.onnegotiationneeded = function () {
         pc.createOffer()
.then(pc.setLocalDescription)
.then(function () {
             // send the offer to the other peer
             remotePeer.send(JSON.stringify({ "sdp": pc.localDescription 
}));
         }, logError);
     };

Errors bubble, so logError isn't needed on every step and still gets called.

> // once remote stream arrives, show it in the remote video element
>     pc.onaddstream = function (evt) {
>         remoteVideo.src = URL.createObjectURL(evt.stream);
>     };
>
>     // get a local stream, show it in a self-view and add it to be sent
>     navigator.getUserMedia({ "audio": true, "video": true })
>             .then(function (stream) {
>         selfVideo.src = URL.createObjectURL(stream);
>         pc.addStream(stream);
>     });
> }
>
> remotePeer.onmessage = function (evt) {
>     // got signaling message from other peer
>     if (!pc)
>         start();
>
>     var message = JSON.parse(evt.data);
>     if (message.sdp) {
>         var desc = new RTCSessionDescription(message.sdp);
>
>         pc.setRemoteDescription(desc).then(function () {
>             // if we received an offer, we need to create an answer
>             if (pc.remoteDescription.type == "offer") {
>                 pc.createAnswer().then(function (answer) {
>                     // set answer locally
>                     return pc.setLocalDescription(answer);
>                 }, logError)
>                 .then(function () {
>                     // send our updated local description to ther peer
>                     remotePeer.send(JSON.stringify(
>                             { "sdp": pc.localDescription }));
>                 }, logError);
>             }
>         }, logError);

Nice! You're leaning right a little though. How about:

     var message = JSON.parse(evt.data);
     if (message.sdp) {
         var desc = new RTCSessionDescription(message.sdp);
         if (desc.type == "offer") {
             pc.setRemoteDescription(desc)
             .then(function () { return pc.createAnswer(); })
             .then(pc.setLocalDescription)
             .then(function () {
                 // send our updated local description to ther peer
                 remotePeer.send(JSON.stringify(
                         { "sdp": pc.localDescription }));
             }, logError);
         } else
pc.setRemoteDescription(desc).then(function(){}, logError);

Different pattern, different pain-points, but I find this easier to read 
as it sets up distinct chains for offer and answer.

.: Jan-Ivar :.

> } else
>         pc.addIceCandidate(new RTCIceCandidate(message.candidate));
> };
>
> function logError(error) {
>     log(error.name + ": " + error.message);
> }

Received on Friday, 7 June 2013 01:11:10 UTC