- 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