mediaDevices开启你本地视频之旅行

navigator.mediaDevices 接口提供访问连接媒体输入的设备,如照相机和麦克风,以及屏幕共享等。它可以使你取得任何硬件资源的媒体数据

本文所有API基本来源于MDN文档,MDN文档在mediaDevices模块翻译也是有的翻译了,有的没有翻译,有的通过链接打开是404(可以将url中的zh-CN改成en-US来查看英文版本),在代码实践(Chrome x86_64 88)中有的API与MDN上也不尽相同,因此基本是按代码实践中的API来罗列。 如果您发现有错误描述,可以留言,我会在第一时间验证,修改。

0. 前提

  • navigator.getUserMedia 已被废弃,请使用 navigator.mediaDevices.getUserMedia
  • 浏览器要获取摄像头权限需要开启本地端口或者https服务;
  • 1. mediaDevices

    mediaDevices上提供了4个方法和一个监听事件

  • enumerateDevices: 获取系统可用的媒体输入和输出设备
  • getSupportedConstraints: 返回一个输入设备可支持的 约束 属性
  • getDisplayMedia: 开启屏幕共享
  • getUserMedia: 开启媒体输入设备
  • devicechange
  • 1.1 enumerateDevices

    enumerateDevices返回一个promise,如果正确执行可以得到一个MediaDeviceInfo的数组,每项分别有4个属性(都是只读)。

    TIPS: 如果页面未获取浏览器设备权限,则返回的deviceId和label都是空字符串。调用 mediaDevices.getUserMedia 会触发询问权限

    navigator.mediaDevices.enumerateDevices().then(res => console.table(res));
    
  • deviceId: 代表设备的id,随机生成,该网页与其他网页获取的id不同;
  • label: 设备的别名
  • kind: 枚举值 audioinput audiooutput videoinput,因为视频输出靠屏幕,因此没有videooutput这个选项
  • groupId: 如果设备是同一个物理设备,那么这些设备的groupId就是同一个
  • 1.2 getUserMedia

    浏览器获取视频、音频的入口

    window.addEventListener('load', initMedia);
    async function initMedia() {
    	const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
    	console.log(stream);
    
  • 初次调用后会询问权限策略:允许或者禁止。允许后会获取永久权限,如果想再改成询问,需要点击地址栏前的小叹号修改;
  • 接口参数十分复杂,放到1.4节详细讲解;
  • 返回的stream表示媒体流,内部可以包括多个媒体轨道track,比如视频轨和音频轨。stream和track的关系如下图所示:
  • 1.2.1 stream

    stream上有2个重要的属性 active 代表该流是否为活动状态 和 id 代表流的唯一值

    简而言之,stream就是管理tarck的集合,在stream上有操作track的一些增删查方法:

  • addTrack: 将track添加到stream中
  • getTracks getVideoTracks getAudioTracks:顾名思义,会返回track list
  • getTrackById: track是有id属性,可以根据id在stream中获取该track,如果不存在会返回null
  • removeTrack: 传入值是MediaStreamTrack对象,而非trackId
  • 同时stream也可以订阅track相关的事件:

  • onaddtrack
  • onremovetrack
  • 除此之外stream上还有clone方法,返回MediaStream的克隆版本,并且会返回一个新的id;

    1.2.2 track

    视频/音频轨道

    async function initMedia() {
    	window.stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
    	console.log(stream.getTracks());
    

    上面有一些重要的属性和方法:

  • 属性(除了enabled外,全是只读):
  • id: 代表唯一值
  • kind: "audio" | "video"
  • enabled: 表示该轨道是否可用,可以被手动设置,设置为false后,视频黑屏,音频静音
  • label: 和mediaDevices.enumerateDevices返回值设备的label相对应
  • muted: 是否静音
  • readyState: 枚举值, "live"表示输入设备正常连接,"ended"表示没有更多的数据
  • getConstraints(): 返回创建该轨道的约束(getUserMedia的参数),只以video为例,video可设置的选项很多,会在1.4详细解读
  • async function initMedia() {
      // case 1 只是简单打开视频
      const stream = await navigator.mediaDevices.getUserMedia({ video: true });
      const track = stream.getTracks()[0];
      console.log(track.getConstraints()); // {} 表示没有任何约束
      // case 2 指定视频宽高
      const stream = await navigator.mediaDevices.getUserMedia({ video: {
          width: 640,
          height: 320
      } });
      const track = stream.getTracks()[0];
      console.log(track.getConstraints()); // {height: 320, width: 640}
    
  • applyConstraints(): 给该轨道应用新的约束
  • async function initMedia() {
        const stream = await navigator.mediaDevices.getUserMedia({ video: true });
        const track = stream.getTracks()[0];
        let constraints = track.getConstraints();
        console.log('old constraints: ', constraints); // {}
        constraints.width = 640;
        constraints.height = 320;
        await track.applyConstraints(constraints);
        console.log('new constraints: ', track.getConstraints()); // {height: 320, width: 640}
    
  • getSettings(): 和getConstraints不同的是,getSettings会返回浏览器添加的默认约束和自己明确添加的约束,也就是轨道的所有约束
  • async function initMedia() {
        const stream = await navigator.mediaDevices.getUserMedia({ video: {
            width: 320,
            height: 240,
        } });
        const track = stream.getTracks()[0];
        let constraints = track.getConstraints();
        let settings = track.getSettings();
        console.log('constraints 1: ', constraints); // {height: 240, width: 320}
        console.log('settings 1: ', settings); // {aspectRatio:1.3333333333333333,deviceId: "ac5572f202526708aa9d0984ac3d7214873266ea014b93d22fc850cb565915f6",frameRate:30.000030517578125,groupId: "ab4cc46dac787502a6c20285b385ea5376aaafe7fa7c77be6d63006308465639",height: 240,resizeMode: "crop-and-scale",width: 320}
        constraints.width = 640;
        constraints.height = 480;
        constraints.frameRate = 25;
        await track.applyConstraints(constraints);
        constraints = track.getConstraints();
        settings = track.getSettings();
        console.log('constraints 2: ', constraints); // {height: 480, width: 640, frameRate: 25}
        console.log('settings 2: ', settings); // {aspectRatio: 1.3333333333333333, deviceId: "ac5572f202526708aa9d0984ac3d7214873266ea014b93d22fc850cb565915f6",frameRate: 25,groupId: "ab4cc46dac787502a6c20285b385ea5376aaafe7fa7c77be6d63006308465639",height: 480,resizeMode: "none",width: 640}
    
  • getCapabilities(): 方法返回一个 MediaTrackCapabilities 对象,此对象表示每个可调节属性的值或者范围,该特性依赖于平台和user agent。
  • video的宽高是可以设置的,但是设置的范围是多少呢,可以通过该方法来查看:

    async function initMedia() {
        const stream = await navigator.mediaDevices.getUserMedia({ video: true });
        const track = stream.getTracks()[0];
        console.log(track.getCapabilities());
    

    可以得到类似以下的结果:

    "aspectRatio":{ "max":1280, "min":0.001388888888888889 "deviceId":"ac5572f202526708aa9d0984ac3d7214873266ea014b93d22fc850cb565915f6", "facingMode":[ "frameRate":{ "max":30.000030517578125, "min":0 "groupId":"f04ed7a36ef8757794bb5db771115bf857b73332e68bf39ec42b30667a76fdca", "height":{ "max":720, "min":1 "resizeMode":[ "none", "crop-and-scale" "width":{ "max":1280, "min":1
  • clone(): 克隆一个track的备份,和stream一样,会产出一个新的id
  • stop(): stop后,readyState的状态就变成了ended
  • 1.3 getDisplayMedia

    调用后会在所有屏幕/应用/Chrome标签页中选择一个共享画面

    因为是共享屏幕,即使约束不添加video选项,stream中也有默认添加一个video。其他约束、stream、track就和getUserMedia一样了,不再赘述

    1.4 getSupportedConstraints

    返回一个对象,该对象表明可以约束MediaStreamTrack的属性

    const constraints = navigator.mediaDevices.getSupportedConstraints();
    console.log(constraints);
    

    getUserMediagetDisplayMedia这两个方法接收一个对象作为参数,对象中可以分别对video和audio设置具体的参数(称之为constraints即约束)。通过getSupportedConstraints获取的属性就是设置约束的具体内容,这些属性可以分为通用设置、只对video设置、只对aduio设置

    navigator.mediaDevices.getUserMedia({
    	video: {},
        audio: {},
    

    1.4.1 通用设置 deviceId & groupId

    通用设置,可以指定外设,可以通过mediaDevices.enumerateDevices来查询外设的deviceId或者groupId

    navigator.mediaDevices.getUserMedia({ video: {
    	deviceId: 'xxx-xxx'
    } });
    

    1.4.2 video width & height

    这两个属性只对video来设置,设置方法一样,单拿一个来讲,一共有3种设置方式:

  • 不设置:使用的是浏览器的默认值,如何获取默认值是多少?可以通过track.getSettings来获取
  • navigator.mediaDevices.getUserMedia({ video: true });
    
  • 精确值:宽度和高度分别设置2个值,有没有范围呢?有。如何获取范围值呢?可以通过track.getCapabilities来获取
  • navigator.mediaDevices.getUserMedia({ video: {
    	width: 640,
        height: 480,
    } });
    
  • 范围: 会在这个范围内尽量满足, ideal是最理想的值,如果ideal在可设置的范围内,那么就会应用ideal的值;如果min的值超出的浏览器的最大值,那么就会按浏览器的最大提供的能力来展示;
  • navigator.mediaDevices.getUserMedia({ video: {
    	width: { min: 1024, ideal: 1280, max: 1920 },
        height: { min: 776, ideal: 720, max: 1080 }
    } });
    

    1.4.3 video frameRate

    属性只对video生效,也分为不设置、精确值、范围值来设置,不再赘述

    1.4.4 video facingMode

    一般用在移动设备上,选择开启前置或者后置摄像头

    navigator.mediaDevices.getUserMedia({ video: {
    	facingMode: 'user'
    } });
    

    值为枚举值:

  • 'user': 前置摄像头
  • 'environment': 后置摄像头
  • 'left':视频源面向用户但在他们的左边,例如一个对准用户但在他们的左肩上方的摄像机。
  • 'right': 视频源面向用户但在用户的右边,例如,摄像机对准用户但在他们的右肩上
  • 1.4.5 video aspectRatio

    设置采集图像的宽高比

    async function initMedia() {
        const stream = await navigator.mediaDevices.getUserMedia({
            video: {
                width: 640,
                aspectRatio: 2,
        // 宽度 640 高度 320
        localvideo.srcObject = stream;
    

    1.4.6 audio autoGainControl & noiseSuppression & echoCancellation

    这3个属性通过track.getSettings来看默认值都是true。 autoGainControl是自动音量调节; noiseSuppression是噪声消除; echoCancellation是回声消除;

    噪声消除和回声消除是浏览器内置算法消除的,一定不要设置成false

    1.4.7 audio channelCount & sampleRate & sampleSize

    音频三要素,分别是声道数、采样率、位深。我在浏览器中得到的默认值是 1 48k 16bit 可以得到未压缩的码率:48k * 16 * 1 = 768kb/s

    1.4.8 audio latency

    可以接受的延迟,值是数字,也可以设置范围

    x. 参考文档

  • MDN mediaDevices
  • 分类:
    前端
    标签: