HTML躬行记(2)——WebRTC基础实践
WebRTC (Web Real-Time Communications) 是一项实时通讯技术,在 2011 年由 Google 提出,经过 10 年的发展,W3C 于 2021 年正式发布 WebRTC 1.0 标准。
WebRTC 标准概括介绍了两种不同的技术:媒体捕获设备和点对点连接(P2P,Peer-to-Peer),可让用户无需安装任何插件或第三方软件的情况下,实现共享桌面、文件传输、视频直播等功能。
下图是官方给出的一张 WebRTC 整体架构设计图:
- 紫色部分是前端开发所使用的 API。
- 蓝色实线部分是各大浏览器厂商所使用的 API。
- 蓝色虚线部分包含可自定义的 3 块:音频引擎、视频引擎和网络传输。
由于各个浏览器对 WebRTC 的实现有所不同,因此 Google 官方提供了一个适配器脚本库:adapter.js,省去很多兼容工作。
本文的源码已上传至 Github,有需要的可以随意下载。
一、自拍
自拍是指通过摄像头拍照生成图片,先看下 HTML 结构,其实就 4 个元素。
<video id="video"></video>
<button id="btn">拍照</button>
<canvas id="canvas" width="300" height="300"></canvas>
<img id="img" alt="照片"/>
1)getUserMedia()
然后在脚本中声明各个元素,通过 navigator.mediaDevices.getUserMedia() 方法获取媒体流。
const video = document.getElementById('video');
const canvas = document.getElementById('canvas');
const btn = document.getElementById('btn');
const img = document.getElementById('img');
const size = 300;
/**
* 获取媒体流
*/
navigator.mediaDevices.getUserMedia({
video: {
width: size,
height: size,
},
audio: false
}).then((stream) => {
video.srcObject = stream;
video.play();
});
getUserMedia() 的参数是一个包含了video 和 audio 两个成员的 MediaStreamConstraints 对象,上面代码将摄像头的分辨率限制为 300 x 300。
then() 中的 stream 参数是一个 MediaStream 媒体流,一个流相当于容器,可以包含几条轨道,例如视频和音频轨道,每条轨道都是独立的。
video 元素中的 src 和 srcObject 是一对互斥的属性,后者可关联媒体源,根据规范也可以是 Blob 或者 File 等类型的数据。
接着为按钮绑定点击事件,并且在点击时从流中捕获帧,画到 Canvas 内,再导出赋给 img 元素。
/**
* 点击拍照
*/
btn.addEventListener('click', (e) => {
const context = canvas.getContext('2d');
// 从流中捕获帧
context.drawImage(video, 0, 0, size, size);
// 将帧导出为图片
const data = canvas.toDataURL('image/png');
img.setAttribute('src', data);
}, false);
在下图中,左边是 video 元素,打开摄像头后就会有画面,在点击拍照按钮后,右边显示捕获的帧。
2)enumerateDevices()
MediaDevices 提供了访问媒体输入和输出的设备,例如摄像头、麦克风等,得到硬件资源的媒体数据。
mediaDevices.enumerateDevices() 会得到一个描述设备的 MediaDeviceInfo 的数组。
其中 groupId 用于标识多个设备属于同一个物理设备,例如一个显示器内置了摄像头和麦克风。
navigator.mediaDevices.enumerateDevices()
.then((devices) => {
devices.forEach((device) => {
console.log(`${device.kind}: ${device.label} id = ${device.deviceId}`);
});
})
3)devicechange
当媒体设备(例如麦克风、摄像头等)连接到系统或从系统中移除时,devicechange 事件就会被发送给设备实例。
navigator.mediaDevices.ondevicechange = (event) => { };
event 参数没有附加任何特殊的属性。
二、共享桌面
Windows 系统采用的共享桌面协议是 RDP(Remote Desktop Protocal),另一种可在不同操作系统共享桌面的协议是 VNC(Virtual Network Console)。
像 TeamViewer 采用的就是后一种协议,而 WebRTC 的远程桌面没有采用传统的 RDP、VNC 等协议,因为不需要远程控制。
WebRTC 提供了 getDisplayMedia() 方法采集桌面,在使用上与之前的 getUserMedia() 方法类似。
navigator.mediaDevices.getDisplayMedia({
video: {
width: 2000,
height: 1000
}
}).then((stream) => {
video.srcObject = stream;
video.play();
});
在刷新页面后,会要求选择共享的桌面,包括整个屏幕、窗口或 Chrome 标签页。
三、录像
WebRTC 的录像包括录制音频和视频两种流,通过 Blob 对象将数据保存成多媒体文件。
1)MediaRecorder
WebRTC 提供了 MediaRecorder 类,它能接收两个参数,第一个是远程的 MediaStream 媒体流,第二个是配置项。
其配置项包括编解码器、音视频码率、容器的 MIME 类型(例如 video/webm、video/mp4 )等相关信息。
先看个示例,HTML结构如下所示,一个 video 元素和两个 button 元素:回放和下载。
<video id="video"></video>
<button id="playback">回放</button>
<button id="download">下载</button>
然后看下录像的整体逻辑,和之前自拍一节类似,也需要调用 getUserMedia() 获取媒体流。
在 then() 的回调中实例化 MediaRecorder 类,并配置多媒体格式。
其中WebM是一个由Google资助,免版权费用的视频文件格式;VP8是一个开放的影像压缩格式。
const video = document.getElementById('video');
const playback = document.getElementById('playback');
const download = document.getElementById('download');
const size = 300;
const chunks = []; // 一个由 Blob 对象组成的数组 navigator.mediaDevices.getUserMedia({
video: {
width: size,
height: size,
},
audio: true
}).then((stream) => {
// 配置多媒体格式
const options = { mimeType: 'video/webm;codecs=vp8' };
// 实例化录制对象
const recorder = new MediaRecorder(stream, options);
// 当收到数据时触发该事件
recorder.ondataavailable = function(e) {
chunks.push(e.data); // data 是一个可用的 Blob 对象
}
// 开始录制
recorder.start(10);
});
recorder 的 dataavailable 事件会在收到数据时触发,e 参数的 data 属性是一个可用的 Blob 对象。
最后在开始录制调用 start() 方法时,可以配置一个毫秒级的时间片,那么在录制时会按照配置的值分割成一个个单独的区块,而不是录制一个非常大的整块内容。
分块可以提高效率和可靠性,如果是一整块,那么会变得越来越大,读写效率也会变差。
2)回放
首先根据 chunks 生成 Blob 对象,再根据 Blob 对象生成 URL 对象。
playback.addEventListener('click', () => {
// 根据 chunks 生成 Blob 对象
const blob = new Blob(chunks, {type: 'video/webm'});
// 根据 Blob 对象生成 URL 对象
video.src = window.URL.createObjectURL(blob);
video.play();
}, false);
URL.createObjectURL 是一个静态方法,返回值是一个指定的 File 对象或 Blob 对象。
3)下载
首先与回放一样,也是生成一个 URL 对象,然后创建 a 元素,将对象赋给 href 属性。
并且要指定 download 属性,告诉浏览器下载 URL 而不是导航。
download.addEventListener('click', (e) => {
const blob = new Blob(chunks, {type: 'video/webm'});
const url = window.URL.createObjectURL(blob);
// 创建 a 元素
const a = document.createElement('a');
a.href = url;
// 指示浏览器下载 URL 而不是导航
a.download = 'test.webm';
a.click();
}, false);
参考资料:
Build the backend services needed for a WebRTC app
HTML躬行记(2)——WebRTC基础实践的更多相关文章
- ES6躬行记(1)——let和const
古语云:“纸上得来终觉浅,绝知此事要躬行”.的确,不管看了多少本书,如果自己不实践,那么就很难领会其中的精髓.自己研读过许多ES6相关的书籍和资料,平时工作中也会用到,但在用到时经常需要上搜索引擎中查 ...
- ES6躬行记 笔记
ES6躬行记(18)--迭代器 要实现以下接口## next() ,return,throw 可以用for-of保证迭代对象的正确性 例如 var str = "向
- HTML躬行记(3)——WebRTC视频通话
WebRTC 在创建点对点(P2P)的连接之前,会先通过信令服务器交换两端的 SDP 和 ICE Candidate,取两者的交集,决定最终的音视频参数.传输协议.NAT 打洞方式等信息. 在完成媒体 ...
- HTML躬行记(4)——Web音视频基础
公司目前的业务会接触比较多的音视频,所以有必要了解一些基本概念. 文章涉及的一些源码已上传至 Github,可随意下载. 一.基础概念 本节音视频的基础概念摘自书籍<FFmpeg入门详解 音视频 ...
- React躬行记(11)——Redux基础
Redux是一个可预测的状态容器,不但融合了函数式编程思想,还严格遵循了单向数据流的理念.Redux继承了Flux的架构思想,并在此基础上进行了精简.优化和扩展,力求用最少的API完成最主要的功能,它 ...
- 前端利器躬行记(3)——webpack基础
webpack是一个静态模块打包器,此处的模块可以是任意文件,包括Sass.TypeScript.模板和图像等.webpack可根据输入文件的依赖关系,打包输出浏览器可识别的JavaScript.CS ...
- CSS躬行记(1)——CSS基础拾遗
一.box-decoration-break CSS3新增的box-decoration-break属性可指定行内非替换元素在跨行.跨列或跨页时的样式渲染,它包含两个值: (1)slice:默认值,盒 ...
- Node.js躬行记(9)——微前端实践
后台管理系统使用的是umi框架,随着公司业务的发展,目前已经变成了一个巨石应用,越来越难维护,有必要对其进行拆分了. 计划是从市面上挑选一个成熟的微前端框架,首先选择的是 icestark,虽然文档中 ...
- Web优化躬行记(6)——优化闭环实践
在遇到一个页面性能问题时,我理解的优化闭环是:分析.策略.验证和沉淀. 分析需要有分析数据,因此得有一个性能监控管理. 策略就是制订针对性的优化方案,解决当前遇到的问题. 验证的对象上述策略,判断方案 ...
随机推荐
- 基于Apache Hudi构建分析型数据湖
为了有机地发展业务,每个组织都在迅速采用分析. 在分析过程的帮助下,产品团队正在接收来自用户的反馈,并能够以更快的速度交付新功能. 通过分析提供的对用户的更深入了解,营销团队能够调整他们的活动以针对特 ...
- MySQL事务概念与流程和索引控制
MySQL事务概念与流程和索引控制 视图 1.什么是视图 我们在执行SQL语句其实就是对表进行操作,所得到的其实也是一张表,而我们需要经常对这些表进行操作,拼接什么的都会产生一张虚拟表,我们可以基于该 ...
- LOJ2312 LUOGU-P3733「HAOI2017」八纵八横 (异或线性基、生成树、线段树分治)
八纵八横 题目描述 Anihc国有n个城市,这n个城市从1~n编号,1号城市为首都.城市间初始时有m条高速公路,每条高速公路都有一个非负整数的经济影响因子,每条高速公路的两端都是城市(可能两端是同一个 ...
- Docker0网络及原理探究
个人观点:Docker网络通信在容器编排.集群部署中具有举足轻重的地位,(玩docker不懂docker0那就......玩不透哇)本篇分析Docker网络,并通过启动几个容器来探究Docker网络及 ...
- 第九十二篇:Vue 自定义指令
好家伙, 1.什么是自定义指令? vue官方提供了v-text,v-for,v-model,v-if等常用的指令.除此之外vue还允许开发者自定义指令. 2.自定义指令的分类 vue中的自定义指令分为 ...
- C++ 初识函数模板
1. 前言 什么是函数模板? 理解什么是函数模板,须先搞清楚为什么需要函数模板. 如果现在有一个需求,要求编写一个求 2 个数字中最小数字的函数,这 2 个数字可以是 int类型,可以是 float ...
- KingbaseES 如何把一个schema下的所有对象访问权限授权给其他用户
用户需求:新建一个用户 B,需要能够查询A用户的所有表,并且对以后新建的表也要有select权限. 问题分析:对于现有的表可以通过动态sql批量进行授权,但是未来新建的表要如何进行授权呢? 查询了帮助 ...
- KingbaseES 转义字符
在SQL标准中字符串是用单引号括起来的,在KingbaseES中遵守了该标准,如果在字符串中需要使用到单引号,就需要对其进行转义. 方式一:使用E和反斜杠进行转义 方式二:直接用一个单引号来转义 在K ...
- KingbaseES R6 集群禁用 root ssh 后需要修改集群为es_server 案例
案例说明: 在生产环境下,由于安全需要,主机间不允许建立root用户的ssh信任连接,这样导致KingbaseES R6 repmgr集群,通过sys_monitor.sh脚本启动集群时,节点之间不能 ...
- Nginx location总结
最近操作Nginx.conf 的location部分,发现了一个巨坑,在这做个记录 当我用一下配置时 location = / { root /usr/share/nginx/html; index ...