canvas实现实时截屏、录屏,解决模糊问题

功能:

  1. 实时播放视频流:采用xgplayer
  2. 实时点击按钮来截屏、录屏

注意点:

  1. 视频流播放时,获取每一帧,绘制canvas(界面上不显示)。
  2. 注意canvas是有默认宽高的,要修改width、height属性,否则绘制的canvas宽高比不对,会变形模糊。
  3. videoWidth、videoHeight获取 视频流真正的分辨率(用来获取宽高比)

index.html 引用下面两个工具

<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/RecordRTC/5.6.1/RecordRTC.min.js"></script>
<script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/html2canvas/0.4.0/html2canvas.min.js"></script>
<template>
  <div class="video-player">
    <div class="uav-video" :id="videoRef" :ref="videoRef"></div>
    <canvas v-if="isShow" v-show="whetherShow" ref="myCanvas" class="clone-video"></canvas>
  </div>
</template>

<script>
import 'flv.js';
import 'xgplayer';
import FlvJsPlayer from 'xgplayer-flv.js';
import api from '@/services';
import constant from '@/utils/common';
import utils from '@/utils/utils';
import axios from 'axios';
import bus from '@/bus';
import RecordRTC from 'recordrtc';
var player;

export default {
  props: {
    screenShot: {
      type: Boolean,
      default: false
    },
    videoUrl: {
      type: String,
      default: constant.uavUrl
    },
    videoRef: {
      type: String,
      default: 'uavVideo'
    },
    // 是否显示当前的canvas
    isShow: {
      type: Boolean,
      default: false
    }
  },

  data() {
    return {
      completeTimer: null, // 视频加载完成的interVal定时器
      // 获取元素的canvas video 和 画板 解决延时器不一致的问题
      timerId: null,
      canvas: null,
      context: null,
      video: null, // recorder对象 和 按钮控制 以及是否展示
      recorder: null,
      whetherShow: false,
      isStart: false // 开始录屏的标志位
    };
  },

  mounted() {
    this.createPlayer(constant[this.videoUrl]);
  },

  methods: {
    // 创建新的监控
    createPlayer(uid) {
      console.log('flv url====', uid);

      // 生成播放器
      player = new FlvJsPlayer({
        el: this.$refs[this.videoRef],
        isLive: true,
        url: uid,
        autoplay: true,
        height: '100%',
        width: '100%',
        // fluid: true,
        // fitVideoSize: 'fixWidth',
        /**
         * 由于 Chrome 新的 autoplay 政策,必须设置静音才可播放。
         * 但经测试 Chrome 下 xgplayer 仍无法自动播放。需要手动再配置一下muted为true。flv.js 却可播放,初步怀疑是 xgplayer 问题
         * 其他浏览器暂无此问题(Firefox, Safari, 360极速浏览器, Edge)
         */
        muted: true,
        screenShot: this.screenShot,
        flvOptionalConfig: {
          enableWorker: true,
          enableStashBuffer: false,
          stashInitialSize: 128
        },
        lang: 'zh-cn',
        controls: true,
        ignores: ['play', 'volume', 'fullscreen'],
        controlsList: ['nofullscreen', 'noremoteplayback']
      });
      
      player.muted = true; // 需要再次手动设置下静音才生效
      this.resetFrame(player, this.videoRef);

      // 重连
      player.on('error', e => {
        player.destroy();
      });

      player.once('destroy', () => {
        this.isStart = false;
        if (this.completeTimer) {
          clearInterval(this.completeTimer);
        }
        setTimeout(() => {
          this.createPlayer(uid);
        }, 2000);
      });

      player.on('waiting', () => {
        var timer = setTimeout(() => {
          player.once('waiting', () => {
            // console.log('等待三秒后重新播放咯');
            player.reload();
          });
          player.once('canplaythrough', () => {
            clearTimeout(timer);
            timer = null;
          });
        }, 15000);
      });

      player.on('canplaythrough', e => {
        console.log('视频可以流畅播放');
        if (!this.isShow) return;
        //  当视频可以播放时显示录屏按钮
        this.whetherShow = true;
        this.isStart = true;

        // 当视频可以播放时开始获取当前的video元素和canvas元素以及创建recorder对象
        this.video = e.video;
        this.canvas = this.$refs['myCanvas'];

        this.$nextTick(() => {
          // 要手动修改 canvas 的宽高比,否则会使用默认的300 150.影响后面的计算
          this.canvas.width = document.getElementsByClassName('video-player')[0].clientWidth;
          this.canvas.height = document.getElementsByClassName('video-player')[0].clientHeight;
          this.context = this.canvas.getContext('2d');
          this.context.imageSmoothingEnabled = false;
        });
      });
    },

    // 销毁此播放器并通知后端关闭推流
    closePlayer(player) {
      player.destroy();
    },

    // 将直播帧设置为缓存的最新的一帧
    resetFrame(player, mseId) {
      player.once('complete', () => {
        if (document.getElementById(mseId).childNodes && document.getElementById(mseId).childNodes.length > 0) {
          let htmlMediaElement = document.getElementById(mseId).childNodes[0];
          this.completeTimer = setInterval(() => {
            if (!htmlMediaElement.buffered.length) {
              return;
            }
            // 实时调用
            if (this.isShow && this.isStart) {
              this.draw(player.video);
            }
            // console.log('定时获取最新---视频');
            let end = htmlMediaElement.buffered.end(0);
            let diff = end - htmlMediaElement.currentTime;
            if (diff >= 10) {
              htmlMediaElement.currentTime = end;
            }
          }, 2000);
        }
      });
    },
    handleReload() {
      player.reload();
    },
    // 实时获取当前页面中的video 动态 来实现录屏
    draw() {
      // 用视频真正宽高比例,以高为基准,水平方向留白,来画canvas。
      let _contextWidth = (this.video.videoWidth * this.canvas.clientHeight) / this.video.videoHeight,
        _contextHeight = this.canvas.clientHeight;

      this.context.clearRect(0, 0, _contextWidth, _contextHeight);
      this.context.drawImage(this.video, (this.canvas.width - _contextWidth) / 2, 0, _contextWidth, _contextHeight);
      if (this.isStart) {
        this.timerId = setTimeout(this.draw, 0);
      }
    },
    // 点击录屏
    StartRecord() {
      this.recorder = new RecordRTC(this.canvas, {
        type: 'canvas'
      });
      this.recorder.startRecording();
    },
    // 点击结束 获取媒体流
    EndRecord() {
      this.recorder.stopRecording(() => {
        const blob = this.recorder.getBlob();
        var _file = new File([blob], 'screenVideo-' + new Date().getTime() + '.mp4');
        var forms = new FormData();
        forms.append('file', _file);
        forms.append('uavName', this.$store.state.uavId);
        forms.append('type', 'video');
        axios
          .post('/api/file/upload', forms)
          .then(res => {
            this.$message.success('录频上传成功');
            bus.$emit('uploadSceenShot', 'success');
          })
          .catch(res => {
            this.$message.error(res.msg);
            bus.$emit('uploadSceenShot', 'fail');
          });
      });
    },
    // 点击截图
    async Screenshots() {
      this.$nextTick(() => {
        let _base64 = this.canvas.toDataURL('image/png');
        let _file = utils.base64ToFile(_base64, 'screenShoot-' + new Date().getTime() + '.png');
        var forms = new FormData();
        forms.append('file', _file);
        forms.append('uavName', this.$store.state.uavId);
        forms.append('type', 'image');
        axios
          .post('/api/file/upload', forms)
          .then(res => {
            this.$message.success('截频上传成功');
            bus.$emit('uploadSceenShot', 'success');
          })
          .catch(res => {
            this.$message.error(res.msg);
            bus.$emit('uploadSceenShot', 'fail');
          });
      });
    }
  },
  destroyed() {
    window.clearTimeout(this.timerId);
    this.isStart = false;
  }
};
</script>
<style lang="less" scoped>
.video-player,
.video-player #mse-monitor {
  width: 100% !important;
  height: 100% !important;
}

.video-player {
  height: 100% !important;
  .reload {
    position: absolute;
    top: 0;
    cursor: pointer;
  }
}

/deep/ .xgplayer-live {
  display: none !important;
}

/deep/ .xgplayer-screenshot {
  margin-right: 6vw !important;

  span {
    font-size: 1.2vw !important;
    width: 4vw !important;
  }
}
.clone-video {
  width: 100%;
  height: 100%;
  position: absolute;
  top: -99999999px;  // 隐藏canvas
}
</style>
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值