- From: Harald Alvestrand <harald@alvestrand.no>
- Date: Mon, 22 Sep 2014 01:21:25 -0700
- To: Jan-Ivar Bruaroey <jib@mozilla.com>, "public-webrtc@w3.org" <public-webrtc@w3.org>
Jan-Ivar, promises for getUserMedia (promiseUserMedia?) is a Media Capture TF matter. Can you repost to that list? (I'm also interested in seeing your polyfill, if it's just a polyfill, and figure out why it doesn't work in Chrome (if it doesn't))..... On 09/19/2014 10:54 PM, Jan-Ivar Bruaroey wrote: > If you don't care for promises, their minimal impact is: > > - navigator.getUserMedia(constraints, function(stream) { > + mediaDevices.getUserMedia(constraints).then(function(stream) { > video.srcObject = stream; > video.play(); > - }, function (err) { > + }).catch(function (err) { > console.log(err.message); > }); > > But you'd be missing out by stopping there, as I believe they have a > lot more to offer WebRTC. > > To illustrate, I've rewritten our local-loop replaceTrack demo-script > to use a promises polyfill that works in Firefox Nightly [1]. Here's a > cleaned-up excerpt: > > function call(pc, signal) { > return mediaDevices.getUserMedia(myrequest) > .then(video => { > localvideo1.srcObject = video; > localvideo1.play(); > video1.getTracks().forEach(track => pc.addTrack(track, video)); > return pc.createOffer(offer_options); > }) > .then(offer => pc.setLocalDescription(offer)) > .then(() => signal.then(answer => > pc.setRemoteDescription(answer))); > } > > function pickup(pc, signal) { > return (oneway.checked? new Promise(resolve => resolve()) : > mediaDevices.getUserMedia(myrequest_reverse) > .then(video => { > localvideo2.srcObject = video; > localvideo2.play(); > video.getTracks().forEach(track => pc.addTrack(track, > video)); > })) > .then(() => signal.then(offer => pc.setRemoteDescription(offer))) > .then(() => pc.createAnswer(answer_options)) > .then(answer => pc.setLocalDescription(answer)); > } > > Promise.all([call(pc1, pc2.stable), pickup(pc2, pc1.haveLocalOffer)]) > .then(() => log("HIP HIP HOORAY")) > .catch(failed); // 1-line error handling > > This is a complete local-loop call. The two functions, call() and > pickup(), may be a bit contrived, but illustrate that unlike most > local-loop tests you've probably seen, each peer is set up > asynchronously and in parallel, with only a signal sent across, much > like in a remote call. > > Several things are going on here - which I will explain - and promises > are used in three interesting ways: > > First, there's the promise-chains: then().then().then(). We start two > chains in parallel and wait for both to complete with Promise.all. > This is pretty straightforward promise-stuff, but powerful. > > Then, there's three new promises that I've added as > read-only-attributes (yes!) to the peerConnection polyfill that > trigger on signalingstate: > > pc.hasLocalOffer > pc.hasRemoteOffer > pc.stable > > Turns out promises can be used to track simple states. E.g. to make > something happen when signalingstate changes to "has-local-offer", you > just do: pc.hasLocalOffer.then(function(offer) { ... }); To make it > more useful, I've made the fulfillment value be the offer (or answer > depending), which means I can use them as the carrier 'signal' > argument that gives the call() and pickup() functions the other peer's > sdp when it becomes available. > > > Lastly, my final use of promises was to fix an unfortunate race in > local-loop calls. With the two peers less tightly coupled, I started > seeing ICE failures. Turns out candidates were arriving before the > other peer could receive them, and addIceCandidate was failing, > complaining that it could not be called beforesetRemoteDescription. Is > that by spec? > > To solve this, I relied again on the signalingstate promises: > > pc1.onicecandidate = obj => { > pc2.haveRemoteOffer.then(() => { > if (obj.candidate) { > pc2.addIceCandidate(obj.candidate).catch(failed); > } > }); > }; > > pc2.onicecandidate = obj => { > pc1.stable.then(() => { > if (obj.candidate) { > pc1.addIceCandidate(obj.candidate).catch(failed); > } > }); > }; > > Turns out promises can be used to queue function-calls. Calling then() > multiple times on the same promise queues functions up to be executed > in order later (or right away if the promise has already been > resolved). This made it easy to subjugate addIceCandidate's timing-needs. > > So there you have it. Three uses of promises in the same API. I also > enjoy that there is one line of error-handling in the whole thing. > > Feel free to have a look at the full test or take it for a spin: > > Comments welcome. > > [1] https://bug1033885.bugzilla.mozilla.org/attachment.cgi?id=8492552 > > .: Jan-Ivar :. > > -- Surveillance is pervasive. Go Dark.
Received on Monday, 22 September 2014 08:21:57 UTC