媒体流基础:getUserMedia API详解、音视频约束参数、屏幕共享实现
各位同学,咱们直接进入正题。这一章讲的是WebRTC的"水源"——媒体流。没有它,后面的一切都是空谈。我个人习惯把getUserMedia比作水龙头,拧开它,音视频数据才能流进来。
getUserMedia API:打开媒体的大门
这个API其实很简单,就是一个Promise。你给它提要求,它给你返回一个MediaStream对象。但坑就藏在细节里。
// 最基础的调用
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
})
.then(function(stream) {
// 拿到流了,挂到video标签上
const video = document.getElementById('localVideo');
video.srcObject = stream;
})
.catch(function(err) {
console.error('出错了:', err);
});
嗯,这里要注意。老版本的API挂在navigator.getUserMedia上,现在统一走navigator.mediaDevices.getUserMedia。我在项目中遇到过,有些老旧浏览器还认前者,所以兼容代码得写上。
const getUserMedia = navigator.mediaDevices?.getUserMedia ||
navigator.webkitGetUserMedia ||
navigator.mozGetUserMedia;
音视频约束参数:别让摄像头瞎拍
你想想看,如果不加约束,摄像头可能给你返回一个640x480的模糊画面。所以我们要用constraints对象来"调教"它。
视频约束详解
| 参数 | 类型 | 说明 |
|---|---|---|
width |
ConstrainULong | 视频宽度,可设min/max/exact/ideal |
height |
ConstrainULong | 视频高度,同上 |
frameRate |
ConstrainDouble | 帧率,一般设ideal: 30 |
facingMode |
ConstrainDOMString | 摄像头朝向:user前置,environment后置 |
deviceId |
ConstrainDOMString | 指定具体设备ID |
这里有个关键点:ideal和exact的区别。我曾经踩过这个坑——用exact指定分辨率,结果用户摄像头不支持,直接报错。后来我改用ideal,浏览器会选最接近的分辨率,兼容性好很多。
// 推荐写法:用ideal
const constraints = {
video: {
width: { ideal: 1280 },
height: { ideal: 720 },
frameRate: { ideal: 30 },
facingMode: 'user'
},
audio: {
echoCancellation: true,
noiseSuppression: true,
sampleRate: { ideal: 48000 }
}
};
音频约束参数
音频这块,我建议重点关注三个参数:
- echoCancellation:回声消除,做视频会议必须开
- noiseSuppression:降噪,能滤掉键盘声、风扇声
- sampleRate:采样率,44100或48000都行
getSupportedConstraints()检查一下。
const supported = navigator.mediaDevices.getSupportedConstraints();
if (!supported.echoCancellation) {
console.warn('当前浏览器不支持回声消除');
}
屏幕共享实现:不只是录屏那么简单
屏幕共享用的是getDisplayMedia,不是getUserMedia。这个API会弹出一个选择窗口,让用户选共享哪个窗口或整个屏幕。
async function startScreenShare() {
try {
const stream = await navigator.mediaDevices.getDisplayMedia({
video: {
cursor: 'always' // 显示鼠标指针
},
audio: {
echoCancellation: true
}
});
// 挂到video标签
const screenVideo = document.getElementById('screenVideo');
screenVideo.srcObject = stream;
// 监听停止事件
stream.getVideoTracks()[0].addEventListener('ended', () => {
console.log('用户停止了屏幕共享');
});
} catch (err) {
console.error('屏幕共享失败:', err);
}
}
这里有个坑:getDisplayMedia返回的流,音频轨道是可选的。我在项目中遇到过,Chrome和Firefox对音频的支持不一样。Chrome能捕获系统音频,Firefox就不行。所以做产品时,得考虑降级方案。
核心流程:从设备到网络
为了让大家更直观地理解,我画了一张流程图。它展示了从获取媒体流到最终发送的完整链路。
避坑指南:我踩过的那些坑
做WebRTC开发,有些坑是绕不过去的。我把自己踩过的列出来,你们能少走弯路。
- 权限问题:HTTPS环境下才能调摄像头。我曾经在本地开发时用HTTP,死活拿不到流。后来加了localhost例外才搞定。
- 设备切换:用户插拔摄像头后,流不会自动更新。需要监听
devicechange事件,重新枚举设备。 - 轨道结束:用户关掉摄像头或停止共享时,记得清理资源。调用
track.stop()释放设备。 - 分辨率适配:别写死分辨率。用
ideal让浏览器自己选,或者根据屏幕尺寸动态计算。
核心要点总结:
- getUserMedia返回Promise,记得处理异常
- 约束参数用ideal,别用exact
- 屏幕共享用getDisplayMedia,注意浏览器差异
- 设备变化要监听,资源用完要释放
好了,这一章的内容就到这儿。媒体流是WebRTC的基石,搞懂了它,后面讲信令、讲传输,你才能跟得上。代码多写几遍,遇到问题别慌,翻翻这一章,基本都能解决。