疫情时代,视讯串流当头,用视讯镜头来做个线上摄影吧!
自从疫情爆发後,各行各业也开始进行居家办公,使得视讯软件及相关技术开始受重视,身为前端,我们也可以拿视讯镜头来做些好玩有趣的东西,而 ImageCapture 就是其中一个可以运用在这里的 API。
在认识 ImageCapture 之前我们必须要先了解 MediaStream,浏览器将获取的影音资讯称之为「流」(Stream),其中流又包含了「轨」(Track),如影像轨、音讯轨,而我们可以透过向使用者获取授权并透过装置来取得这些影音资讯,进而达到我们想要的操作目的。
而我们要取得 MediaStream 的手段就是要先向使用者获取设备的授权,这时候就要使用 getUserMedia
:
navigator.mediaDevices.getUserMedia({ video: true })
.then(mediaStream => {
/* use the stream */
})
.catch(err => {
/* handle the error */
});
当我们呼叫 getUserMedia
时,必须传入一个称为 constraints
的参数,该参数为一个物件,当中需要表示你想取得的 Track,例如上面我们就是传入 { video: true }
,来取得视讯轨。
而 getUserMedia
会回传 Promise 给我们,当使用者同意授权後就可以在 then
的 Callback 中取得 MediaStream。
当我们取得 MediaStream 後,我们还需要再取得当中的 Track,之後才可以透过 ImageCapture 来操作,这时候就需要使用 MediaStream 自身的 method getVideoTracks
。
navigator.mediaDevices.getUserMedia({ video: true })
.then(mediaStream => {
const videoTrack = mediaStream.getVideoTracks()[0];
})
.catch(err => {
console.log(err)
});
要注意的是,由於一个 MediaStream 中未必只有一个 VideoTrack,所以 getVideoTracks
回传的会是阵列,记得要透过 index
索引出来。
知道如何取得 MediaStreamTrack 後,就可以来认识 ImageCapture 了,它可以让我们建立一个图像撷取器,只要提供一个有效的 VideoTrack 给 ImageCapture 就可以进行图像的撷取:
const imageCapture = new ImageCapture(videoTrack);
当我们为一个 ImageCapture 绑定了 VideoTrack 後,我们就可以透过 ImageCapture 底下的 methods 来进行图像撷取了:
let imageCapture;
navigator.mediaDevices.getUserMedia({ video: true })
.then(mediaStream => {
const videoTrack = mediaStream.getVideoTracks()[0];
imageCapture = new ImageCapture(videoTrack);
})
.catch(err => {
console.log(err)
});
document.querySelector("button").addEventListener("click", function() {
imageCapture.takePhoto().then(blob => {
console.log(blob)
});
})
呼叫 takePhoto
後,它会回传 Promise,并且我们能在 then
的 Callback 中取得截图的 Blob 物件。
再来要介绍的则是 grabFrame
,它和 takePhoto
一样是撷取 videoTrack 的影像,差别在於它回传的是 ImageBitmap 物件,而这种物件的好处是可以直接拿来画在 Canvas 上。
imageCapture.takePhoto().then(blob => {
console.log(blob)
});
imageCapture.grabFrame().then(imageBitmap => {
console.log(imageBitmap)
});
Blob 物件可以够过
createImageBitmap(blob)
来转换成 ImageBitmap 物件
那最後我们就透过今天认识的 API 来实际做个视讯截图摄影吧,首先先准备几个按钮以及 video
和 canvas
。
<div>
<button onclick="openCamera()">开启镜头</button>
<button onclick="capture()">撷取画面</button>
</div>
<video></video>
<canvas></canvas>
再来是在 openCamera
的时候使用 getUserMedia
及 getVideoTracks
来取得 MediaStreamTrack 并建立 ImageCapture。处此之外,我们还设定了 video.srcObject
,如此一来我们就可以够过 <video>
标签来预览视讯画面。
var video = document.querySelector('video');
var canvas = document.querySelector('canvas');
var context = canvas.getContext('2d');
var videoTrack;
var imageCapture;
// 开启镜头
function openCamera() {
navigator.mediaDevices
.getUserMedia({ video: true })
.then((stream) => {
// 取得视讯轨并建立 imageCapture
videoTrack = stream.getVideoTracks()[0];
imageCapture = new ImageCapture(videoTrack);
// 将媒体流设定到 <video> 中显示播放
video.srcObject = stream;
video.play();
})
.catch((err) => {
console.log(err);
});
}
//撷取画面
function capture() {
imageCapture
.takePhoto()
.then(blob => {
// 将 Blob 转成 ImageBitmap
return createImageBitmap(blob)
})
.then(imageBitmap => {
// 绘制在 canvas 上
const { width, height } = imageBitmap;
const ratio = video.videoWidth / width;
canvas.setAttribute('width', width * ratio);
canvas.setAttribute('height', height * ratio);
context.drawImage(imageBitmap, 0, 0, width * ratio, height * ratio);
});
}
最後只要在 capture
的时候透过 takePhoto
进行截图,并将 Blob 转成 ImageBitmap 後丢到 <canvas>
里,就大功告成罗。
完整的 code 我就放在 这里,大家可以看看实际效果。
其实视讯的操作没有大家想像的那麽困难,简单几个 API 就可以做到,今天做的镜头截图其实就可以做在会员的大头照设定,让使用者可以直接利用视讯镜头拍摄大头照,相当便利。
<<: 【Day22】 Transformer 新手包 (二)
>>: Day 8 - 使用 Order API 建立测试订单
如果你曾经在别的程序语言写过OOP,你也许对类别的方法存取限制不会太陌生。类别的方法存取限制常见的主...
大家好我是长风青云。今天是铁人赛21天。我们算是半只脚踏入演算法的阶段。(因为Sort的部分DS和A...
前情提要 将英雄们显示在 Mat-Card 上後,我们进一步地要对英雄资料做点加工,并且制作英雄详细...
时间复杂度(Time complexity) 我们要怎麽知道一个程序要跑多久? 正常来说要真的执行下...
简介 casting 就是资料型态之间的转换。 例如把 A type 转换成 B type。 但是这...