如何用 WebRTC 给自己拍照?

简介: 哈喽,大家好,我是海怪。最近一直在看 WebRTC 的用法,也学了一下音视频流的东西,今天就跟大家分享一个好玩的小实战吧——给自己拍照。

image.png


前言




哈喽,大家好,我是海怪。

最近一直在看 WebRTC 的用法,也学了一下音视频流的东西,今天就跟大家分享一个好玩的小实战吧——给自己拍照。


image.png


**项目已上传至 Github,Repo

地址:github.com/haixiangyan…**


页面结构




首先,我们要拆分一下实现步骤:


  • 打开摄像头,获取视频流
  • 需要一个 <video> 来播放摄像头的画面
  • 点击按钮,生成画面,并展示在 <img>


因此,我们需要 <video>, <img> 以及 <button>


<head>
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
  <link rel="stylesheet" href="styles.css">
</head>
<body>
<main>
  <video id="video">浏览器不支持 Video</video>
  <canvas id="canvas">
    <img id="photo" alt="拍照后的照片">
  </canvas>
</main>
<div class="actions">
  <button id="takePhotoButton" type="button" class="btn btn-primary">拍照</button>
  <button id="downloadButton" type="button" class="btn btn-success">下载</button>
  <button id="clearButton" type="button" class="btn btn-danger">清空</button>
</div>
<script src="./main.js"></script>
</body>


再加上点 CSS,让整个 App 好看一点~


main {
    padding: 24px 24px 16px;
    display: flex;
}
video {
    margin-right: 16px;
    box-sizing: content-box;
    border: 4px solid #ffaabb;
}
canvas {
    box-sizing: content-box;
    border: 4px solid #aabbff;
}
.actions {
    padding: 0 24px;
}
.actions button {
    margin-right: 16px;
}


image.png


左边为摄像头的 <video>,右边则是拍照的图片。


打开摄像头




打开摄像头这一步,其实是调用了 WebRTC 的一个重要接口 navigator.mediaDevices.getUserMedia,通过这个接口不仅可以完成用户对摄像头的使用授权,还可以从返回值里直接拿到视频流:


const start = async () => {
  video = document.getElementById('video');
  canvas = document.getElementById('canvas');
  photo = document.getElementById('photo');
  takePhotoButton = document.getElementById('takePhotoButton');
  downloadButton = document.getElementById('downloadButton');
  // 获取摄像头的视频流
  try {
    video.srcObject = await navigator.mediaDevices.getUserMedia({video: true, audio: false})
    video.play()
  } catch (e) {
    console.error(e)
  }
}
start().then()


将视频流接到 <video> 元素的 srcObject 上,再调用一下 video.play() 就可以展示视频流了:



image.png


拍照




从 HTML 结构里我们可以看到 <canvas> 里面还藏着一个 <img>。这里 <canvas> 的作用是负责从视频流中生成图片数据,再将这个数据放到 <img>src 上,这样就完成了我们的拍照功能了。


const takePhoto = () => {
  const context = canvas.getContext('2d')
  if (width && height) {
    // 将 video 元素的 width 和 height 拿过来
    canvas.width = width;
    canvas.height = height;
    context.drawImage(video, 0, 0, width, height);
    // 生成图片
    const data = canvas.toDataURL('image/png');
    photo.setAttribute('src', data);
  } else {
    clearPhoto()
  }
}


不过在调用 drawImage 的时候要传入对应的宽和高,这里的宽和高可以从 <video> 元素中的 widthheight 获得。


const start = async () => {
  video = document.getElementById('video');
  canvas = document.getElementById('canvas');
  photo = document.getElementById('photo');
  takePhotoButton = document.getElementById('takePhotoButton');
  downloadButton = document.getElementById('downloadButton');
  // 获取摄像头的视频流
  try {
    video.srcObject = await navigator.mediaDevices.getUserMedia({video: true, audio: false})
    video.play()
  } catch (e) {
    console.error(e)
  }
  video.addEventListener('canplay', (event) => {
    if (!streaming) {
      // 按比例放大 videoHeight
      height = video.videoHeight / (video.videoWidth / width);
      // 设置 video 的宽高
      video.setAttribute('width', width);
      video.setAttribute('height', height);
      // 设置 canvas 的宽高
      canvas.setAttribute('width', width);
      canvas.setAttribute('height', height);
      streaming = true;
    }
  }, false)
  takePhotoButton.addEventListener('click', (event) => {
    // 拍照
    takePhoto()
  }, false)
}
start().then()


这样一来就能生成视频中定格后的某一个画面了。


image.png


从上面可以看到这里的 <img> 里的 srcdata:xxx 的图片数据。


清空图片




如果要重清除已经拍好的照片呢?我们可以利用 <canvas>fillRect 来生成一个空白图片,然后再转化成图片数据,放到 src 里就可以了:


// 清空操作
const clearPhoto = () => {
  const context = canvas.getContext('2d')
  // 生成空白图片
  context.fillStyle = "#AAA";
  context.fillRect(0, 0, canvas.width, canvas.height);
  const data = canvas.toDataURL('image/png');
  photo.setAttribute('src', data);
}
// 开始
const start = async () => {
  // ...
  clearButton.addEventListener('click', (event) => {
    clearPhoto();
  })
  // 生成默认空白图片
  clearPhoto();
}
start().then()


或许有的人会觉得:我把图片 v-if=falsedisplay: nonevisibility: hidden 不也可以嘛?


当然可以!这里我只是想再分享另一种思路嘛~ 因为像这种调用 fillRect 来做重置功能的是比较常用的,比较画板里的重置就可以这样来清空画布


下载




下载则比较简单了,也是面试常考的一道技巧题。


先生成一个 <a> 标签,然后通过 <canvas> 生成 URL,将这个 URL 放到 href 里,用 JS 出发 click 事件,就可模拟下载了:


// 下载操作
const downloadPhoto = () => {
  const link = document.createElement('a');
  link.download = '你的帅照.png';
  link.href = canvas.toDataURL();
  link.click();
}
const start = async () => {
  // ...
  downloadButton.addEventListener('click', (event) => {
    // 下载
    downloadPhoto()
  })
}
start().then()


总结



到这里,这个小实战就结束啦,并没有什么难度,这里稍微做下总结吧。


WebRTC 最重要的 API 就是 await navigator.mediaDevices.getUserMedia({video: true, audio: false}),通过返回值可以获取当前摄像头、麦克风的音视频流。


通过 <video> 元素的 srcObject 属性可以直接接上视频流,这在直播、P2P、视频聊天的场景都可以这样使用。


通过 <canvas>drawImage 以及 fillRect 来生成视频图片以及空白图片数据,再把这些数据放到 <img> 就可以实现 JS 生成画面的效果。



image.png

网络异常,图片无法展示
|

相关文章
|
小程序 前端开发 数据挖掘
嘘!市面上短视频(douyin)“去水印”的工具原来是这样实现的
现在视频号非常火热,之前在做抖音和快手的人就直接把之前的视频直接搬运过来了。但是从抖音app下载的视频都是带官方水印的?这个是怎么去掉的?哦,不对,他们应该都有保 留原视频的吧。但是还有很多人是直接搬运别人的视频的,那他们是怎么去水印的呢? 其实早就有很多现成的工具,如小程序、去水印app都能直接去水印,甚至还有收费的。
1032 1
嘘!市面上短视频(douyin)“去水印”的工具原来是这样实现的
|
4天前
|
移动开发 JSON 前端开发
📺搞懂前端流媒体字幕
📺搞懂前端流媒体字幕
|
5月前
|
小程序 JavaScript 前端开发
微信小程序的音频视屏播放
微信小程序的音频视屏播放
43 0
|
应用服务中间件 nginx
流媒体技术学习笔记之(十四)FFmpeg进行笔记本摄像头+麦克风实现流媒体直播服务
FFmpeg推送视频流,Nginx RTMP模块转发,VLC播放器播放,实现整个RTMP直播 查看本机电脑的设备 ffmpeg -list_devices true -f dshow -i dummy 红色标记表示视频设备和麦克风设备 看到乱码了吧!来这里查看哦   FFmpeg编码推送到R...
3334 0
|
8月前
|
小程序
uniapp 微信语音播放功能(整理)
uniapp 微信语音播放功能(整理)
|
Web App开发 前端开发 中间件
WebRTC 实战:实现 P2P 实时视频互动
只有虽然说WebRTC支持P2P,但是需要有一台信令服务器来交换双方的SDP,现在我们就来用Node实现一个信令服务器。
354 0
|
Web App开发 编解码 中间件
海康威视摄像头RTSP视频流嵌入到谷歌Chrome等WEB页面中实时播放方案(图文教程)
近期在做一个智慧城市项目,要求将海康威视、大华等摄像头RTSP视频流在Chrome、Firefox、Edge等浏览器中播放,并且要求延迟必须要低,能到多低就多低,最好是实时视频。 小编了解很多不同的方案,目前市面上大部分是转码转流方案,不仅需要服务器支持,并且需要服务器不停的转码转流,如果多路同时播放或者播放高清视频,非常容易出现卡顿、花屏等情况,延迟更是高达数秒甚至数分钟,对于一些延迟要求较高的项目来说,这简直是灾难性后果。
2623 0
海康威视摄像头RTSP视频流嵌入到谷歌Chrome等WEB页面中实时播放方案(图文教程)
|
Web App开发 缓存 算法
白话解读 WebRTC 音频 NetEQ 及优化实践
NetEQ 是 WebRTC 音视频核心技术之一,对于提高 VoIP 质量有明显的效果,本文将从更为宏观的视角,用通俗白话介绍 WebRTC 中音频 NetEQ 的相关概念背景和框架原理,以及相关的优化实践。
白话解读 WebRTC 音频 NetEQ 及优化实践
|
存储 编解码 C语言
最简单的基于FFmpeg的直播系统开发移动端例子:IOS 视频解码器
本文记录IOS平台下基于FFmpeg的视频解码器。该示例C语言的源代码来自于《最简单的基于FFMPEG+SDL的视频播放器》。相关的概念就不再重复记录了。
|
Web App开发 API 网络协议
WebRTC:一个视频聊天的简单例子
在前面的章节中,已经对WebRTC相关的重要知识点进行了介绍,包括涉及的网络协议、会话描述协议、如何进行网络穿透等,剩下的就是WebRTC的API了。
1892 0