1

Please consider the code below:

navigator.mediaDevices.getUserMedia({audio: true}).then(function() {
    navigator.mediaDevices.enumerateDevices().then((devices) => {
        devices.forEach(function(device1, k1) {
            if (device1.kind == 'audiooutput' && device1.deviceId == 'default') {
                const speakersGroupId = device1.groupId;
                devices.forEach(function(device2, k2) {
                    if (device2.groupId == speakersGroupId && ['default', 'communications'].includes(device2.deviceId) === false) {
                        const speakersId = device2.deviceId;
                        const constraints = {
                            audio: {
                                deviceId: {
                                    exact: speakersId
                                }
                            }
                        };
                        console.log('Requesting stream for deviceId '+speakersId);
                        navigator.mediaDevices.getUserMedia(constraints).then((stream) => { // **this always fails**
                            console.log(stream);
                        });
                    }
                });
            }
        });
    });
});

The code asks for permissions via the first getUserMedia, then enumerates all devices, picks the default audio output then tries to get a stream for that output.

But it will always throw the error: OverconstrainedError { constraint: "deviceId", message: "", name: "OverconstrainedError" } when getting the audio stream.

There is nothing I can do in Chrome (don't care about other browsers, tested Chrome 108 and 109 beta) to get this to work.

I see a report here that it works, but not for me.

Please tell me that I'm doing something wrong, or if there's another way to get the speaker stream that doesn't involve chrome.tabCapture or chrome.desktopCapture.

Chrome MV3 extension ways are welcomed, not only HTML5.

Dude
  • 121
  • 8

1 Answers1

0

.getUserMedia() is used to get input streams. So, when you tell it to use a speaker device, it can't comply. gUM's error reporting is, umm, confusing (to put it politely).

To use an output device, use element.setSinkId(deviceId). Make an audio or video element, then set its sink id. Here's the MDN example; it creates an audio element. You can also use a preexisting audio or video element.

const devices = await navigator.mediaDevices.enumerateDevices()
const audioDevice = devices.find((device) => device.kind === 'audiooutput')
const audio = document.createElement('audio')
await audio.setSinkId(audioDevice.deviceId)
console.log(`Audio is being played on ${audio.sinkId}`)
O. Jones
  • 103,626
  • 17
  • 118
  • 172