- From: Stefan Håkansson LK <stefan.lk.hakansson@ericsson.com>
- Date: Sat, 9 Nov 2013 14:55:07 +0000
- To: Martin Thomson <martin.thomson@gmail.com>, "public-media-capture@w3.org" <public-media-capture@w3.org>
On 08/11/13 20:48, Martin Thomson wrote: > I was asked to write this up and I've sat on this for a while, worried > that the barrage of detail would destroy the message. There's a fair > degree of nuance in the proposal, so I recommend reading through, > maybe skipping the code for getUserMedia() and then we can discuss the > various issues that you see. > > # Connecting Devices and Tracks > > The device enumeration function provided by getMediaDevices() is > critical to this proposal. The object that is produced by > getMediaDevices(), MediaDeviceInfo, is extended to include a way to > open and use that source (we can entertain a name change later; it > doesn't appear in the API explicitly, but it might be nice for a > better handle): > > partial interface MediaDeviceInfo { > MediaStreamTrack createTrack(Constraints? constraints); > void connectTrack(MediaStreamTrack track); > } > > For someone who was going to be using the sourceId constraint to > select a source, this is really easy to use. > > var track = deviceInfo.createTrack({ ... constraints ... }); > > It gets a little harder when you don't want to choose, and for that > purpose I'm going to propose that we retain getUserMedia > (implementation below). > > The constructor of MediaStreamTrack becomes more critical to the > process. The track that is acquired as a result of calling the > constructor directly produces black or silence. You'll notice that > this is intentionally synchronous; there are consequences for that, > but I think that this is something that the browser will have to > handle. > > Any user consent interactions are triggered when connectTrack() is > called. Once an active (i.e., unmuted) device is attached to the > track, the 'onunmuted' event is fired for that track. > > createTrack() is a shortcut for both creating a track and connecting it: > > MediaDeviceInfo.createTrack = function createTrack(constraints) { > var track = new {Video,Audio}MediaStreamTrack(constraints); > this.connectTrack(track); > return track; > }; Let's see if I've understood correctly: * If you do createTrack, the UA will immediately create a track connected to the source in question, but it will only produce silence/blackness until the user approves its use, right? So it could be used immediately for negotiation? Is that about right? * What is then the use of the MediaStreamTrack constructor? It has been argued that if you just construct a track (with no info at all about an eventual source) it can't be used for negotiation, and I can see no other use for it? * Is connectTrack supposed to give an error if the track you supply already has a source, or is the plan to allow for changing source of a track? I'm trying to understand what this proposal (which I like BTW, but if we can get support the same functionality with fewer changes that feels safer) buys us over the one Jan-Ivar did a long time ago [1]. [1] http://lists.w3.org/Archives/Public/public-media-capture/2013Sep/0080.html > > And that's it. > > The following code demonstrates how this can be used to replicate the > behaviour of getUserMedia. It uses createTrack(). > > -- > > navigator.getUserMedia = function getUserMedia(constraints, > successCb, failureCb) { > // error checking omitted > if (constraints.video) { > getTrack("video", constraints.video, trackConnected, trackFailure); > } > if (constraints.audio) { > getTrack("audio", constraints.audio, trackConnected, trackFailure); > } > > var firstTrack; > function trackConnected(track) { > if (firstTrack || !constraints.video || !constraints.audio) { > successCb(new MediaStream([firstTrack, track])); > } > } > > var failedAlready; > function trackFailure(e) { > if (!failedAlready) { > failedAlready = true; > failureCb(e); > } > } > }; > > function getTrack(type, constraints, successCb, failureCb) { > var devices = navigator.getMediaDevices().filter(ofType(type)); > > function stopMonitoring(track) { > track.removeEventListener('unmute', connected); // overly cautious here > track.removeEventListener('overconstrained', tryDevice); > } > > function tryDevice(e) { > if (e) { > if (timeout) { > window.clearTimeout(timeout); > } > stopMonitoring(e.target); > e.target.stop(); > } > > var dev = devices.shift(); // pick the next one > if (!dev) { > failureCb(new Error('overconstrained')); // use real thing > } > var track = dev.openTrack(typeof constraints === 'object' ? > constraints : undefined); > track.addEventListener('unmute', connected); > track.addEventListener('overconstrained', tryDevice); > > var timeout = window.setTimeout(tryDevice, 1000); > } > > function connected(e) { > stopMonitoring(e.target); > successCb(e.target); > } > } > > function ofType(t) { > return function(x) { > return x.type == "video"; > }; > } > > -- > > Please excuse the inevitable bugs and syntax errors; obviously I can't > compile or run this and I didn't bother linting it. > > Obviously, this isn't going to be how a browser would really implement > this. This results in the camera going on all cameras in turn, which > is not ideal. It might also find a device that meets the provided > contraints but is suboptimal. Another problem is that this requires a > timeout in case a muted device is selected because we don't > distinguish between muted and no user consent (intentionally). A > browser can, of course, use the extra knowledge it has about sources > in order to implement these functions. > > Names are, of course, negotiable, as is every other aspect of this. > For instance, I was considering the use of an overload on > "connectTrack", but the difference in method signatures seemed enough > to justify two names. > >
Received on Saturday, 9 November 2013 14:55:31 UTC