JavaScript 获得 Audio.setSinkId 的权限

Hus*_*012 5 javascript audio google-chrome google-chrome-app

我正在尝试更改sinkIdchrome 应用程序中的音频元素。

代码:

var audio = new Audio();
audio.setSinkId("communications");
Run Code Online (Sandbox Code Playgroud)

我会收到这个错误:

DOMException: No permission to use requested device

因此,为了获得许可,我尝试这样做:

navigator.webkitGetUserMedia({
    audio: true
}, (stream) => {
    console.log("stream: ", stream);
}, (error) => {
    console.log("error: ", error);
});
Run Code Online (Sandbox Code Playgroud)

但现在我明白了:

error:
NavigatorUserMediaError {name: "InvalidStateError", message: "", constraintName: ""}
Run Code Online (Sandbox Code Playgroud)

不知道有没有

  • “音频”不可用(但实际上应该)
  • 我需要一个安全的连接(但我如何在 Chrome 应用程序上获得它?)

Kai*_*ido 7

现在有一个mediacapture-output WG,他们确实为MediaDevices接口定义了一个扩展,它添加了一个selectAudioOutput()处理内部权限请求的方法。
此方法将返回一个deviceInfo对象,您可以从中提取deviceId要传递给HTMLMediaElement.setSinkId().

const deviceInfo = await navigator.mediaDevices.selectAudioOuput();
audio_elem.setSinkId(deviceInfo.deviceId);
Run Code Online (Sandbox Code Playgroud)

另请注意,有关将此setSinkId方法重命名为 的积极讨论setAudioOutput,因此当浏览器开始实施所有这些时,它可能会再次更改。

Firefox 93 开始在media.setsinkid.enabled标志下公开此方法。



或者应该有一个Permissions.request()方法,它应该接受一个PermissionDescriptor对象,该对象本身应该支持value的PermissionName"speaker-selection"

所以这会给我们

 await navigator.permissions.request( { name: "speaker-selection" } );
 const all_devices = await navigator.mediaDevices.enumerateDevices();
 const audio_outs = all_devices.filter( ({ kind }) => kind === "audiooutput" );
 // ...
 audioElem.setSinkId( audio_outs[ n ].deviceId );
Run Code Online (Sandbox Code Playgroud)

但截至今天 2021 年 8 月,似乎仍然没有浏览器支持这一点。

Chrome(目前唯一支持的MediaElement.setSinkId())确实公开了Permission.request()下的方法chrome://flags/#enable-experimental-web-platform-features,但它们仍然不支持PermissionName "speaker-selection"

所以我们仍然需要请求"microphone"一个不太理想的解决方法。

(async () => {
  const query = await navigator.permissions.query( { name: "microphone" } );
  switch( query.state ) {
    case "denied": throw new Error('denied');
    case "prompt":
      await queryUserMedia();
      await getListOfDevices();
      break;
    case "granted": await getListOfDevices();
  }
  function queryUserMedia() {
    return navigator.mediaDevices.getUserMedia( { audio: true } );
  }
  async function getListOfDevices() {
    const all_devs = await navigator.mediaDevices.enumerateDevices();
    const audioouts = all_devs.filter( ({ kind }) => kind === "audiooutput" );
    const options = audioouts.map( ( dev, index ) => {
      const name = dev.label || ('audio out ' + index );
      return new Option( name , dev.deviceId );
    } );
    sel.append.apply( sel, options );
    sel.onchange = e => aud.setSinkId( sel.value );
  }

})().catch( console.error );
Run Code Online (Sandbox Code Playgroud)

作为一个小提琴,因为 Stack-Snippets iframes 不允许使用麦克风。


PS:请注意,如果/当浏览器中支持定义的API规格,那就甚至有可能运行在非安全环境这段代码