3、媒体流基础:MediaStream API详解、获取音频与视频轨道、控制摄像头与麦克风。
好,我们正式开始接触 WebRTC 里最实在的东西——媒体流。
你想想看,WebRTC 之所以叫 WebRTC,核心就是能传视频、能传音频。而这一切的起点,就是 MediaStream 这个 API。说白了,它就是浏览器里摄像头和麦克风的“数据管家”。
我个人习惯把 MediaStream 理解成一个“轨道容器”。它本身不生产数据,但它负责把音频轨道和视频轨道组织起来,统一调度。你拿到一个 MediaStream 对象,就等于拿到了一个或多个媒体源的访问权。
3.1 什么是 MediaStream?
MediaStream 是一个接口,代表一个媒体内容的流。一个流可以包含多个轨道,比如一个视频轨道和一个音频轨道。每个轨道都是一个 MediaStreamTrack 对象。
我在项目中遇到过不少新手,以为 MediaStream 就是摄像头画面。其实不对。它更像是一个“遥控器”,你可以通过它来开关麦克风、切换摄像头、甚至调整分辨率。
核心概念:
- MediaStream:媒体流容器,管理多个轨道。
- MediaStreamTrack:单个媒体轨道,可以是音频或视频。
- getUserMedia():获取用户媒体设备的入口 API。
嗯,这里要注意:一个 MediaStream 可以包含多个同类型的轨道吗?理论上可以,但实际开发中很少这么干。大部分场景下,一个视频轨道、一个音频轨道就够了。
3.2 获取音频与视频轨道
获取媒体流的核心方法就是 navigator.mediaDevices.getUserMedia()。这个方法返回一个 Promise,resolve 后给你一个 MediaStream 对象。
我曾经踩过一个坑:在本地测试时一切正常,但部署到 HTTPS 环境后,getUserMedia 直接报错。后来才发现,这个 API 在安全上下文(HTTPS 或 localhost)下才能用。你想想看,要是随便一个 HTTP 网站都能调你摄像头,那还得了?
3.2.1 基本用法
// 请求摄像头和麦克风
async function getMedia() {
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: true
});
// 拿到 stream 后,可以赋值给 video 元素
const videoElement = document.getElementById('localVideo');
videoElement.srcObject = stream;
} catch (err) {
console.error('获取媒体流失败:', err);
}
}
这段代码很简单,但有几个细节值得注意:
video: true表示请求视频轨道,默认使用前置摄像头(移动端)。audio: true表示请求音频轨道,默认使用系统默认麦克风。- 如果用户拒绝授权,Promise 会 reject,错误类型通常是
NotAllowedError。
3.2.2 精细控制:指定分辨率与帧率
在实际项目中,你很少会直接用 true 来请求媒体。更常见的做法是传入一个约束对象,精确控制视频的宽度、高度、帧率。
const constraints = {
video: {
width: { min: 640, ideal: 1280, max: 1920 },
height: { min: 480, ideal: 720, max: 1080 },
frameRate: { ideal: 30, max: 60 }
},
audio: {
echoCancellation: true,
noiseSuppression: true,
sampleRate: 48000
}
};
const stream = await navigator.mediaDevices.getUserMedia(constraints);
这里我解释一下约束的含义:
| 约束属性 | 说明 | 常用值 |
|---|---|---|
width / height |
视频分辨率,支持 min、ideal、max | 640x480、1280x720、1920x1080 |
frameRate |
视频帧率,单位 fps | 15、30、60 |
echoCancellation |
回声消除,音频通话必备 | true / false |
noiseSuppression |
降噪,减少背景噪音 | true / false |
sampleRate |
音频采样率,越高音质越好 | 44100、48000 |
小技巧:ideal 值表示“最希望的值”,浏览器会尽量满足。如果设备不支持,会自动降级到最接近的值。所以别把 ideal 设得太离谱,否则可能拿到很差的画质。
3.3 控制摄像头与麦克风
拿到 MediaStream 之后,真正的控制才刚刚开始。你可以通过 getTracks() 方法获取所有轨道,然后对每个轨道进行启用、禁用、甚至替换。
3.3.1 启用/禁用轨道
最常见的需求就是“静音”和“关闭摄像头”。这不需要重新调用 getUserMedia,直接操作轨道即可。
// 获取所有视频轨道
const videoTracks = stream.getVideoTracks();
videoTracks.forEach(track => {
track.enabled = false; // 关闭摄像头画面(但设备仍在工作)
});
// 获取所有音频轨道
const audioTracks = stream.getAudioTracks();
audioTracks.forEach(track => {
track.enabled = false; // 静音麦克风
});
注意:enabled = false 只是让轨道不输出数据,但摄像头指示灯可能还亮着。如果你想彻底释放设备,需要用 stop() 方法。
3.3.2 停止轨道(释放设备)
我记得有一次做视频会议项目,用户挂断后摄像头灯还亮着,被投诉了。后来发现是忘了调用 stop()。
// 停止所有轨道
stream.getTracks().forEach(track => track.stop());
// 或者只停止视频轨道
stream.getVideoTracks().forEach(track => track.stop());
调用 stop() 后,轨道会进入 ended 状态,设备被释放,指示灯熄灭。这是一个好习惯,尤其是在单页应用中,页面不刷新,资源需要手动清理。
3.3.3 切换摄像头(前后置)
移动端开发中,切换前后摄像头是高频需求。这需要用到 MediaStreamTrack.applyConstraints() 或者重新调用 getUserMedia。
我个人更推荐重新调用 getUserMedia,因为切换设备时往往需要改变分辨率等参数,重新获取更干净。
async function switchCamera() {
// 先停止当前视频轨道
const videoTracks = localStream.getVideoTracks();
videoTracks.forEach(track => track.stop());
// 重新获取,切换 facingMode
const newStream = await navigator.mediaDevices.getUserMedia({
video: {
facingMode: 'environment' // 'user' 前置,'environment' 后置
},
audio: false
});
// 替换视频轨道
const newVideoTrack = newStream.getVideoTracks()[0];
const oldVideoTrack = localStream.getVideoTracks()[0];
localStream.removeTrack(oldVideoTrack);
localStream.addTrack(newVideoTrack);
// 更新 video 元素
videoElement.srcObject = localStream;
}
避坑指南:我曾经在切换摄像头时直接替换了 srcObject,结果导致音频轨道也丢失了。正确做法是只替换视频轨道,保留音频轨道。上面代码中的 removeTrack 和 addTrack 就是干这个的。
3.4 知识体系总览
为了让你更直观地理解 MediaStream 的整体结构,我画了一张图。这张图涵盖了从设备获取到轨道控制的完整链路。
3.5 常见问题与调试技巧
最后,分享几个我在实际开发中遇到的坑,希望能帮你少走弯路。
- 权限问题:如果 getUserMedia 一直 reject,先检查浏览器地址栏是否有摄像头/麦克风图标。用户拒绝后,需要手动重置权限。
- 多设备选择:使用
navigator.mediaDevices.enumerateDevices()可以列出所有可用设备,然后通过deviceId精确指定。 - 性能问题:高分辨率视频流会消耗大量 CPU。如果只是做视频通话,720p 30fps 就足够了,别盲目追求 4K。
- 移动端兼容:iOS Safari 要求 getUserMedia 必须在用户手势(如点击按钮)中调用,否则会失败。
调试小工具:在 Chrome 开发者工具中,打开 chrome://webrtc-internals 可以查看所有媒体流的详细信息,包括分辨率、帧率、丢包率等。这是排查问题的利器。