在进行 Web 开发时,WebRTC 标准提供了一些 API,用于访问
摄像头和麦克风已连接到计算机或智能手机。这些设备
通常称为媒体设备,可通过 JavaScript 进行访问
通过 navigator.mediaDevices
对象实现,该对象会实现 MediaDevices
界面。通过此对象,我们可以枚举所有连接的设备,
设备更改(当设备连接或断开连接时),并打开设备
检索媒体流(见下文)。
最常见的用法是通过函数 getUserMedia()
返回一个 promise,该 promise 会针对匹配媒体解析为 MediaStream
设备。此函数接受一个 MediaStreamConstraints
对象,
规定了我们的要求例如,要直接打开
默认麦克风和摄像头,我们会执行以下操作。
使用 Promise
const constraints = {
'video': true,
'audio': true
}
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
console.log('Got MediaStream:', stream);
})
.catch(error => {
console.error('Error accessing media devices.', error);
});
使用 async/await
const openMediaDevices = async (constraints) => {
return await navigator.mediaDevices.getUserMedia(constraints);
}
try {
const stream = openMediaDevices({'video':true,'audio':true});
console.log('Got MediaStream:', stream);
} catch(error) {
console.error('Error accessing media devices.', error);
}
调用 getUserMedia()
会触发权限请求。如果用户
接受权限后,promise 会通过包含以下内容的 MediaStream
进行解析:
一个视频和一个音轨。如果权限被拒绝,
抛出 PermissionDeniedError
。如果没有匹配设备
连接,系统会抛出 NotFoundError
。
有关 MediaDevices
接口的完整 API 参考文档,请访问 MDN 网页
文档。
查询媒体设备
在更复杂的应用中,我们很可能需要检查所有
连接摄像头和麦克风,并向
用户。这可以通过调用函数 enumerateDevices()
来实现。这将
返回一个 promise,该 promise 可解析为 MediaDevicesInfo
数组,该数组描述了
每台已知的媒体设备我们可以用它来向用户展示界面
会选择自己喜欢的那一项每个 MediaDevicesInfo
都包含一个名为
kind
,值为 audioinput
、audiooutput
或 videoinput
,表示
媒体设备的类型。
使用 Promise
function getConnectedDevices(type, callback) {
navigator.mediaDevices.enumerateDevices()
.then(devices => {
const filtered = devices.filter(device => device.kind === type);
callback(filtered);
});
}
getConnectedDevices('videoinput', cameras => console.log('Cameras found', cameras));
使用 async/await
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
const videoCameras = getConnectedDevices('videoinput');
console.log('Cameras found:', videoCameras);
监听设备更改
大多数计算机都支持在运行时插入各种设备。它可能是
通过 USB 连接的摄像头、蓝牙耳机或一组外部扬声器。在
为了正确支持此操作,Web 应用应监听更改
各种媒体设备这可以通过向
navigator.mediaDevices
,表示 devicechange
事件。
// Updates the select element with the provided set of cameras
function updateCameraList(cameras) {
const listElement = document.querySelector('select#availableCameras');
listElement.innerHTML = '';
cameras.map(camera => {
const cameraOption = document.createElement('option');
cameraOption.label = camera.label;
cameraOption.value = camera.deviceId;
}).forEach(cameraOption => listElement.add(cameraOption));
}
// Fetch an array of devices of a certain type
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// Get the initial set of cameras connected
const videoCameras = getConnectedDevices('videoinput');
updateCameraList(videoCameras);
// Listen for changes to media devices and update the list accordingly
navigator.mediaDevices.addEventListener('devicechange', event => {
const newCameraList = getConnectedDevices('video');
updateCameraList(newCameraList);
});
媒体限制条件
constraints 对象,必须实现 MediaStreamConstraints
作为参数传递给 getUserMedia()
,我们可以打开
符合特定要求的媒体设备。这项要求可能
宽泛定义(音频和/或视频),或非常具体(最少使用摄像头)
分辨率或确切的设备 ID)。建议使用
getUserMedia()
API 首先检查现有设备,然后指定一个
使用 deviceId
约束条件与确切设备匹配的约束条件。
如果可能,还会根据限制条件来配置设备。周三
可以在麦克风上启用回声消除功能,或者设置特定宽度或最小宽度
与镜头之间的距离和高度
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// Open camera with at least minWidth and minHeight capabilities
async function openCamera(cameraId, minWidth, minHeight) {
const constraints = {
'audio': {'echoCancellation': true},
'video': {
'deviceId': cameraId,
'width': {'min': minWidth},
'height': {'min': minHeight}
}
}
return await navigator.mediaDevices.getUserMedia(constraints);
}
const cameras = getConnectedDevices('videoinput');
if (cameras && cameras.length > 0) {
// Open first available video camera with a resolution of 1280x720 pixels
const stream = openCamera(cameras[0].deviceId, 1280, 720);
}
如需查看 MediaStreamConstraints
接口的完整文档,请访问
在 MDN Web 上
文档。
本地播放
打开媒体设备且找到可用的 MediaStream
后,
可以将其分配给视频或音频元素,以便在本地播放视频流。
async function playVideoFromCamera() {
try {
const constraints = {'video': true, 'audio': true};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
const videoElement = document.querySelector('video#localVideo');
videoElement.srcObject = stream;
} catch(error) {
console.error('Error opening video camera.', error);
}
}
与 getUserMedia()
搭配使用的典型视频元素所需的 HTML 将
通常具有 autoplay
和 playsinline
属性。autoplay
属性会自动播放分配给该元素的新视频流。
playsinline
属性允许视频以内嵌方式(而非仅以完整方式)播放
。此外,建议使用
controls="false"
(适用于直播,除非用户应该能够暂停直播)
。
<html>
<head><title>Local video playback</title></head>
<body>
<video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>