前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >利用WebRTC给自己拍照

利用WebRTC给自己拍照

作者头像
码农帮派
发布2021-01-12 14:55:18
8210
发布2021-01-12 14:55:18
举报
文章被收录于专栏:码农帮派码农帮派

在利用WebRTC进行拍照之前,先来学习两个概念:非编码帧、编码帧。

1. 非编码帧

播放音频文件的时候,播放的其实是一幅幅图像数据,在播放器播放某个音频文件的时候,会按照一定的时间间隔从视频文件中读取解码后的视频帧,这样视频就动了起来。播放从摄像头中获取的视频帧也是如此,只不过从摄像头中获取到的本来就是非编码帧,无需解码。

  • - 播放的视频帧之间的间隔时间是非常小的,如果按照20帧的帧率计算,每帧的间隔是50ms;
  • - 播放器播放的是非编码帧(解码之后的帧),而这些非编码帧其实就是一幅幅独立的图像;

从摄像头中采集到的非编码帧,非编码帧的格式一般是YUV或者RGB的格式。

2. 编码帧

相比于非编码帧,经过编码器(H264/H265、VP8/VP9)压缩之后的帧称为是编码帧,以H264为例,经过H264编码的帧包括下面三种类型:

  • I帧:关键帧,压缩率低,可以单独解码成一幅完整的图像;
  • P帧:参考帧,压缩率较高,解码时依赖于前面已经解码的数据;
  • B帧:前后参考帧,压缩率高,解码时不光依赖前面已经解码的帧,而且还依赖后面的P帧,所以B帧的解码需要在P帧的后面。

关于I帧、P帧、B帧

以H264视频压缩标准为例

我们在传输视频数据的每一帧数据的时候,发现单纯的传输视频图像,视频帧的数据量是非常大的,在以太网中单个数据包的大小是1.5k,那么为了完整的传输一个图片帧可能需要几十个数据包,对于现有的网络是无法接受的。在视频传输和存储的过程中,人们发现视频帧之间存在大量的重复数据,如果将这些重复数据剔除,在接收端再进行恢复,这样就可以大大减少网络带宽的压力,这就是H264视频压缩标准。

编码器将多张图片帧编码成一组GOP(Group Of Picture),这组GOP数据是一组连续的画面,在这组GOP数据中,第一帧是I帧和其他多个P/B帧组成。I帧称为是关键帧,I帧的压缩率低,可以解码出一张完整的图像帧,P帧是前向预测帧,B帧是双向内插帧,I帧是一副完整的图像帧,而P/B帧记录的是在I帧的基础上视频流数据发生的变化,如果没有I帧,P/B帧无法解码的,而无P/B帧,I帧就是一个静态的画面。编码器在进行编码的时候,会比较前后两个视频帧的变化率,要是变化率达到了一定程度(比如前后两幅24位真彩图中有70%的数据发生了改变),那么就会从后一帧开始重新划分一个GOP。

播放器播放的视频帧是非编码帧,我们拍照的过程其实就是从连续播放的一幅幅非编码帧中抽取一张正在播放的帧。

使用WebRTC进行拍照,并添加滤镜:

代码语言:javascript
复制
<template>
    <div class="panel">
        <video class="small_panel" autoplay playsinline></video>
    </div>

    <div class="operate">
        <div><button @click="handleTakePhoto">Take</button></div>
        <canvas id="picture" :class="filterSelectClazz"></canvas>
        <div><button @click="handleSavePhoto">Save</button></div>
    </div>

    <div class="panel">
        <select @change="handleSelectFilter">
            <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>
</template>

<script>
  import { defineComponent, onMounted, ref } from "vue";

  export default defineComponent({
    name: "TestWebRTC",
    setup () {
      // 采集视频数据
      const getUserMedia = () => {
        const mediaStreamConstrains = {
          video: true,
        };

        const localVideo = document.querySelector('video');

        navigator.mediaDevices.getUserMedia(mediaStreamConstrains).then((mediaStream) => {
          localVideo.srcObject = mediaStream;
          // 将mediaStream挂载到window上
          window.mediaStream = mediaStream;
        }).catch((error) => {
          console.log('[Error]getUSerMedia:', error);
        });
      };

      // 从video的视频流中提取图片
      const handleTakePhoto = () => {
        const picture = document.querySelector('canvas#picture');
        picture.width = 200;
        picture.height = 200;
        const videoPlayer = document.querySelector('video');
        picture.getContext('2d').drawImage(videoPlayer, 0, 0, picture.width, picture.height);
      };

      // 保存图片
      const handleSavePhoto = () => {
        const aTag = document.createElement('a');
        aTag.download = 'phote_' + new Date().getTime();

        const canvas = document.querySelector('canvas#picture');
        aTag.href = canvas.toDataURL("image/png");
        document.body.appendChild(aTag);
        aTag.click();
        aTag.remove();
      };

      const filterSelectClazz = ref("");

      // 设置图片滤镜
      const handleSelectFilter = (e) => {
        if (e.target.value) {
          filterSelectClazz.value = e.target.value;
        } else {
          filterSelectClazz.value = '';
        }
      };

      onMounted(() => {
        getUserMedia();
      });

      return {
        handleTakePhoto,
        handleSavePhoto,
        filterSelectClazz,
        handleSelectFilter,
      };
    }
  })
</script>

<style scoped>
    .panel { text-align: center; margin-top: 20px; }
    .small_panel { width: 160px; height: 160px; }

    .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>
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农帮派 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档