Audio Output Devices API

W3C Candidate Recommendation Draft

More details about this document
This version:
https://www.w3.org/TR/2023/CRD-audio-output-20230504/
Latest published version:
https://www.w3.org/TR/audio-output/
Latest editor's draft:
https://w3c.github.io/mediacapture-output/
History:
https://www.w3.org/standards/history/audio-output
Commit history
Implementation report:
https://wpt.fyi/audio-output
Editors:
Justin Uberti (Google)
Guido Urdaneta (Google)
Youenn Fablet (Apple)
Feedback:
GitHub w3c/mediacapture-output (pull requests, new issue, open issues)
public-webrtc@w3.org with subject line [audio-output] … message topic … (archives)
Participate
Mailing list

Abstract

This document defines a set of JavaScript APIs that let a Web application manage how audio is rendered on the user audio output devices.

Status of This Document

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at https://www.w3.org/TR/.

The WebRTC and Device and Sensors Working Group intend to publish this specification as a Candidate Recommendation soon. Consequently, this is a Request for wide review of this document.

This document was published by the Web Real-Time Communications Working Group as a Candidate Recommendation Draft using the Recommendation track.

Publication as a Candidate Recommendation does not imply endorsement by W3C and its Members. A Candidate Recommendation Draft integrates changes from the previous Candidate Recommendation that the Working Group intends to include in a subsequent Candidate Recommendation Snapshot.

This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 2 November 2021 W3C Process Document.

1. Introduction

This section is non-normative.

This proposal allows JavaScript to direct the audio output of a media element to permitted devices other than the system or user agent default. This can be helpful in a variety of real-time communication scenarios as well as general media applications. For example, an application can use this API to programmatically direct output to a device such as a Bluetooth headset or speakerphone.

2. HTMLMediaElement Extensions

This section specifies additions to the HTMLMediaElement [HTML] when the Audio Output Devices API is supported.

When the HTMLMediaElement constructor is invoked, the user agent MUST add the following initializing step:

  1. Let the element have a [[SinkId]] internal slot, initialized to "".

WebIDLpartial interface HTMLMediaElement {
  [SecureContext] readonly attribute DOMString sinkId;
  [SecureContext] Promise<undefined> setSinkId (DOMString sinkId);
};

Attributes

sinkId of type DOMString, readonly

This attribute contains the ID of the audio device through which output is being delivered, or the empty string if output is delivered through the user-agent default device. If nonempty, this ID should be equal to the deviceId attribute of one of the MediaDeviceInfo values returned from enumerateDevices().

On getting, the attribute MUST return the value of the [[SinkId]] slot.

Methods

setSinkId

Sets the ID of the audio device through which audio output should be rendered if the application is permitted to play out of a given device.

When this method is invoked, the user agent must run the following steps:

  1. Let document be the current settings object's relevant global object's associated Document.

  2. If document is not allowed to use the feature identified by "speaker-selection", return a promise rejected with a new DOMException whose name is NotAllowedError.

  3. Let element be the HTMLMediaElement object on which this method was invoked.

  4. Let sinkId be the method's first argument.

  5. If sinkId is equal to element's [[SinkId]], return a promise resolved with undefined.

  6. Let p be a new promise.

  7. Run the following substeps in parallel:

    1. If sinkId is not the empty string and does not match any audio output device identified by the result that would be provided by enumerateDevices(), reject p with a new DOMException whose name is NotFoundError and abort these substeps.

    2. If sinkId is not the empty string, and the application would not be permitted to play audio through the device identified by sinkId if it weren't the current user agent default device, reject p with a new DOMException whose name is NotAllowedError and abort these substeps.

    3. Switch the underlying audio output device for element to the audio device identified by sinkId.

      Note

      If this substep is successful and the media element's paused attribute is false, audio MUST stop playing out of the device represented by the element's sinkId attribute and will start playing out of the device identified by sinkId

    4. If the preceding substep failed, reject p with a new DOMException whose name is AbortError, and abort these substeps.

    5. Queue a task that runs the following steps:

      1. Set element's [[SinkId]] to sinkId.

      2. Resolve p.

  8. Return p.

2.1 Algorithms

2.1.1 Sink no longer available

The audio device identified by a media element's sinkId attribute may become unavailable, for example if it is unplugged.

When the audio device identified by the sinkId attribute is no longer available, the user agent must take no action. For example, if the media element's paused attribute is false when the device identified by the sinkId is no longer available, then playback will continue as normal. In this case, audio will not be rendered because the device to which the media element is attached is unavailable.

The following paragraph is non-normative.

If the application wishes to react to the device change, the application can listen to the devicechange event and query enumerateDevices() for the list of updated devices. If the value of the media element's sinkId attribute is no longer present as the deviceId attribute in the returned list of MediaDeviceInfos, the device is no longer available and the application can choose to react accordingly.

2.1.2 New sink available

New audio devices may become available to the user agent, or an audio device (identified by a media element's sinkId attribute) that had previously become unavailable may become available again, for example, if it is unplugged and later plugged back in.

In this scenario, the user agent must run the following steps:

  1. Let sinkId be the identifier for the newly available device.

  2. For each media element whose sinkId attribute is equal to sinkId:

    1. If the media element's paused attribute is false, start rendering this object's audio out of the device represented by the sinkId attribute.

The following paragraph is non-normative.

If the application wishes to react to the device change, the application can listen to the devicechange event and query enumerateDevices() for the list of updated devices.

3. MediaDevices Extensions

This section specifies additions to the MediaDevices when the Audio Output Devices API is supported.

WebIDLpartial interface MediaDevices {
  Promise<MediaDeviceInfo> selectAudioOutput(optional AudioOutputOptions options = {});
};

Methods

selectAudioOutput

Prompts the user to select a specific audio output device.

When the selectAudioOutput method is called, the user agent MUST run the following steps:

  1. If the relevant global object of this does not have transient activation, return a promise rejected with a DOMException object whose name attribute has the value InvalidStateError.

  2. Let options be the method's first argument.

  3. Let deviceId be options.deviceId.

  4. Let p be a new promise.

  5. Run the following steps in parallel:

    1. Let descriptor be a PermissionDescriptor with its name set to "speaker-selection"

    2. If descriptor's permission state is "denied", reject p with a new DOMException whose name attribute has the value NotAllowedError, and abort these steps.

    3. Probe the user agent for available audio output devices.

    4. If there is no audio output device, reject p with a new DOMException whose name attribute has the value NotFoundError and abort these steps.

    5. If deviceId is not "" and matches an id previously exposed by selectAudioOutput in an earlier browsing session, the user agent MAY decide, based on its previous decision of whether to persist this id or not for this set of origins, to run the following sub steps:

      1. Let device be the device identified by deviceId, if available.

      2. If device is available, resolve p with either deviceId or a freshly rotated device id for device, and abort the in-parallel steps.

    6. Prompt the user to choose an audio output device, with descriptor.

    7. If the result of the request is "denied", reject p with a new DOMException whose name attribute has the value NotAllowedError and abort these steps.

    8. Let selectedDevice be the user-selected audio output device.

    9. Let deviceInfo be the result of creating a device info object to represent selectedDevice, with mediaDevices.

    10. Add deviceInfo.deviceId to [[explicitlyGrantedAudioOutputDevices]].

    11. Resolve p with deviceInfo.

  6. Return p.

Once a device is exposed after a call to selectAudioOutput, it MUST be listed by enumerateDevices() for the current browsing context.

If the promise returned by selectAudioOutput is resolved, then the user agent MUST ensure the document is both immediately allowed to play media in an HTMLMediaElement, and immediately allowed to start an AudioContext, without needing any additional user gesture.

Note

This is imprecise due to the current lack of standardization of autoplay in browsers.

AudioOutputOptions dictionary

This dictionary describes the options that can be used to obtain access to an audio output device.

WebIDLdictionary AudioOutputOptions {
  DOMString deviceId = "";
};

Dictionary AudioOutputOptions Members

deviceId of type DOMString, defaulting to ""

When the value of this dictionary member is not "", and matches the id previously exposed by selectAudioOutput in an earlier session, the user agent MAY opt to skip prompting the user in favor of resolving with this id or a new rotated id for the same device, assuming that device is currently available.

Note

Applications that wish to rely on user agents supporting persisted device ids must pass these through selectAudioOutput successfully before they will work with setSinkId. The reason for this is that it exposes fingerprinting information, but at the risk of prompting the user if the device is not available or the user agent decides not to honor the device id.

4. Privacy Considerations

4.3 Permissions Integration

The Audio Output Devices API is a powerful feature that is identified by the name "speaker-selection".

It defines the following types and algorithms:

permission descriptor type

A permission covers access to the device given in the associated DevicePermissionDescriptor descriptor.

If the descriptor does not have a deviceId, its semantic is that it queries for access to all devices of that class. Thus, if a query for the "speaker-selection" powerful feature with no deviceId returns "granted", the client knows that there will not be a permission prompt for any audio output device known to it, if requested using the deviceId option to selectAudioOutput, and if "denied" is returned, it knows that no selectAudioOutput request for an audio output device will succeed.

If a permission state is present for access to some, but not all, audio output devices, a query without the deviceId will return "prompt".

extra permission data type
A list of deviceId values for the devices the user has made a non-default decision on access to.
permission query algorithm
The permission query algorithm runs the following steps:
  1. If permissionDesc.deviceId exists in the extra permission data, set status.state to permissionDesc's permission state and terminate these steps.
  2. Let global be a copy of permissionDesc with the deviceId member removed.
  3. Set status.state to global's permission state.
permission revocation algorithm
This is the result of calling the device permission revocation algorithm passing name and deviceId as arguments. If the descriptor does not have a deviceId, then undefined is passed in place of deviceId.

4.4 Permissions Policy Integration

This specification defines one policy-controlled feature identified by the string "speaker-selection". It has a default allowlist of "self".

Note

A document's permissions policy determines whether any content in that document is allowed to use selectAudioOutput to prompt the user for an audio output device, or allowed to use setSinkId to change the device through which audio output should be rendered, to a non-system-default user-permitted device. For selectAudioOutput this is enforced by the prompt the user to choose algorithm.

5. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

The key words MAY and MUST in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

This specification defines conformance criteria that apply to a single product: the user agent that implements the interfaces that it contains.

Conformance requirements phrased as algorithms or specific steps may be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)

Implementations that use ECMAScript to implement the APIs defined in this specification must implement them in a manner consistent with the ECMAScript Bindings defined in the Web IDL specification [WEBIDL], as this specification uses that specification and terminology.

6. Acknowledgments

The following people have contributed directly to the development of this specification: Harald Alvestrand, Rick Byers, Dominique Hazael-Massieux (via the HTML5Apps project), Philip Jägenstedt, Victoria Kirst, Shijun Sun, Martin Thomson, Chris Wilson.

A. References

A.1 Normative references

[GETUSERMEDIA]
Media Capture and Streams. Cullen Jennings; Bernard Aboba; Jan-Ivar Bruaroey; Henrik Boström; youenn fablet. W3C. 27 April 2023. W3C Candidate Recommendation. URL: https://www.w3.org/TR/mediacapture-streams/
[HTML]
HTML Standard. Anne van Kesteren; Domenic Denicola; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[permissions]
Permissions. Marcos Caceres; Mike Taylor. W3C. 20 December 2022. W3C Working Draft. URL: https://www.w3.org/TR/permissions/
[permissions-policy]
Permissions Policy. Ian Clelland. W3C. 22 March 2023. W3C Working Draft. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[WEBAUDIO]
Web Audio API. Paul Adenot; Hongchan Choi. W3C. 17 June 2021. W3C Recommendation. URL: https://www.w3.org/TR/webaudio/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

A.2 Informative references

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/