问题
I'm trying to change the sinkId at an Audio-Element in a chrome app.
Code:
var audio = new Audio();
audio.setSinkId("communications");
I'll get this error:
DOMException: No permission to use requested device
So to get the permission I tried to do that:
navigator.webkitGetUserMedia({
audio: true
}, (stream) => {
console.log("stream: ", stream);
}, (error) => {
console.log("error: ", error);
});
But now I get this:
error:
NavigatorUserMediaError {name: "InvalidStateError", message: "", constraintName: ""}
I don't know, whether
- 'audio' is not available (but actually it should)
- I need a secure connection (but how do I get this at a chrome app?)
回答1:
Per specs, there should be a Permissions.request() method, which should accept a PermissionDescriptor object which itself should support a PermissionName of value "speaker".
So per specs, that would give us
await navigator.permissions.request( { name: "speaker" } );
const all_devices = await navigator.mediaDevices.enumerateDevices();
const audio_outs = all_devices.filter( ({ kind }) => kind === "audiooutput" );
// ...
audioElem.setSinkId( audio_outs[ n ].deviceId );
But as of today december of 2019, no browser supports any of this...
Chrome (which is the only one currently supporting MediaElement.setSinkId()) does expose the Permission.request() method under chrome://flags/#enable-experimental-web-platform-features, but they still don't support the PermissionName "speaker" yet.
So we still need the less than optimal workaround of requesting for the "microphone" one instead.
(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 );
As a fiddle since Stack-Snippets iframes won't allow for the use of the microphone.
Ps: Note that if/when browsers were supporting the specs defined API, then it would even be possible to run this code on non-secure contexts.
来源:https://stackoverflow.com/questions/48854159/javascript-get-permission-for-audio-setsinkid