媒体流基础: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就不行。所以做产品时,得考虑降级方案。

核心流程:从设备到网络

为了让大家更直观地理解,我画了一张流程图。它展示了从获取媒体流到最终发送的完整链路。

媒体流获取与处理流程 媒体流获取与处理流程 getUserMedia 约束参数检查 enumerateDevices MediaStream Track处理/替换 addTrack → 发送 getDisplayMedia 说明: 1. 先检查约束支持,再调用getUserMedia 2. 屏幕共享走getDisplayMedia,与普通摄像头流分开处理

避坑指南:我踩过的那些坑

做WebRTC开发,有些坑是绕不过去的。我把自己踩过的列出来,你们能少走弯路。

  • 权限问题:HTTPS环境下才能调摄像头。我曾经在本地开发时用HTTP,死活拿不到流。后来加了localhost例外才搞定。
  • 设备切换:用户插拔摄像头后,流不会自动更新。需要监听devicechange事件,重新枚举设备。
  • 轨道结束:用户关掉摄像头或停止共享时,记得清理资源。调用track.stop()释放设备。
  • 分辨率适配:别写死分辨率。用ideal让浏览器自己选,或者根据屏幕尺寸动态计算。

核心要点总结:

  • getUserMedia返回Promise,记得处理异常
  • 约束参数用ideal,别用exact
  • 屏幕共享用getDisplayMedia,注意浏览器差异
  • 设备变化要监听,资源用完要释放

好了,这一章的内容就到这儿。媒体流是WebRTC的基石,搞懂了它,后面讲信令、讲传输,你才能跟得上。代码多写几遍,遇到问题别慌,翻翻这一章,基本都能解决。