loading
Warning:
Needs HTTPS, and a gesture for the choosersetSinkId ships in Chromium-based browsers and in recent Firefox; selectAudioOutput is narrower still, so check both flags. The chooser must be opened from a user gesture.
Note:
Routing is per element The sink is a property of one <audio> or <video> element, not of the page, so an app playing several sounds sets it on each element it wants moved. An empty device id routes back to the system default.

Support check

IsSupported / IsSelectionSupported

IsSupported reports whether audio can be routed at all; IsSelectionSupported reports whether the browser offers a chooser, which is narrower - an engine can route without offering one.

C#
@inject Bit.Butil.AudioOutput audioOutput

var canRoute  = await audioOutput.IsSupported();
var canChoose = await audioOutput.IsSelectionSupported();
Live sample
support output
Results will appear here when you interact with the samples.

List and choose a device

GetDevices / SelectDevice

GetDevices enumerates the output devices this origin can see - unlabelled until a device permission has been granted once. SelectDevice opens the browser's chooser, which is the way to get a labelled device without asking for a microphone.

C#
var devices = await audioOutput.GetDevices();

var picked = await audioOutput.SelectDevice();   // needs a user gesture
Live sample
device output
Results will appear here when you interact with the samples.

Route a media element

SetSinkId / GetSinkId

SetSinkId moves one element's playback to the chosen device; GetSinkId reads back where it is going. Press play, then switch devices - the sound follows without the element reloading.

C#
<audio @ref="_player" controls src="..."></audio>

await audioOutput.SetSinkId(_player, deviceId);
var current = await audioOutput.GetSinkId(_player);   // "" means the system default
Live sample
routing output
Results will appear here when you interact with the samples.

API reference

Member
Signature
Description
IsSupported
ValueTask<bool> IsSupported()
True when the runtime exposes HTMLMediaElement.setSinkId. Returns default (false) during prerender/SSR instead of throwing.
IsSelectionSupported
ValueTask<bool> IsSelectionSupported()
True when the runtime exposes navigator.mediaDevices.selectAudioOutput.
GetDevices
ValueTask<MediaDeviceInfo[]> GetDevices()
The audio output devices this origin can see. Labels are empty until a device permission has been granted.
SelectDevice
ValueTask<MediaDeviceInfo?> SelectDevice()
Opens the browser's output chooser and returns the picked device, or null when dismissed. Needs a user gesture.
SetSinkId
ValueTask<bool> SetSinkId(ElementReference mediaElement, string deviceId)
Routes one media element's sound to the given device; an empty id means the system default.
GetSinkId
ValueTask<string> GetSinkId(ElementReference mediaElement)
The device an element is currently playing through; empty for the system default.
MediaDeviceInfo
class MediaDeviceInfo { string DeviceId; string Kind; string Label; string GroupId; }
One device, the same type MediaDevices.EnumerateDevices returns - so a device from either service can be routed without re-fetching. Kind is 'audiooutput' here.
An unhandled error has occurred. Reload 🗙