-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The first "audiooutput" MediaDeviceInfo
returned from enumerateDevices()
is not the default device when the default device is not exposed
#133
Comments
This is causing trouble on some web conferencing sites that appear to be assuming that the first device is the default device. When the default device is not exposed, they don't offer a way to switch to the default device nor (sometimes) to switch to the first exposed device. The biggest problem is that sites present UI to (sometimes) allows switching audio output away from the default device, but there is then no way to switch back to the default device. This would be a regression on adding support for |
Proposal: Always include, in |
For a A generic minimal-fingerprint Possible variations might have
Either an empty Perhaps similar options exist for I'm favoring empty |
Should this be adopted, it would need support for localization in the |
This SGTM. |
…ces results when the first exposed output device is not the default r=jib This is the simplest of the variations of the proposal at w3c/mediacapture-output#133 (comment) Creating the dummy output in FilterExposedDevices(), rather than later in the enumerateDevices() process, provides a "devicechange" event when an exposed device becomes or ceases to be the default device. Differential Revision: https://phabricator.services.mozilla.com/D159013
…ces results when the first exposed output device is not the default r=jib This is the simplest of the variations of the proposal at w3c/mediacapture-output#133 (comment) Creating the dummy output in FilterExposedDevices(), rather than later in the enumerateDevices() process, provides a "devicechange" event when an exposed device becomes or ceases to be the default device. Differential Revision: https://phabricator.services.mozilla.com/D159013
Chromium's solution is to use "default" as device ID. Wrt to the label, I think we should let browsers decide what label to use. I don't think an empty label would be useful since it would require applications to give special treatment to such a label. Wrt groupId I see no problem with allowing groupId to be the group ID of the physical device that the system default is currently pointing to, as long as it gets updated when the system default device starts to point to a different device. I also have no problem with also allowing it to be empty or uniquely nonempty. |
The proposal in #133 (comment) is a neutered entry instead of the physical "device that would not otherwise be exposed", and only when it is the OS default. E.g. if the user selectAudioOutputs their AirPods AND they're the current OS default (which is common after they put them on in macOS), then no other entry is added to enumerateDevices: {label: "AirPods", deviceId: "234", ...} If they then go to macOS's System Settings→Sounds and change the OS default to "MacBook Pro Speakers", you'll see: {label: "System default audio output device", deviceId: "", ...}
{label: "AirPods", deviceId: "234", ...} If they then selectAudioOutput their MacBook Pro Speakers, you'll see: {label: "MacBook Pro Speakers", deviceId: "123", ...}
{label: "AirPods", deviceId: "234", ...} @karlt is that right? This solves finding the default device in the spec: const defSpkr = (await mediaDevices.enumerateDevices())
.find(d => d.kind == “audio-output”);
Unfortunately, this poses a competing model for finding the default device: const defSpkr = (await mediaDevices.enumerateDevices())
.find(d => d.kind == “audio-output” && d.deviceId == "default");
|
That's correct, and this finds a
Chrome is using {label: "", deviceId: "", groupId: ""} The spec (and the proposal here, which is Gecko's behavior now) would expect no "audiooutput" devices. When the default "audiooutput" device is exposed via {label: "Default", deviceId: "default", groupId: "default"}
{label: "Built-in Audio Analog Stereo", deviceId: "38cf59402979c7c92a7dfe139b46a07932288d562b1dce9bca711b1e7d2097bc", groupId: "fc5673e12055618bb6840857ef5ffaf81ae28949a50d518734c167530922a3bf"} https://jan-ivar.github.io/dummy/enumerate.html is useful for testing browser behavior. In Chrome currently AFAIK there is currently no way to create a situation where Chrome would expose a non-default "audiooutput" device without exposing the user-agent default "audiooutput" device. If there were, then Chrome's virtual Chrome's |
If "default" is the ID for the system default device as exposed in enumerateDevices() then you just need to compare with "default" instead of "".
What is exactly the problem with this? And the model is competing with what?
The spec uses the empty string for several different things. And the
In this case "" is not the ID of the default device. It is an entry with that signals that the system has output devices, but the information about them cannot be exposed (because gUM hasn´t been called or because there are no permissions).
This is correct.
I haven´t thought about the details of what Chrome would do if it had a per-device permission model, but if the system default device is exposed by enumerateDevices, its ID (whatever it is) should be accepted by setSinkId and should send the output to the system default device.
Why would using the empty string (which has different meanings in different contexts) as ID for the default device in enumerateDevices solve this problem and not "default"? The way Chrome solves that problem right now is by setting the groupId to the same groupId of the physical device currently considered the default. |
It will only work in Chrome, not the spec. E.g. in the first "AirPods" example above.
The spec, which says the way to find the default is to assume it's the first one listed:
These are competing ways to learn the same thing, and we want web developers to adopt the interoperable way. Chrome's virtual (large "D") Default device is listed first, so why do we need another way?
There's no all-speakers permission model in https://w3c.github.io/mediacapture-output
That's clever, but means web developers need to write additional code to work around Chrome's virtual Default device: const speakers = (await mediaDevices.enumerateDevices()).filter(({kind}) => kind == "audio-output");
const defSpkr = speakers.reverse().find(({groupId}) => groupId == speakers[0].groupId); |
What I'm saying is, what would be the problem with this if we say in the spec that the deviceId of a system default device is "default". So far, Chromium is the only browser that exposes a system default device. Safari doesn't expose output devices at all and Firefox exposes output devices, but not a system default device. In my experience with Firefox 129, it shows the physical device currently marked as system default device as the first element in the output of enumerateDevices, but if the system default devices changes, the enumerateDevices result doesn't change, which is correct if we interpret this device to be a user-agent default (not system default). |
Chromium lists the default as the first device, so no contradiction there.
We don't need another way.
There is no mandated permission model. IIUC now, the problem here is specific to a per-device permission model.
I agree. That would be a solution to the problem of knowing if an entry for a physical device corresponds to the current system default device if you expose a system default device like Chromium does, but it's not a solution for the case when the UA default is one of the physical entries. |
System default = OS default. Firefox has a bug where it doesn't update enumerateDevices when the OS default changes. We hope to fix this soon. Sorry for any confusion.
setSinkId says: "If sinkId ... does not match any audio output device identified by the result that would be provided by enumerateDevices(), reject p ...". enumerateDevices's exposure decision algorithm for devices other than camera and microphone says:
IOW, only deviceIds exposed in enumerateDevices() are valid inputs, and the only way to expose them there is through selectAudioOutput() or getUserMedia({audio: true}) for speakers with a microphone groupId. I don't see any allowance for different behavior here. |
I don't think an explicit allowance is needed. The spec says that if you perform those actions you should be able to access the devices indicated there. |
What permission? What prompt? The exposure decision algorithm for devices other than camera and microphone contains no implementation-defined steps, which means the UA cannot choose which devices to expose based on its own policies. Chrome's exposure of non-miked speakers through enumerateDevices() violates this. |
The current language of the spec indeed doesn't allow exposure of non-miked speakers based on the gUM() permission, but Chromium's implementation predates the new spec language by several years and for a long time it was the only implementation of audio output devices. The language before the addition of
I think we should put back some of this language in the spec for compatibility reasons. |
Yes, that is the problem tracked in this issue. Thank you for identifying the source of some of our differences here. Media Capture and Streams uses the term "system default device for kind", while Audio Output Devices API more often uses "user agent default device". The OS has a default output device, which might be configurable per application (depending on the OS) including for each browser / user agent (depending on the OS), so I have assumed that both specs mean the system default device for the user agent. That is what I intended to describe here. I don't feel a need to distinguish unless some user-agents would like to distinguish between different kinds of OS default output devices.
Perhaps the answer might already be clearer now in light of the different problem, but I'll elaborate. IIUC Chrome's
Chrome always adds the The primary purpose of the
The
An additional attribute might be a possible solution, but it is quite a different mechanism to the current first-is-default mechanism. I proposed the placeholder device for similarity with the placeholder empty audioinput device and for its backward compatibility with client apps that assume the first-is-default mechanism. The placeholder would make an additional attribute unnecessary.
A user agent is somewhat free to choose the FWIW Chrome is inconsistent on this. I have heard reported the behavior you describe on MacOS, but the Linux behavior that I have seen is that the virtual default device has a different For a placeholder device, I feel an empty |
That seems like a separate issue. The spec was tightened to satisfy a PING privacy review 5 years ago. |
This issue came back again at the Audio WG meeting. From that prospective, the empty string and Chrome device with deviceId I don't have a strong opinion either way. |
#113 (comment) proposed output device ordering in enumerateDevices() results to identify the default audio output device. It also said "This also probably means it should always be exposed since '' allows to be used by setSinkId."
While the first position for the default device was specified in w3c/mediacapture-main#757, there was no change to always expose the default device and no discussion around this.
The result is that a client app does not know whether or not the first audio output device returned from
enumerateDevices()
is the default device.The text was updated successfully, but these errors were encountered: