├── .github └── workflows │ └── auto-publish.yml ├── .gitignore ├── .manually_checked_links ├── .pr-preview.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── explainer.md ├── index.html ├── script.js ├── style.css └── w3c.json /.github/workflows/auto-publish.yml: -------------------------------------------------------------------------------- 1 | name: Auto Publish 2 | 3 | on: 4 | push: 5 | paths: index.html 6 | branches: 7 | - main 8 | pull_request: {} 9 | 10 | jobs: 11 | validate-and-publish: 12 | name: Validate and Publish 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v4 16 | - uses: w3c/spec-prod@v2 # use the action 17 | with: 18 | W3C_ECHIDNA_TOKEN: ${{ secrets.ECHIDNA_TOKEN }} 19 | # Please use the URL that's appropriate for your working group! 20 | W3C_WG_DECISION_URL: "https://lists.w3.org/Archives/Public/public-webrtc/2016Mar/0031.html" 21 | W3C_NOTIFICATIONS_CC: "dom@w3.org" 22 | VALIDATE_LINKS: false 23 | W3C_BUILD_OVERRIDE: | 24 | shortName: audio-output 25 | specStatus: CRD 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | build 4 | support 5 | webrtc-respec-ci 6 | -------------------------------------------------------------------------------- /.manually_checked_links: -------------------------------------------------------------------------------- 1 | http://webaudio.github.io/web-audio-api/#the-audiocontext-interface 2 | http://webaudio.github.io/web-audio-api/#widl-AudioContext-createMediaStreamDestination-MediaStreamAudioDestinationNode 3 | http://webaudio.github.io/web-audio-api/#the-audiodestinationnode-interface 4 | -------------------------------------------------------------------------------- /.pr-preview.json: -------------------------------------------------------------------------------- 1 | { 2 | "src_file": "index.html", 3 | "type": "respec" 4 | } 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributions to this repository are intended to become part of Recommendation-track documents governed by the 2 | [W3C Patent Policy](https://www.w3.org/Consortium/Patent-Policy/) and 3 | [Software and Document License](https://www.w3.org/Consortium/Legal/copyright-software). To make substantive contributions to specifications, you must either participate 4 | in the relevant W3C Working Group or make a non-member patent licensing commitment. 5 | 6 | If you are not the sole contributor to a contribution (pull request), please identify all 7 | contributors in the pull request comment. 8 | 9 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 10 | 11 | ``` 12 | +@github_username 13 | ``` 14 | 15 | If you added a contributor by mistake, you can remove them in a comment with: 16 | 17 | ``` 18 | -@github_username 19 | ``` 20 | 21 | If you are making a pull request on behalf of someone else but you had no part in designing the 22 | feature, you can remove yourself with the above syntax. 23 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All documents in this Repository are licensed by contributors 2 | under the 3 | [W3C Software and Document License](https://www.w3.org/Consortium/Legal/copyright-software). 4 | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Specification 'mediacapture-output' 3 | 4 | This is the repository for mediacapture-output. You're welcome to contribute! Let's make the Web rock our socks 5 | off! 6 | -------------------------------------------------------------------------------- /explainer.md: -------------------------------------------------------------------------------- 1 | # Audio Output Devices API explained 2 | 3 | The Audio Output Devices API allows JavaScript applications to direct the audio 4 | output of a media element to authorized devices other than the system or user 5 | agent default. This can be helpful in a variety of real-time communication 6 | scenarios as well as general media applications. For example, an application 7 | can use this API to programmatically direct output to a device such as a 8 | Bluetooth headset or speakerphone. 9 | 10 | The Audio Output Devices API adds a setSinkId method and a sinkId field to 11 | HTML media elements. The setSinkId() method allows changing the audio output 12 | device on which the element renders audio, and the sinkId attribute is a string 13 | that contains the ID of the device where the element is currently rendering 14 | audio. 15 | 16 | By default, the sinkId field is the empty string, which means that audio will be 17 | rendered on the default audio output device in the system. 18 | 19 | The set of valid device IDs that can be used as argument to setSinkId() can be 20 | obtained from a call to [enumerateDevices](https://w3c.github.io/mediacapture-main/#dom-mediadevices-enumeratedevices). 21 | For example: 22 | 23 | ```js 24 | var audioElement = new Audio('sound.ogg'); 25 | audioElement.play(); 26 | navigator.mediaDevices.enumerateDevices().then(devices => { 27 | var lastOutputDeviceId = ''; 28 | for (i in devices) { 29 | if (devices[i].kind == 'audiooutput') { 30 | lastOutputDeviceId = devices[i].deviceId; 31 | } 32 | } 33 | audioElement.setSinkId(lastOutputDeviceId).then(() => { 34 | console.log('Audio output device set to ' + audioElement.sinkId); 35 | }).catch(err => { 36 | console.log('setSinkId() failed', err); 37 | }); 38 | }).catch(err => { 39 | console.log('enumerateDevices() failed :(', err); 40 | }); 41 | 42 | ``` 43 | 44 | In this example, the script creates an audio element that starts to play the 45 | 'sound.ogg' file on the default audio output device. Then the output device 46 | is changed to the last audio output device listed in the result of a call to 47 | enumerateDevices. 48 | 49 | SetSinkId requires the user to provide authorization if a device other than 50 | the system default is to be used. A programmatic way to get this authorization 51 | is to use the [getUserMedia](https://w3c.github.io/mediacapture-main/#dom-mediadevices-getusermedia()) function. 52 | getUserMedia can be used to produce audio streams from a microphone and it 53 | is required to authorize any corresponding output devices. 54 | If the application does not require the audio track produced by getUserMedia, 55 | it can be closed. For example, one standard way to ensure that all output 56 | devices are authorized (provided there is a corresponding microphone) is: 57 | 58 | ```js 59 | navigator.mediaDevices.enumerateDevices().then(async devices => { 60 | for (i in devices) { 61 | if (devices[i].kind == 'audiooutput') { 62 | var stream = await navigator.mediaDevices.getUserMedia({audio: {groupId: {exact: devices[i].groupId}}}); 63 | stream.getAudioTracks()[0].stop(); 64 | console.log('Authorization succeeded for device ' + devices[i].deviceId); 65 | } 66 | } 67 | }).catch(err => { 68 | console.log('An error occurred :(', err); 69 | }); 70 | ``` 71 | 72 | Note that implementations are not required to use a per-device authorization 73 | model. For example, an implementation may use a model based on authorization 74 | per device class. In this case a single getUserMedia call would suffice to 75 | authorize all devices. The [permissions API](https://w3c.github.io/permissions/) 76 | can be used to determine if it is necessary to request permissions. 77 | 78 | ## Demo 79 | 80 | [WebRTC samples](https://webrtc.github.io/samples) has a 81 | [demo](https://webrtc.github.io/samples/src/content/devices/multi/) that shows 82 | how to use the Audio OutputDevices API to change the output device used by media 83 | elements that render static files and sound coming from a microphone. 84 | 85 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Audio Output Devices API 7 | 10 | 12 | 13 | 14 |
15 |

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

18 |
19 |
20 |

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

23 |
24 |
25 |

Introduction

26 |

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

32 |
33 |
34 |

{{HTMLMediaElement}} Extensions

35 |

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

37 |

When the {{HTMLMediaElement}} constructor is invoked, the user 38 | agent MUST add the following initializing step:

39 |
    40 |
  1. 41 |

    Let the element have a [[\SinkId]] internal slot, 42 | initialized to "". 43 |

  2. 44 |
45 |
46 |
 47 |       partial interface HTMLMediaElement {
 48 |         [SecureContext] readonly attribute DOMString sinkId;
 49 |         [SecureContext] Promise<undefined> setSinkId (DOMString sinkId);
 50 |       };
 51 |       
52 |
53 |

Attributes

54 |
56 |
sinkId of type {{DOMString}}, readonly
57 |
58 |

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

64 |

On getting, the 65 | attribute MUST return the value of the {{HTMLMediaElement/[[SinkId]]}} slot.

66 |
67 |
68 |
69 |
70 |

Methods

71 |
73 |
setSinkId
74 |
75 |

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

79 |

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

81 |
    82 |
  1. 83 |

    Let document be the 84 | 85 | current settings object's 86 | 87 | relevant global object's 88 | 89 | associated Document.

    90 |
  2. 91 |
  3. 92 |

    If document is not 93 | 94 | allowed to use the feature identified by 95 | "speaker-selection", return a 96 | promise rejected with a new {{DOMException}} 97 | whose name is {{NotAllowedError}}. 98 |

    99 |
  4. 100 |
  5. 101 |

    Let element be the {{HTMLMediaElement}} 102 | object on which this method was invoked.

    103 |
  6. 104 |
  7. 105 |

    Let sinkId be the method's first argument.

    106 |
  8. 107 |
  9. 108 |

    If sinkId is equal to element's 109 | {{HTMLMediaElement/[[SinkId]]}}, 110 | return a promise resolved with undefined.

    111 |
  10. 112 |
  11. 113 |

    Let p be a new promise.

    114 |
  12. 115 |
  13. 116 |

    Run the following substeps in parallel:

    117 |
      118 |
    1. 119 |

      If sinkId is not the empty string and does not 120 | match any audio output device identified by the result that 121 | would be provided by {{MediaDevices/enumerateDevices()}}, 122 | reject p with a new 123 | {{DOMException}} whose name is 124 | {{NotFoundError}} and abort these substeps.

      125 |
    2. 126 |
    3. 127 |

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

      133 |
    4. 134 |
    5. 135 |

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

      137 |

      If this substep is successful and the media 138 | element's {{HTMLMediaElement/paused}} attribute is false, audio MUST stop playing 139 | out of the device represented by the element's {{HTMLMediaElement/sinkId}} attribute and will start 140 | playing out of the device identified by sinkId

      141 |
    6. 142 |
    7. 143 |

      If the preceding substep failed, reject p 144 | with a new {{DOMException}} whose name is 145 | {{AbortError}}, 146 | and abort these substeps.

      147 |
    8. 148 |
    9. 149 |

      Queue a task that runs the following steps:

      150 |
        151 |
      1. 152 |

        Set element's {{HTMLMediaElement/[[SinkId]]}} to 153 | sinkId.

        154 |
      2. 155 |
      3. 156 |

        Resolve p.

        157 |
      4. 158 |
      159 |
    10. 160 |
    161 |
  14. 162 |
  15. 163 |

    Return p.

    164 |
  16. 165 |
166 |
167 |
168 |
169 |
170 |
171 |

Algorithms

172 |
173 |

Sink no longer available

174 |

The audio device identified by a media element's {{HTMLMediaElement/sinkId}} attribute may become 175 | unavailable, for example if it is unplugged.

176 |

When the audio device identified by the {{HTMLMediaElement/sinkId}} 177 | attribute is no longer 178 | available, the user agent must take no action. For example, if the 179 | media element's {{HTMLMediaElement/paused}} 180 | attribute is false when the device identified by the 181 | sinkId is no longer available, then playback will continue as normal. 182 | In this case, audio will not be rendered because the device to which 183 | the media element is attached is unavailable.

184 |

The following paragraph is non-normative.

185 |

If the application wishes to react to the device 186 | change, the application can listen to the 187 | devicechange event and query 188 | {{MediaDevices/enumerateDevices()}} for the list of updated devices. If 189 | the value of the media element's {{HTMLMediaElement/sinkId}} attribute is no longer 190 | present as the {{MediaDeviceInfo/deviceId}} 191 | attribute in the returned list of {{MediaDeviceInfo}}s, the device is no longer available and the 192 | application can choose to react accordingly.

193 |
194 |
195 |

New sink available

196 |

New audio devices may become available to the user agent, or an 197 | audio device (identified by a media element's {{HTMLMediaElement/sinkId}} attribute) that had 198 | previously become [= unavailable =] may become available 199 | again, for example, if it is unplugged and later plugged back in.

200 |

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

201 |
    202 |
  1. 203 |

    Let sinkId be the identifier for the newly available 204 | device.

    205 |
  2. 206 |
  3. 207 |

    For each media element whose {{HTMLMediaElement/sinkId}} attribute is equal to 208 | sinkId:

    209 |
      210 |
    1. 211 |

      If the media element's 212 | {{HTMLMediaElement/paused}} attribute is false, start rendering 213 | this object's audio out of the device represented by the 214 | {{HTMLMediaElement/sinkId}} 215 | attribute.

      216 |
    2. 217 |
    218 |
  4. 219 |
220 |

The following paragraph is non-normative.

221 |

If the application wishes to react to the device 222 | change, the application can listen to the 223 | devicechange event and query 224 | {{MediaDevices/enumerateDevices()}} for the list of updated 225 | devices.

226 |
227 |
228 |
229 |
230 |

{{MediaDevices}} Extensions

231 |

This section specifies additions to the {{MediaDevices}} 232 | when the Audio Output Devices API is 233 | supported.

234 |
235 |
236 |       partial interface MediaDevices {
237 |         Promise<MediaDeviceInfo> selectAudioOutput(optional AudioOutputOptions options = {});
238 |       };
239 |       
240 |
241 |

Methods

242 | 243 |
245 |
selectAudioOutput
246 |
247 |

Prompts the user to select a specific audio output device.

248 |

When the {{selectAudioOutput}} method is called, 249 | the [=user agent=] MUST run the following steps:

250 |
    251 |
  1. If the [=relevant global object=] of [=this=] does not have 252 | [=transient activation=], return a promise rejected with 253 | a {{DOMException}} object whose {{DOMException/name}} attribute 254 | has the value {{InvalidStateError}}.

  2. 255 |
  3. Let options be the method's first argument.

  4. 256 |
  5. Let deviceId be options.deviceId.

  6. 257 |
  7. Let mediaDevices be [=this=].

  8. 258 |
  9. Let p be a new promise.

  10. 259 |
  11. 260 |

    Run the following steps in parallel:

    261 |
      262 |
    1. Let descriptor be a {{PermissionDescriptor}} with its 263 | [=powerful feature/name=] set to "speaker-selection"

      264 |
    2. 265 |
    3. 266 |

      If descriptor's [=permission state=] is 267 | {{PermissionState/"denied"}}, reject 268 | p with a new {{DOMException}} whose 269 | {{DOMException/name}} attribute has the value 270 | {{NotAllowedError}}, and abort these steps.

      271 |
    4. 272 |
    5. Probe the [=user agent=] for available audio output devices.

    6. 273 |
    7. 274 |

      If there is no audio output device, reject p 275 | with a new {{DOMException}} whose {{DOMException/name}} 276 | attribute has the value {{NotFoundError}} and abort 277 | these steps.

      278 |
    8. 279 |
    9. 280 |

      If deviceId is not "" 281 | run the following sub steps:

      282 |
        283 |
      1. 284 |

        If deviceId matches a 285 | a device id previously exposed by 286 | {{MediaDevices/selectAudioOutput}} in this or an earlier browsing 287 | session, or matches a device id of an audio output device 288 | with the same groupId as an audio input device previously 289 | exposed by {{MediaDevices/getUserMedia()}} in this or an earlier browsing 290 | session, the user agent MAY decide, based on its previous 291 | decision of whether to persist this id or not for this set 292 | of origins, to run the following sub steps:

        293 |
          294 |
        1. 295 |

          Let device be the device identified by 296 | deviceId, if available.

          297 |
        2. 298 |
        3. If device is available, resolve 299 | p with either deviceId or a freshly 300 | rotated device id for device, and abort the 301 | in-parallel steps.

        4. 302 |
        303 |
      2. 304 |
      305 |
    10. 306 |
    11. [=Prompt the user to choose=] an audio output device, with 307 | descriptor.

    12. 308 |
    13. If the result of the request is {{PermissionState/"denied"}}, reject 309 | p with a new {{DOMException}} whose {{DOMException/name}} attribute 310 | has the value {{NotAllowedError}} and abort these steps.

    14. 311 |
    15. 312 |

      Let selectedDevice be the user-selected audio output device.

      313 |
    16. 314 |
    17. Let deviceInfo be the result of 315 | [=creating a device info object=] to represent selectedDevice, 316 | with mediaDevices.

    18. 317 |
    19. Add deviceInfo.{{MediaDeviceInfo/deviceId}} 318 | to [[\explicitlyGrantedAudioOutputDevices]].

    20. 319 |
    21. Resolve p with deviceInfo.

    22. 320 |
    321 |
  12. 322 |
  13. Return p.

  14. 323 |
324 |

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

326 |

If the promise returned by {{MediaDevices/selectAudioOutput}} is resolved, 327 | then the user agent MUST ensure the document is both immediately 328 | allowed to play media in an 329 | {{HTMLMediaElement}}, and immediately 330 | allowed to start an 331 | {{AudioContext}}, without needing any additional user gesture.

332 |
333 |

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

335 |
336 |
337 |
338 |
339 |
340 |

AudioOutputOptions dictionary

341 |

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

343 |
344 |
dictionary AudioOutputOptions {
346 |   DOMString deviceId = "";
347 | };
348 |
349 |

Dictionary AudioOutputOptions Members

350 |
352 |
deviceId of type {{DOMString}}, defaulting to 353 | ""
354 |
355 |

When the value of this dictionary member 356 | is not "", and matches the id previously exposed by 357 | {{MediaDevices/selectAudioOutput}} or 358 | a device id of an audio output device with the same groupId as an 359 | audio input device previously exposed by 360 | {{MediaDevices/getUserMedia()}} in this or an earlier session, the user 361 | agent MAY opt to skip prompting the user in favor of resolving 362 | with this id or a new rotated id for the same device, assuming 363 | that device is currently available.

364 |

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

371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |

Privacy Considerations

380 | 400 | 468 |
469 |

Permissions Integration

470 |

The Audio Output Devices API is a [=powerful feature=] that is 471 | identified by the [=powerful feature/name=] "speaker-selection".

472 |

It defines the following types and algorithms:

473 |
474 |
475 | [=powerful feature/permission descriptor type=] 476 |
477 |
478 |

479 | A permission covers access to at least one non-default speaker output device. 480 |

481 |

482 | The semantics of the descriptor is that it queries for access to any non-default speaker 483 | output device. Thus, if a query for the "speaker-selection" [=powerful feature=] returns 484 | {{PermissionState/"granted"}}, the client knows that at least one of the 485 | {{AudioOutputOptions/deviceId}}s previously shared with it can be passed to 486 | {{MediaDevices/selectAudioOutput}} without incurring a permission prompt, 487 | and if {{PermissionState/"denied"}} is returned, it knows that no {{MediaDevices/selectAudioOutput}} request 488 | for an audio output device will succeed. 489 |

490 |

491 | If the User Agent considers permission given to some, but not all, audio output devices, 492 | a query will return {{PermissionState/"granted"}}. 493 |

494 |

495 | If the User Agent considers permission denied to all audio output devices, a query 496 | will return {{PermissionState/"denied"}}. 497 |

498 |
499 |
500 |
501 |
502 |

Permissions Policy Integration

503 |

This specification defines one 504 | [=policy-controlled feature=] identified by the string 505 | "speaker-selection". 506 | It has a [=policy-controlled feature/default allowlist=] 507 | of "self". 508 |

509 |

A [=document=]'s [=Document/permissions policy=] 510 | determines whether any content in that document is 511 | [=allowed to use=] {{MediaDevices/selectAudioOutput}} to prompt the user for 512 | an audio output device, or 513 | [=allowed to use=] {{HTMLMediaElement/setSinkId}} to change the device 514 | through which audio output should be rendered, to a non-system-default 515 | user-permitted device. For {{MediaDevices/selectAudioOutput}} this is 516 | enforced by the [=prompt the user to choose=] algorithm. 517 |

518 |
519 |
520 |
521 |
522 |

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

525 |

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

529 |

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

533 |
534 |
535 |

Acknowledgments

536 |

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

540 |
541 | 542 | 543 | -------------------------------------------------------------------------------- /script.js: -------------------------------------------------------------------------------- 1 | let aMonthFromNow = new Date(); 2 | aMonthFromNow.setMonth(aMonthFromNow.getMonth() + 1); 3 | 4 | var respecConfig = { 5 | // specification status (e.g. WD, LCWD, NOTE, etc.). If in doubt use ED. 6 | specStatus: "ED", 7 | 8 | // the specification's short name, as in http://www.w3.org/TR/short-name/ 9 | shortName: "audio-output", 10 | // if your specification has a subtitle that goes below the main 11 | // formal title, define it here 12 | // subtitle : "an excellent document", 13 | 14 | // if you wish the publication date to be other than today, set this 15 | // publishDate: "2009-08-06", 16 | 17 | 18 | // if the specification's copyright date is a range of years, specify 19 | // the start date here: 20 | // copyrightStart: "2005", 21 | 22 | // if there is a previously published draft, uncomment this and set its YYYY-MM-DD 23 | // prevED: "", 24 | 25 | // if there a publicly available Editor's Draft, this is the link 26 | edDraftURI: "https://w3c.github.io/mediacapture-output/", 27 | 28 | // if this is a LCWD, uncomment and set the end of its review period 29 | // lcEnd: "2009-08-05", 30 | 31 | // if you want to have extra CSS, append them to this list 32 | // it is recommended that the respec.css stylesheet be kept 33 | //extraCSS: ["http://dev.w3.org/2009/dap/ReSpec.js/css/respec.css"], 34 | //extraCSS: ["../../../2009/dap/ReSpec.js/css/respec.css"], 35 | crEnd: aMonthFromNow.toJSON().slice(0,10), 36 | // editors, add as many as you like 37 | // only "name" is required 38 | editors: [ 39 | // { name: "Your Name", url: "http://example.org/", 40 | // company: "Your Company", companyURL: "http://example.com/" }, 41 | { name: "Guido Urdaneta", company: "Google", w3cid: 84810 }, 42 | { name: "Youenn Fablet", company: "Apple", w3cid: 96458 } 43 | ], 44 | formerEditors: [ 45 | { name: "Justin Uberti", company: "Google", w3cid: 51065, retiredDate: "2021-06-23" }, 46 | ], 47 | 48 | // authors, add as many as you like. 49 | // This is optional, uncomment if you have authors as well as editors. 50 | // only "name" is required. Same format as editors. 51 | 52 | //authors: [ 53 | // { name: "Your Name", url: "http://example.org/", 54 | // company: "Your Company", companyURL: "http://example.com/" }, 55 | //], 56 | 57 | // name of the WG 58 | group: "webrtc", 59 | // name (without the @w3.org) of the public mailing to which comments are due 60 | wgPublicList: "public-webrtc", 61 | github: "https://github.com/w3c/mediacapture-output/", 62 | xref: ["webidl", "html", "permissions", "mediacapture-streams", "permissions-policy","dom", "webaudio"], 63 | implementationReportURI: "https://wpt.fyi/audio-output", 64 | otherLinks: [ 65 | { 66 | key: "Participate", 67 | data: [ 68 | { 69 | value: "Mailing list", 70 | href: "https://lists.w3.org/Archives/Public/public-webrtc/" 71 | } 72 | ] 73 | } 74 | ], 75 | }; 76 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @media screen { 2 | html { background: #eeeeee; } 3 | body { margin-bottom: 30%; border-bottom: thin solid #3c790a; } 4 | } 5 | 6 | pre { margin-left: 2em; white-space: pre-wrap; } 7 | dt, dfn { font-weight: bold; font-style: normal; } 8 | i, em, dt dfn { font-style: italic; } 9 | pre, code { font-size: inherit; font-family: monospace, Droid Sans Fallback, sans-serif; font-variant: normal; } 10 | pre strong { color: black; font: inherit; font-weight: bold; background: yellow; } 11 | pre em { font-weight: bolder; font-style: normal; } 12 | @media screen { code { color: orangered; } } 13 | var sub { vertical-align: bottom; font-size: smaller; position: relative; top: 0.1em; } 14 | table { border-collapse: collapse; border-style: hidden hidden none hidden; } 15 | table thead, table tbody { border-bottom: solid; } 16 | table tbody th { text-align: left; } 17 | table tbody th:first-child { border-left: solid; } 18 | table td, table th { border-left: solid; border-right: solid; border-bottom: solid thin; vertical-align: top; padding: 0.2em; } 19 | 20 | ins { background: green; color: white; /* color: green; border: solid thin lime; padding: 0.3em; line-height: 1.6em; */ text-decoration: none; } 21 | del { background: maroon; color: white; /* color: maroon; border: solid thin red; padding: 0.3em; line-height: 1.6em; */ text-decoration: line-through; } 22 | body ins, body del { display: block; } 23 | body * ins, body * del { display: inline; } 24 | 25 | 26 | p > span:not([title=""]):not([class="XXX"]):not([class="impl"]):not([class="note"]), 27 | li > span:not([title=""]):not([class="XXX"]):not([class="impl"]):not([class="note"]) { border-bottom: solid #99CC99; } 28 | 29 | .note { color: green; background: transparent; font-family: sans-serif, Droid Sans Fallback; } 30 | .warning { color: red; background: transparent; } 31 | .note, .warning { font-weight: bolder; font-style: italic; } 32 | .note em, .warning em, .note i, .warning i { font-style: normal; } 33 | p.note, div.note { padding: 0.5em 2em; } 34 | span.note { padding: 0 2em; } 35 | .note p:first-child, .warning p:first-child { margin-top: 0; } 36 | .note p:last-child, .warning p:last-child { margin-bottom: 0; } 37 | .warning:before { font-style: normal; } 38 | p.note:before { content: 'Note: '; } 39 | p.warning:before { content: '\26A0 Warning! '; } 40 | 41 | .bookkeeping:before { display: block; content: 'Bookkeeping details'; font-weight: bolder; font-style: italic; } 42 | .bookkeeping { font-size: 0.8em; margin: 2em 0; } 43 | .bookkeeping p { margin: 0.5em 2em; display: list-item; list-style: square; } 44 | .bookkeeping dt { margin: 0.5em 2em 0; } 45 | .bookkeeping dd { margin: 0 3em 0.5em; } 46 | 47 | .critical { margin: 1em; border: double thick red; padding: 1em; background: #FFFFCC; } 48 | .critical > :first-child { margin-top: 0; } 49 | 50 | .example { display: block; color: #222222; background: #FCFCFC; border-left: double; margin-left: 2em; padding-left: 1em; } 51 | td > .example:only-child { margin: 0 0 0 0.1em; } 52 | a.externalDFN { 53 | color: inherit; 54 | border-bottom: 1px dotted #ccc; 55 | text-decoration: none; 56 | } 57 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": 47318 3 | , "contacts": ["dontcallmedom", "caribouW3"] 4 | , "shortName": "audio-output" 5 | , "repo-type": "rec-track" 6 | } 7 | --------------------------------------------------------------------------------