一文带你看懂浏览器采集音视频
  xemNE32VmUwG 2023年12月08日 29 0

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

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

0. 前提

  1. navigator.getUserMedia已被废弃,请使用navigator.mediaDevices.getUserMedia
  2. 浏览器要获取摄像头权限需要开启本地端口或者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));

一文带你看懂浏览器采集音视频_webrtc

  • 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. 初次调用后会询问权限策略:允许或者禁止。允许后会获取永久权限,如果想再改成询问,需要点击地址栏前的小叹号修改;
  2. 接口参数十分复杂,放到1.4节详细讲解;
  3. 返回的stream表示媒体流,内部可以包括多个媒体轨道track,比如视频轨和音频轨。stream和track的关系如下图所示:

一文带你看懂浏览器采集音视频_webrtc_02

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());
}

一文带你看懂浏览器采集音视频_webrtc_03

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

  1. 属性(除了enabled外,全是只读):
  • id: 代表唯一值
  • kind: "audio" | "video"
  • enabled: 表示该轨道是否可用,可以被手动设置,设置为false后,视频黑屏,音频静音
  • label: 和mediaDevices.enumerateDevices返回值设备的label相对应
  • muted: 是否静音
  • readyState: 枚举值, "live"表示输入设备正常连接,"ended"表示没有更多的数据
  1. 方法:
  • 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标签页中选择一个共享画面

一文带你看懂浏览器采集音视频_webrtc_04

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

1.4 getSupportedConstraints

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

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

一文带你看懂浏览器采集音视频_webrtc_05

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种设置方式:

  1. 不设置:使用的是浏览器的默认值,如何获取默认值是多少?可以通过track.getSettings来获取
navigator.mediaDevices.getUserMedia({ video: true });
  1. 精确值:宽度和高度分别设置2个值,有没有范围呢?有。如何获取范围值呢?可以通过track.getCapabilities来获取
navigator.mediaDevices.getUserMedia({ video: {
	width: 640,
    height: 480,
} });
  1. 范围: 会在这个范围内尽量满足, 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. 参考文档

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月08日 0

暂无评论

推荐阅读
xemNE32VmUwG
作者其他文章 更多