EnumerateDevices: примеры (JAVASCRIPT)
enumerateDevices: Promise> Основы функции enumerateDevices
Функция enumerateDevices() является методом интерфейса MediaDevices. Она используется для асинхронного запроса списка доступных медиаустройств на компьютере пользователя, таких как микрофоны, камеры, динамики и аудиовыходы.
Функция не принимает никаких аргументов. Она вызывается как navigator.mediaDevices.enumerateDevices().
Метод возвращает Promise, который разрешается в массив объектов MediaDeviceInfo. Каждый объект описывает одно устройство и содержит следующие свойства:
- deviceId (String): уникальный идентификатор устройства. Может быть пустой строкой, если пользователь не предоставил разрешение на доступ к соответствующему типу устройства.
- kind (String): тип устройства. Возможные значения:
'audioinput'(микрофон),'videoinput'(камера),'audiooutput' (динамик, аудиовыход). - label (String): человекочитаемое название устройства (например, 'Built-in Microphone'). Строка будет пустой, если разрешение на доступ не было предоставлено.
- groupId (String): идентификатор группы, к которой принадлежит устройство. Устройства, физически расположенные в одном аппаратном блоке (например, камера и микрофон в ноутбуке), имеют одинаковый
groupId.
Использование функции актуально при создании интерфейсов для выбора устройств в приложениях для видеозвонков, аудиозаписи, стриминга.
Простые примеры использования
Базовый пример получения списка всех устройств:
async function listAllDevices() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
console.log('Найдено устройств:', devices.length);
devices.forEach(device => {
console.log(`${device.kind}: ${device.label || 'Название скрыто'} id=${device.deviceId}`);
});
} catch (err) {
console.error('Ошибка при перечислении устройств:', err);
}
}
listAllDevices();// Вывод в консоль (пример): // Найдено устройств: 4 // audioinput: Internal Microphone id=default // videoinput: FaceTime HD Camera id=abc123... // audiooutput: Internal Speakers id=default // audioinput: USB Microphone id=def456...
Фильтрация только камер:
navigator.mediaDevices.enumerateDevices()
.then(devices => {
const cameras = devices.filter(device => device.kind === 'videoinput');
console.log('Камеры:', cameras);
});Альтернативные методы в JavaScript
Прямых аналогов enumerateDevices() в JavaScript нет. Однако часто её используют в связке с другими методами MediaDevices API:
- getUserMedia(constraints): Запрашивает доступ к конкретным медиаустройствам (камере, микрофону) и возвращает медиапоток. Косвенно, после успешного вызова
getUserMedia, функцияenumerateDevices()начинает возвращать непустые метки (label) устройств, так как пользователь дал разрешение. Эти функции решают разные задачи: одна запрашивает доступ, другая — только перечисляет доступное. - getDisplayMedia(constraints): Специализированный метод для захвата экрана или его части. Не является альтернативой для перечисления физических устройств ввода/вывода.
enumerateDevices() предпочтительнее использовать для построения списка выбора устройств в настройках приложения. getUserMedia() — для непосредственного начала захвата медиа.
Аналоги в других языках программирования
Концепция перечисления устройств существует и в других средах, но реализация сильно зависит от платформы и фреймворков.
Python (библиотека PyAudio):
import pyaudio
p = pyaudio.PyAudio()
# Получение количества и информации об аудиоустройствах
for i in range(p.get_device_count()):
info = p.get_device_info_by_index(i)
print(f"{i}: {info['name']} (in:{info['maxInputChannels']}, out:{info['maxOutputChannels']})")
p.terminate()# Пример вывода: # 0: HDA Intel PCH: ALC262 Analog (hw:0,0) (in:2, out:2) # 1: HDA NVidia: HDMI 0 (hw:1,3) (in:0, out:8)
PHP (расширение OpenCV или комманда shell): Прямого стандартного способа нет. Часто используют вызов системных утилит.
// Пример для Linux с использованием shell
$cameras = shell_exec('v4l2-ctl --list-devices 2>/dev/null');
echo $cameras;C/C++ (Windows, Media Foundation или DirectShow): Требуется сложная, многоэтапная работа с COM-объектами для создания перечислителя устройств.
Ключевое отличие веб-API JavaScript — его кроссплатформенность, асинхронность и сильная зависимость от модели разрешений браузера (метки устройств скрыты без разрешения).
Типичные ошибки
1. Игнорирование асинхронной природы функции. Попытка использовать результат сразу, без await или then.
// Неправильно
const devices = navigator.mediaDevices.enumerateDevices();
devices.forEach(d => console.log(d.label)); // Ошибка: devices.forEach is not a function
// Правильно
navigator.mediaDevices.enumerateDevices().then(devices => {
devices.forEach(d => console.log(d.label));
});2. Предположение, что label всегда будет заполнен. До получения разрешения через getUserMedia, метки устройств будут пустыми строками.
// label может быть пустой строкой
navigator.mediaDevices.enumerateDevices().then(devices => {
const mic = devices.find(d => d.kind === 'audioinput');
// mic.label может быть ''
if (mic && mic.label) {
console.log(`Микрофон: ${mic.label}`);
} else {
console.log('Доступ к названию микрофона запрещен или его нет.');
}
});3. Отсутствие проверки поддержки MediaDevices API.
if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {
console.error('Ваш браузер не поддерживает enumerateDevices');
return;
}
// Дальнейший вызов функции...История изменений
Спецификация Media Capture and Streams, частью которой является enumerateDevices(), развивается, но сама функция остается относительно стабильной. Основные изменения касаются контекста безопасности:
- Функция изначально была доступна только в безопасных контекстах (HTTPS). В локальной разработке (localhost) она также работает.
- Политика в отношении свойства
labelужесточилась: для его заполнения теперь почти всегда требуется активное разрешение пользователя (черезgetUserMediaили Persistent Permission). Раньше в некоторых браузерах метки были доступны сразу. - Был добавлен тип устройства
'audiooutput'для устройств вывода звука, что позволило управлять, например, выбором колонок или гарнитуры.
Рекомендуется всегда работать с API, учитывая, что label может быть пустым, и обеспечивать корректную обработку таких случаев в интерфейсе.
Расширенные примеры
1. Динамическое построение select-списка для выбора камеры с обновлением при подключении нового устройства.
async function populateCameraSelect(selectElementId) {
const selectEl = document.getElementById(selectElementId);
selectEl.innerHTML = '';
const devices = await navigator.mediaDevices.enumerateDevices();
const cameras = devices.filter(d => d.kind === 'videoinput');
selectEl.innerHTML = '';
cameras.forEach(camera => {
const option = document.createElement('option');
option.value = camera.deviceId;
// Используем label или создаем generic-название
option.text = camera.label || `Камера ${selectEl.length + 1}`;
selectEl.appendChild(option);
});
// Слушаем событие изменения доступных устройств
navigator.mediaDevices.ondevicechange = async () => {
console.log('Список устройств изменился, обновляем...');
await populateCameraSelect(selectElementId);
};
}
// Вызов: populateCameraSelect('cameraSelect');2. Получение полной информации о группировке устройств (например, найти микрофон и камеру одного硬件ного блока).
async function getDeviceGroups() {
const devices = await navigator.mediaDevices.enumerateDevices();
const groups = {};
devices.forEach(device => {
if (!groups[device.groupId]) {
groups[device.groupId] = [];
}
groups[device.groupId].push(device);
});
// Вывод групп, где более одного устройства
Object.values(groups).forEach(group => {
if (group.length > 1) {
console.log('Устройства в одной группе:', group.map(d => `${d.kind}: ${d.label}`));
}
});
return groups;
}3. Комбинированный пример: запрос разрешения на камеру для разблокировки label и последующее перечисление.
async function unlockLabelsAndList() {
// Сначала запрашиваем доступ к медиа (любое устройство)
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: false });
// Останавливаем стрим, он нам больше не нужен, разрешение остаётся
stream.getTracks().forEach(track => track.stop());
// Теперь enumerateDevices вернет устройства с заполненными label
const devices = await navigator.mediaDevices.enumerateDevices();
const detailedDevices = devices.filter(d => d.label);
console.log('Устройства с разрешенными метками:', detailedDevices);
} catch (err) {
console.error('Не удалось получить разрешение:', err);
}
}