媒体设备入门
当为网络开发时,WebRTC标准提供了用于访问连接到计算机或智能手机的摄像头和麦克风的API。这些设备通常被称为媒体设备,可以通过navigator.mediaDevices对象使用JavaScript访问,该对象实现了媒体设备界面。通过该对象,我们可以枚举所有连接的设备,侦听设备更改(当设备连接或断开连接时),并打开设备以检索媒体流(见下文)。
最常见的使用方法是通过getUserMedia()函数,该函数返回一个promise,该promise将解析为匹配媒体设备的MediaStream。此函数接受一个MediaStreamConstraints对象,该对象指定了我们的需求。例如,要简单地打开默认麦克风和相机,我们将执行以下操作。
调用getUserMedia()将触发权限请求。如果用户接受了许可,promise将通过包含一个视频和一个音频曲目的MediaStream解决。如果权限被拒绝,则抛出PermissionDeniedError。如果没有连接匹配的设备,将引发NotFoundError。
MDN web文档中提供了MediaDevices接口的完整API参考。
查询媒体设备
在更复杂的应用程序中,我们很可能希望检查所有连接的摄像头和麦克风,并向用户提供适当的反馈。这可以通过调用函数enumerateDevice()来完成。这将返回一个promise,该promise解析为描述每个已知媒体设备的MediaDevicesInfo数组。我们可以使用它向用户呈现一个UI,让他们选择自己喜欢的UI。每个MediaDevicesInfo都包含一个名为kind的属性,其值为audioinput、audiooutput或videoinput,指示它是什么类型的媒体设备。
监听设备变化
大多数计算机支持在运行时插入各种设备。它可以是通过USB连接的网络摄像头、蓝牙耳机或一组外部扬声器。为了正确地支持这一点,web应用程序应该侦听媒体设备的更改。这可以通过为devicechange事件向navigator.mediaDevices添加侦听器来完成。
// 使用提供的一组相机更新选择元素
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));
}
//获取特定类型的设备阵列
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// 连接初始摄像机组
const videoCameras = getConnectedDevices('videoinput');
updateCameraList(videoCameras);
// 侦听媒体设备的更改并相应更新列表
navigator.mediaDevices.addEventListener('devicechange', event => {
const newCameraList = getConnectedDevices('video');
updateCameraList(newCameraList);
});
Media constraints:媒体限制
约束对象必须实现MediaStreamConstraints()接口,我们将其作为参数传递给getUserMedia(),允许我们打开符合特定要求的媒体设备。这一要求可以是非常松散的定义(音频和/或视频),也可以是非常具体的定义(最小摄像机分辨率或确切的设备ID)。建议使用getUserMedia()API的应用程序首先检查现有设备,然后使用deviceId约束指定与确切设备匹配的约束。如果可能,还将根据约束条件配置设备。我们可以在麦克风上启用回声消除,或者设置摄像机视频的特定或最小宽度和高度。
async function getConnectedDevices(type) {
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.filter(device => device.kind === type)
}
// 打开至少具有minWidth和minHeight功能的摄像机
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) {
// 打开第一个分辨率为1280x720像素的可用摄像机
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</video></head>
<body>
<video id="localVideo" autoplay playsinline controls="false"/>
</body>
</html>