WebRTC 采集音视频数据
- WebRTC 采集音视频数据
- getUserMedia API 简介
- 浏览器兼容性
- getUserMedia 接口格式
- MediaStreamConstraints
- MediaTrackConstraints
- 采集音频数据
- MediaStream 和 MediaStreamTrack
- 本地视频预览
- 切换摄像头显示
- 参考
WebRTC 采集音视频数据
getUserMedia API 简介
HTML5 的 getUserMedia API为用户提供访问硬件设备媒体(摄像头、视频、音频、地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。
getUserMedia API 最初是 navigator.getUserMedia,目前已被最新Web标准废除,变更为 navigator.mediaDevices.getUserMedia,但浏览器支持情况不如旧版 API 普及。
MediaDevices.getUserMedia 方法提示用户允许使用一个视频和/或一个音频输入设备,例如相机或屏幕共享和/或麦克风。如果用户给予许可,就返回一个 Promise 对象,MediaStream 对象作为此 Promise 对象的 Resolved[成功]状态的回调函数参数,相应的,如果用户拒绝了许可,或者没有媒体可用的情况下PermissionDeniedError 或者 NotFoundError 作为此 Promise 的 Rejected[失败]状态的回调函数参数。注意,由于用户不会被要求必须作出允许或者拒绝的选择,所以返回的 Promise 对象可能既不会触发 resolve 也不会触发 reject。
浏览器兼容性
getUserMedia 接口格式
navigator.mediaDevices.getUserMedia(constraints)
.then(function(mediaStream) { ... })
.catch(function(error) { ... })
该接口有一个 MediaStreamConstraints 类型的输入参数,指定请求的媒体类型,主要包含 video 和 audio,必须至少一个类型或者两个同时可以被指定。如果浏览器无法找到指定的媒体类型或者无法满足相对应的参数要求,那么返回的 Promise 对象就会处于 rejected[失败]状态,NotFoundError 作为 rejected[失败]回调的参数。
成功回调函数seccessCallback的参数mediaStream:mediaStream是MediaStream的对象,表示媒体内容的数据流,可以通过URL.createObjectURL转换后设置为Video或Audio元素的src属性来使用,部分较新的浏览器也可以直接设置为srcObject属性来使用。
失败回调函数errorCallback的参数error,可能的异常有:
- AbortError:硬件问题
- NotAllowedError:用户拒绝了当前的浏览器实例的访问请求;或者用户拒绝了当前会话的访问;或者用户在全局范围内拒绝了所有媒体访问请求。
- NotFoundError:找不到满足请求参数的媒体类型。
- NotReadableError:操作系统上某个硬件、浏览器或者网页层面发生的错误导致设备无法被访问。
- OverConstrainedError:指定的要求无法被设备满足。
- SecurityError:安全错误,在getUserMedia() 被调用的 Document
上面,使用设备媒体被禁止。这个机制是否开启或者关闭取决于单个用户的偏好设置。 - TypeError:类型错误,constraints对象未设置[空],或者都被设置为false。
MediaStreamConstraints
MediaStreamConstraints 结构体:
dictionary MediaStreamConstraints {
(boolean) or (MediaTrackConstraints) video = true;
(boolean) or (MediaTrackConstraints) audio = true;
}
video 和 audio 属性可以是 boolean 类型,也可以是 MediaTrackConstraints 类型,只有像 JavaScript 这种弱类型语言才可以做到这一点。因此,我们既可以直接给 video 和 audio 赋值 true/false,简单地指明是否采集视频/音频数据,此时浏览器会采用默认设备和默认参数采集音视频数据;也可以赋值一个 MediaTrackConstraints 类型值,对音视频设备做更精准的设置。
MediaTrackConstraints
MediaTrackConstraints 结构体:
dictionary MediaTrackConstraints {
// Instance properties of video tracks
ConstrainULong width;
ConstrainULong height;
ConstrainDouble aspectRatio; // 宽高比
ConstrainDouble frameRate;
ConstrainDOMString facingMode; // 前置/后置摄像头
ConstrainDOMString resizeMode; // 缩放或裁剪
// Instance properties of audio tracks
ConstrainULong sampleRate;
ConstrainULong sampleSize
ConstrainULong channelCount;
ConstrainDouble latency; // 目标延迟
ConstrainDouble volume; // 已废弃
ConstrainBoolean autoGainControl; // 自动增益
ConstrainBoolean echoCancellation; // 回声消除
ConstrainBoolean noiseSuppression; // 降噪
// Instance properties of all media tracks
ConstrainDOMString deviceId;
ConstrainDOMString groupId;
}
MediaTrackConstraints官方文档:https://developer.mozilla.org/en-US/docs/Web/API/MediaTrackConstraints
采集音频数据
通用模板:
// 从设备选项栏里选择某个设备
var deviceId = ...;
// 设置采集限制
var constraints = {
// 音频约束
audio: {
deviceId: deviceId?{exact:deviceId}:undefined,
sampleRate: 16000, // 采样率
sampleSize: 16, // 每个采样点大小的位数
channelCount: 1, // 通道数
volume: 1, // 从 0(静音)到 1(最大音量)取值,被用作每个样本值的乘数
echoCancellation: true, // 开启回音消除
noiseSuppression: true, // 开启降噪功能
},
// 视频约束
video: false
}
// 开始采集数据
navigator.mediaDevices.getUserMedia(constraints)
.then(gotMediaStream)
.catch(handleError);
// 采集到某路流
function gotMediaStream(stream) {
...
}
// 处理错误
function handleError {
...
}
更详细的例子:HTML + JavaScript 实现网页录制音频与下载
MediaStream 和 MediaStreamTrack
- MediaStreamTrack 称为“轨”,表示单一类型的媒体源。
- MediaStream 称为“流”,它包含 0 个或多个 MediaStreamTrack。
MediaStream 有两个作用:
- 作为录制或者渲染的源。可以录制成文件,也可以通过浏览器的<video>标签播放出来。
- 同一 MediaStream 中的 MediaStreamTrack 会进行同步。
本地视频预览
当使用 getUserMedia() 接口获得本地音视频流 MediaStream 后,赋值给 H5 的 <video> 标签的 srcObject 属性,就可以显示出来。
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Camera Video Display</title>
</head>
<body>
<video id="video" width="640" height="480" autoplay="autoplay"></video>
<script src="index.js"></script>
</body>
</html>
video 标签设置 autoplay,可以自动显示采集的数据。
index.js:
// 从 HTML 页面获得 video 标签
var videoElement = document.getElementById('video');
// getUserMedia 的采集限制
var constraints = {
video: true,
audio: false
};
// 检查浏览器是否支持 getUserMedia API
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
console.error("您的浏览器不支持 getUserMedia API");
} else {
navigator.mediaDevices.getUserMedia(constraints)
.then(gotLocalStream)
.catch(handleError);
}
function gotLocalStream(mediaStream) {
// 将采集到的视频信息显示在 video 标签中
videoElement.srcObject = mediaStream;
}
function handleError() {
// 处理错误
console.error("获取摄像头错误:", err);
}
切换摄像头显示
结合前一篇文章 WebRTC 遍历音视频设备,我们可以用一个 select 选择不同的摄像头,切换摄像头后,select.value 就是当前的 deviceId,重新设置 constraints 中 video 的 deviceId,使用 getUserMedia() 接口获得本地音视频流 MediaStream 后,赋值给 H5 的 <video> 标签的 srcObject 属性,就可以实现切换摄像头的效果。
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Camera Video Display</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="center-select">
<label>请选择你的采集设备:</label>
<select id="cameraSelect"></select>
</div>
<div class="center-video">
<video id="video" width="640" height="480" autoplay="autoplay"></video>
</div>
<script src="index.js"></script>
</body>
</html>
styles.css:
.center-select {
display: flex;
justify-content: center;
align-items: center;
}
.center-video {
display: flex;
justify-content: center;
align-items: center;
/* 使用视口高度来使容器填满整个屏幕 */
height: 100vh;
}
index.js:
// 使用严格语法
'use strict'
var cameraSelect = document.getElementById('cameraSelect');
// 从 HTML 页面获得 video 标签
var videoElement = document.getElementById('video');
// 获取摄像头列表并填充到 select 元素中
async function getCameraDevices() {
try {
const devices = await navigator.mediaDevices.enumerateDevices();
const cameras = devices.filter(device => device.kind === 'videoinput');
cameras.forEach(camera => {
const option = document.createElement('option');
option.value = camera.deviceId;
option.text = camera.label || `Camera ${cameraSelect.length + 1}`;
cameraSelect.appendChild(option);
});
} catch (error) {
console.error('Error enumerating devices:', error);
}
}
// 切换摄像头
async function switchCamera() {
const deviceId = cameraSelect.value;
const constraints = {
video: {
deviceId: deviceId ? { exact: deviceId } : undefined
},
audio: false
};
try {
const mediaStream = await navigator.mediaDevices.getUserMedia(constraints);
videoElement.srcObject = mediaStream;
} catch (error) {
console.error('Error switching camera:', error);
}
}
// 初始化页面
async function init() {
await getCameraDevices();
if (cameraSelect.length > 0) {
// 默认选择第一个摄像头
await switchCamera();
}
}
cameraSelect.addEventListener('change', switchCamera);
// 初始化
init();
运行效果:
切换采集设备后,下面的显示内容也会跟着切换。
参考
- https://www.cnblogs.com/cangqinglang/p/10210826.html
- https://webrtc.org.cn/20200318-api/
- https://blog.csdn.net/xiehuanbin/article/details/131512316