Class

MediaDeviceManager

MediaDeviceManager()

MediaDeviceManager - Centralized cross-browser media device management

This class provides a unified interface for managing media devices across all browsers, with specific optimizations for Chrome, Firefox, Brave, and Safari. It handles:

  • Device enumeration with proper permission handling
  • Cross-browser getUserMedia with fallback strategies
  • Device label revelation through permission requests
  • Browser-specific timing and permission quirks
Constructor

# new MediaDeviceManager()

Example
const deviceManager = new MediaDeviceManager();

// Get all video devices (with labels if possible)
const videoDevices = await deviceManager.listVideoDevices();

// Get media stream with cross-browser device selection
const stream = await deviceManager.getUserMedia({
  video: { deviceId: 'camera-id' },
  audio: { deviceId: 'mic-id' }
});

// Get default device IDs with localStorage persistence
const defaultVideoId = await deviceManager.getDefaultVideoDevice('video-pref-key');

Classes

MediaDeviceManager

Methods

# addDeviceChangeListener(listener)

Add a listener for device change events

Parameters:
Name Type Description
listener function

Callback function to call when devices change

Example
const manager = new MediaDeviceManager();
manager.addDeviceChangeListener(async () => {
  console.log('Devices changed!');
  const devices = await manager.enumerateDevices(true);
  updateDeviceDropdowns(devices);
});

# clearCache()

Clear device cache (useful when devices are plugged/unplugged)

Example
const manager = new MediaDeviceManager();
// After user plugs in a new USB microphone
manager.clearCache();
const devices = await manager.enumerateDevices();

# dispose()

Cleanup method to remove event listeners

Example
const manager = new MediaDeviceManager();
// When component unmounts or app closes
window.addEventListener('beforeunload', () => {
  manager.dispose();
});

# async enumerateDevices(forceRefresh) → {Promise.<Array>}

Enumerate all media devices with browser-specific optimizations

Parameters:
Name Type Default Description
forceRefresh boolean false

Skip cache and force fresh enumeration

  • Array of device info objects
Promise.<Array>
Examples
const manager = new MediaDeviceManager();
const devices = await manager.enumerateDevices();
devices.forEach(device => {
  console.log(`${device.kind}: ${device.label} (${device.deviceId})`);
});
// Force fresh enumeration, bypassing cache
const freshDevices = await manager.enumerateDevices(true);

# getBrowserInfo() → {Object}

Get browser information

  • Browser detection results
Object
Example
const manager = new MediaDeviceManager();
const info = manager.getBrowserInfo();
console.log(`Browser: Chrome=${info.isChrome}, Firefox=${info.isFirefox}, Safari=${info.isSafari}`);
if (info.isIOS) {
  console.log('Running on iOS device');
}

# async getDefaultDevice(deviceType, localStorageKey) → {Promise.<(string|undefined)>}

Get default device ID with optional localStorage persistence

Parameters:
Name Type Description
deviceType string

'videoinput', 'audioinput', or 'audiooutput'

localStorageKey string

Optional key to save/retrieve preference

  • Default device ID
Promise.<(string|undefined)>
Examples
const manager = new MediaDeviceManager();
// Get default microphone, checking localStorage for saved preference
const micId = await manager.getDefaultDevice('audioinput', 'preferredMicrophone');
console.log('Using microphone:', micId);
// Get default camera without persistence
const cameraId = await manager.getDefaultDevice('videoinput');

# async getUserMedia(constraints) → {Promise.<MediaStream>}

Enhanced getUserMedia with cross-browser device selection fallback Uses unified retry strategy with browser-specific constraint ordering

IMPORTANT: Callers MUST stop all tracks when done using the stream to prevent camera/microphone from staying on and to avoid memory leaks.

Parameters:
Name Type Description
constraints Object

Media constraints

  • The media stream (caller must stop tracks when done!)
Promise.<MediaStream>
Example
// Proper usage with cleanup
const stream = await deviceManager.getUserMedia({ video: true, audio: true });
try {
  // Use the stream (attach to video element, etc.)
  videoElement.srcObject = stream;
} finally {
  // ALWAYS clean up when done
  stream.getTracks().forEach(track => track.stop());
  // Or use the convenience method:
  // stream._cleanup();
}

# isSupported() → {boolean}

Check if mediaDevices API is supported

boolean
Example
const manager = new MediaDeviceManager();
if (!manager.isSupported()) {
  alert('Your browser does not support media devices');
}

# async listAudioInputDevices(forceRefresh) → {Promise.<Array>}

Get all audio input devices

Parameters:
Name Type Default Description
forceRefresh boolean false

Skip cache and force fresh enumeration

  • Array of audio input devices
Promise.<Array>
Example
const manager = new MediaDeviceManager();
const microphones = await manager.listAudioInputDevices();
microphones.forEach(mic => console.log(`Microphone: ${mic.label}`));

# async listAudioOutputDevices(forceRefresh) → {Promise.<Array>}

Get all audio output devices

Parameters:
Name Type Default Description
forceRefresh boolean false

Skip cache and force fresh enumeration

  • Array of audio output devices
Promise.<Array>
Example
const manager = new MediaDeviceManager();
const speakers = await manager.listAudioOutputDevices();
speakers.forEach(spk => console.log(`Speaker: ${spk.label}`));

# async listVideoDevices(forceRefresh) → {Promise.<Array>}

Get all video input devices

Parameters:
Name Type Default Description
forceRefresh boolean false

Skip cache and force fresh enumeration

  • Array of video input devices
Promise.<Array>
Example
const manager = new MediaDeviceManager();
const cameras = await manager.listVideoDevices();
cameras.forEach(cam => console.log(`Camera: ${cam.label}`));

# removeDeviceChangeListener(listener)

Remove a device change listener

Parameters:
Name Type Description
listener function

Listener to remove

Example
const manager = new MediaDeviceManager();
const handleChange = () => console.log('Device changed');
manager.addDeviceChangeListener(handleChange);
// Later, when no longer needed:
manager.removeDeviceChangeListener(handleChange);

# saveDevicePreference(localStorageKey, deviceId)

Save device preference to localStorage

Parameters:
Name Type Description
localStorageKey string

Key to save under

deviceId string

Device ID to save

Example
const manager = new MediaDeviceManager();
// Save user's microphone selection
document.getElementById('mic-select').onchange = (e) => {
  manager.saveDevicePreference('preferredMicrophone', e.target.value);
};