WebRTC源码研究(15)从视频中获取图
1. WebRTC抓拍图片的api
从视频中获取一张图片的方式有很多,如果视频已经渲染到UI上面了,那么可以提前图片的方式就更灵活了,最简单的方式就是我们经常使用的截屏功能,截屏实现的原理就是在获取画布的当前快照,在IOS中我们可以直接从layer中获取当前绘制的上下文,PC,android的原理也是类似。另外还有一种就是直接从视频流中截取一个关键帧实现抓拍功能。
下面我们看看WebRTC在浏览器中给我们提供哪些获取图片的api
2. WebRTC抓拍图片的实战
在浏览器中的视频中获取图片其实也可以和上面说的一样,通过快照的方式获取。具体实现就是通过HTML
中的canvas
标签,从视频流中获取当时的帧,然后把这个帧输出成一个图片就可以了。
我们下面实现一下,首先要拿到音视频流,然后我们要增加两个元素,一个是按钮,这个按钮主要是像相机的快门 一样,当我点击 这个按钮的时候就拍一张照片。
其中第二个是canvas
,canvas
具体是将视频流中的触发的帧获取到读成我们想要的图片就可以了。点击获取图片按钮后,canvas
就获取到图片了,右击图片,就可以保存下来
具体代码如下:
index.html
<html>
<head>
<title>捕获音视频数据 孔雨露 20200603</title>
<style>
.none {
-webkit-filter: none;
}
.blur {
/* 特效模糊 */
-webkit-filter: blur(3px);
}
.grayscale {
/* 特效灰度 */
-webkit-filter: grayscale(1);
}
.invert {
/* 翻转 */
-webkit-filter: invert(1);
}
.sepia {
/* 特效褐色 */
-webkit-filter: sepia(1);
}
</style>
</head>
<body>
<div>
<label>audio Source:</label>
<select id="audioSource"></select>
</div>
<div>
<label>audio Output:</label>
<select id="audioOutput"></select>
</div>
<div>
<label>video Source:</label>
<select id="videoSource"></select>
</div>
<!-- 特效选择器 -->
<div>
<label>Filter:</label>
<select id="filter">
<option value="none">None</option>
<option value="blur">blur</option>
<option value="grayscale">Grayscale</option>
<option value="invert">Invert</option>
<option value="sepia">sepia</option>
</select>
</div>
<!--
我们创建一个video标签,这个标签就可以显示我们捕获的音视频数据
autoplay 表示当我们拿到视频源的时候直接播放
playsinlin 表示在浏览器页面中播放而不是调用第三方工具
-->
<video autoplay playsinlin id="player"></video>
<!-- 获取视频帧图片按钮 -->
<div>
<button id="snapshot">Take snapshot</button>
</div>
<!-- 获取视频帧图片显示在canvas里面 -->
<div>
<canvas id="picture"></canvas>
</div>
<!-- 引入 adapter.js库 来做 不同浏览器的兼容 -->
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="./js/client.js"></script>
</body>
</html>
上面代码中使用到的client.js
内容如下:
'use strict'
var audioSource = document.querySelector('select#audioSource');
var audioOutput = document.querySelector('select#audioOutput');
var videoSource = document.querySelector('select#videoSource');
// 获取video标签
var videoplay = document.querySelector('video#player');
//filter 特效选择
var filtersSelect = document.querySelector('select#filter');
//picture 获取视频帧图片相关的元素
var snapshot = document.querySelector('button#snapshot');
var picture = document.querySelector('canvas#picture');
picture.width = 640;
picture.height = 480;
// deviceInfos是设备信息的数组
function gotDevices(deviceInfos){
// 遍历设备信息数组, 函数里面也有个参数是每一项的deviceinfo, 这样我们就拿到每个设备的信息了
deviceInfos.forEach(function(deviceinfo){
// 创建每一项
var option = document.createElement('option');
option.text = deviceinfo.label;
option.value = deviceinfo.deviceId;
if(deviceinfo.kind === 'audioinput'){ // 音频输入
audioSource.appendChild(option);
}else if(deviceinfo.kind === 'audiooutput'){ // 音频输出
audioOutput.appendChild(option);
}else if(deviceinfo.kind === 'videoinput'){ // 视频输入
videoSource.appendChild(option);
}
})
}
// 获取到流做什么, 在gotMediaStream方面里面我们要传人一个参数,也就是流,
// 这个流里面实际上包含了音频轨和视频轨,因为我们通过constraints设置了要采集视频和音频
// 我们直接吧这个流赋值给HTML中赋值的video标签
// 当时拿到这个流了,说明用户已经同意去访问音视频设备了
function gotMediaStream(stream){
videoplay.srcObject = stream; // 指定数据源来自stream,这样视频标签采集到这个数据之后就可以将视频和音频播放出来
// 当我们采集到音视频的数据之后,我们返回一个Promise
return navigator.mediaDevices.enumerateDevices();
}
function handleError(err){
console.log('getUserMedia error:', err);
}
function start() {
// 判断浏览器是否支持
if(!navigator.mediaDevices ||
!navigator.mediaDevices.getUserMedia){
console.log('getUserMedia is not supported!');
}else{
// 获取到deviceId
var deviceId = videoSource.value;
// 这里是约束参数,正常情况下我们只需要是否使用视频是否使用音频
// 对于视频就可以按我们刚才所说的做一些限制
var constraints = { // 表示同时采集视频金和音频
video : {
width: 640, // 宽带
height: 480, // 高度
frameRate:15, // 帧率
facingMode: 'enviroment', // 设置为后置摄像头
deviceId : deviceId ? deviceId : undefined // 如果deviceId不为空直接设置值,如果为空就是undefined
},
audio : {
noiseSuppression: true, // 降噪
echoCancellation: true // 回音消除
},
}
// 从指定的设备中去采集数据
navigator.mediaDevices.getUserMedia(constraints)
.then(gotMediaStream) // 使用Promise串联的方式,获取流成功了
.then(gotDevices)
.catch(handleError);
}
}
start();
// 当我选择摄像头的时候,他可以触发一个事件,
// 当我调用start之后我要改变constraints
videoSource.onchange = start;
// 选择特效的方法
filtersSelect.onchange = function(){
videoplay.className = filtersSelect.value;
}
// 点击按钮获取视频帧图片
snapshot.onclick = function() {
picture.className = filtersSelect.value;
// 调用canvas API获取上下文,图片是二维的,所以2d,这样我们就拿到它的上下文了
// 调用drawImage绘制图片,第一个参数就是视频,我们这里是videoplay,
// 第二和第三个参数是起始点 0,0
// 第四个和第五个参数表示图片的高度和宽度
picture.getContext('2d').drawImage(videoplay, 0, 0, picture.width, picture.height);
}