Browser media access — audio/video/screen capture, device enumeration, active stream management.
Module media | Source packages/front/fw/src/dom/query/media.js | Deps none | Worker-safe no
Resolve
const media = runtime.resolve('media');
// Returns: { isSupported, permissions, devices, audio, video, screen, streams, select, selected }
API
| Method | Signature | Returns |
|---|---|---|
isSupported |
() => { audio, video, screen } |
Available capabilities |
permissions |
() => { audio, video } |
Inferred from device labels |
devices.refresh |
() => Promise<{ audio[], video[] }> |
Up-to-date device list |
devices.list |
() => { audio[], video[] } |
Current cache |
audio.start |
(opts?) => Promise<StreamHandle> |
Microphone stream |
video.start |
(opts?) => Promise<StreamHandle> |
Camera stream |
screen.start |
(opts?) => Promise<StreamHandle> |
Screen capture stream |
streams.stopAll |
() => void |
Stops all streams |
select |
(type, deviceId) => void |
Saves the preferred device |
selected |
() => { audio, video } |
Saved devices |
dispose |
() => void |
Stops everything, detaches listeners, clears caches |
Capabilities
const caps = media.isSupported();
// → { audio: boolean, video: boolean, screen: boolean }
const perms = media.permissions();
// → { audio: boolean, video: boolean }
// Inferred from the presence of labels on devices (not a native permissions API)
Devices — devices
// Refresh the list (permission must be granted to get labels)
await media.devices.refresh();
// Read
const all = media.devices.list(); // → { audio: MediaDeviceInfo[], video: MediaDeviceInfo[] }
const mics = media.devices.audio(); // → MediaDeviceInfo[]
const cams = media.devices.video(); // → MediaDeviceInfo[]
// Listen for changes (connect/disconnect)
media.devices.listen.add('myListener', ({ audio, video }) => {
console.log('devices updated');
});
media.devices.listen.del('myListener');
The list is automatically refreshed after a successful start() and on the devicechange event.
Audio capture — audio.start(opts?)
const handle = await media.audio.start({
deviceId?: string, // microphone ID (from devices.audio())
sampleRate?: number, // e.g. 44100, 48000
sampleSize?: number, // e.g. 16
channelCount?: number, // 1 = mono, 2 = stereo
echoCancellation?: boolean,
noiseSuppression?: boolean,
autoGainControl?: boolean
});
Video capture — video.start(opts?)
const handle = await media.video.start({
deviceId?: string,
width?: number | { ideal: number }, // e.g. 1280 or { ideal: 1280 }
height?: number | { ideal: number }, // e.g. 720
frameRate?: number | { ideal: number, max: number },
aspectRatio?: number,
facingMode?: 'user' | 'environment'
});
Screen capture — screen.start(opts?)
const handle = await media.screen.start({
frameRate?: number | { ideal: number },
cursor?: 'always' | 'motion' | 'never',
width?: number,
height?: number,
audio?: boolean // include system audio
});
StreamHandle — object returned by start()
{
id: string, // MediaStream.id
type: 'audio' | 'video' | 'screen',
stream: MediaStream, // native stream
tracks: MediaStreamTrack[],
settings: MediaTrackSettings[], // applied parameters
constraints: MediaTrackConstraints[],
stop(): void // stops tracks and removes from registry
}
Active streams — streams
media.streams.list() // → StreamHandle[]
media.streams.get(id) // → StreamHandle | null
media.streams.byType('video') // → StreamHandle[]
media.streams.stop(id) // → void
media.streams.stopAll() // → void
media.streams.stopType('audio') // → void
Device selection memory
media.select('audio', deviceId); // saves the choice for the next start()
media.selected(); // → { audio: string|null, video: string|null }
Global cleanup — dispose()
media.dispose();
// Stops all active streams, detaches the devicechange listener,
// clears device caches and named listeners. Safe to call multiple times.
Examples
Complete — camera with stop
const media = runtime.resolve('media');
const events = runtime.resolve('events');
// Check support
const caps = media.isSupported();
if (!caps.video) {
console.error('Camera not available');
return;
}
// List cameras
await media.devices.refresh();
const cameras = media.devices.video();
console.log('Available cameras:', cameras.map(d => d.label));
// Start capture
const handle = await media.video.start({ width: 1280, height: 720 });
videoElement.srcObject = handle.stream;
videoElement.play();
// Stop
events.on('stopBtn', stopBtn, 'click', () => handle.stop());
Notes
- Device labels are masked until a permission is granted via
start(). - The list is automatically updated after each
start()and ondevicechange. - Screen streams are automatically removed from the registry when the user clicks "Stop sharing".
dispose()stops all streams, detaches thedevicechangelistener, and clears caches and named listeners. Idempotent.