文章目录

  • 1.前言
  • 2.思路
  • 3.源码实现

1.前言

很久没有更新博客了,一言难尽,最近也换了份工作,工作也算是稳定下来了,所以之后都会继续更新自己的博客来记录自己所学的东西,最近项目中需要这么个功能,摄像头实时推送rtsp流,因为摄像头上有各种算法,所以会有框的绘制,本来按道理应该在推流之前实现绘框,那么前端就不需要做什么处理了(直接播放就行了),但是由于开发需要时间,项目比较赶,所以需要前端来绘制框,并且需要截图录屏功能(都要带框)。

2.思路

现在需要利用canvas绘制框(canvas流),rtsp流是在video上播放的(video可直接播放媒体流),现在就有2个流,就需要合并这两个流到一个流中,所以还需要多创建一个canvas( 新的媒体流)来获取合并后的媒体流,最后把两个流的音频添加到新的媒体流中,所以看下面思路:

  1. 将canvas绘制框流叫做lineStream,rtsp流叫做videoStream,新创建的myCanvas流叫做newStream。
  2. 将lineStream完全绘制到myCanvas上(撑满整个myCanvas),然后将videoStream也绘制到myCanvas的指定位置上,利用drawImage(x, y, width, height)设置开始位置和大小,这样就绘制好了一帧的影像。
  3. 再用requestAnimationFrame不停的更新myCanvas内容, 即可实现再canvas上播放混合流 再使用myCanvas.captureStream()获取合并后的媒体流。
  4. 最后把lineStream和videoStream音频添加进去. 使用mediaStream.getAudioTracks()获取音频轨道, 添加到新的媒体流中。

3.源码实现

<template><div id="app"><!-- 项目中这里是rtsp流,这里直接用个MP4来测试 --><videoid="videoPlayer"src="../static/test.mp4"width="1000"height="562"ref="videoPlayer"></video><!-- myCanvas为新的媒体流 --><canvas id="myCanvas" ref="myCanvas"></canvas><!-- line为canvas画框的流 --><canvas id="line" ref="line"></canvas><button @click="screenShot()">截图</button><button @click="recoder()">开始录像</button><button @click="endRecoder()">结束录像</button></div>
</template><script>
export default {name: "smallVideo",data() {return {//摄像头通过websocket传过来的坐标,我这里写个测试数据data: {tgtList: {tgtCoords: [{classId: 0,rect: {height: 0.5,point: {x: 0.25,y: 0.25,},width: 0.5,},},{classId: 0,rect: {height: 0.5,point: {x: 0.75,y: 0.25,},width: 0.5,},},{classId: 0,rect: {height: 0.5,point: {x: 0.25,y: 0.75,},width: 0.5,},},{classId: 0,rect: {height: 0.5,point: {x: 0.75,y: 0.75,},width: 0.5,},},{classId: 0,rect: {height: 0.5,point: {x: 0.5,y: 0.5,},width: 0.5,},},],tgtNUM: 5,},timeStamp: 5903765758,},recorder: null, //进行录制的 MediaRecorder 对象videoData: [], //Blob对象集合};},mounted() {//动态设置line和myCanvas两个canvas的宽高let videoPlayer = this.$refs.videoPlayer,myCanvas = this.$refs.myCanvas,line = this.$refs.line,width = videoPlayer.offsetWidth,height = videoPlayer.offsetHeight,lineContext = line.getContext("2d");line.width = myCanvas.width = width;line.height = myCanvas.height = height;//转换websocket传过来的坐标let arr = this.paintDIv(this.data.tgtList.tgtCoords, width, height);//用lineCanvas实施画出框setInterval((context) => {lineContext.clearRect(0,0,videoPlayer.clientWidth,videoPlayer.clientHeight);context.drawRect(arr, lineContext);},300,this);},methods: {//转换坐标paintDIv(data, width, height) {return data.map((ele) => {ele.rect.width = Math.round(width * ele.rect.width);ele.rect.height = Math.round(height * ele.rect.height);ele.rect.point.x = Math.round(ele.rect.point.x * width - ele.rect.width / 2);ele.rect.point.y = Math.round(ele.rect.point.y * height - ele.rect.height / 2);if (ele.rect.point.x < 0) {ele.rect.width = Math.round(ele.rect.width + ele.rect.point.x);ele.rect.point.x = 0;}if (ele.rect.point.y < 0) {ele.rect.height = Math.round(ele.rect.height + ele.rect.point.y);ele.rect.point.y = 0;}return ele;});},//实时画框,为了测试这里用data里面随机一个数据每300毫秒画一个框drawRect(pionts, lineContext) {lineContext.strokeStyle = "#ff0000";lineContext.lineWidth = 5;let index = Math.round(Math.random() * 4);let piont = pionts[index];lineContext.strokeRect(piont.rect.point.x,piont.rect.point.y,piont.rect.width,piont.rect.height);},//截屏screenShot() {let videoPlayer = this.$refs.videoPlayer,myCanvas = this.$refs.myCanvas,line = this.$refs.line,width = videoPlayer.offsetWidth,height = videoPlayer.offsetHeight,context = myCanvas.getContext("2d");context.drawImage(videoPlayer, 0, 0, width, height);context.drawImage(line, 0, 0, width, height);//转成base64let data = myCanvas.toDataURL();if (data) {// 如果直接返回了base64代码部分,所以不需要截取,如果含"data:image/png;base64," 则需要自己做截取处理//解码一个Base64字符串let raw = window.atob(data.split(",")[1]),rawLength = raw.length,//将Base64字符串转成uint8数组uInt8Array = new Uint8Array(rawLength);for (var i = 0; i < rawLength; ++i) {uInt8Array[i] = raw.charCodeAt(i);}//将图片的base64 转变成Blob形式let blob = new Blob([uInt8Array], {type: "image/png",});//保存图片var a = document.createElement("a");a.download = `file_${new Date().getTime()}.png`;a.href = URL.createObjectURL(blob);document.body.appendChild(a);a.click();a.remove();window.URL.revokeObjectURL(a.href);}},//合并流mergeStream() {let videoPlayer = this.$refs.videoPlayer,myCanvas = this.$refs.myCanvas,line = this.$refs.line,width = videoPlayer.offsetWidth,height = videoPlayer.offsetHeight,context = myCanvas.getContext("2d");let videoStream = videoPlayer.captureStream(),lineStream = line.captureStream(),render = () => {if (videoStream) {context.drawImage(videoPlayer, 0, 0, width, height);context.drawImage(line, 0, 0, width, height);window.requestAnimationFrame(render);}};render();// 创建新的媒体流let newStream = myCanvas.captureStream();//合并音频videoStream.getAudioTracks().forEach((track) => newStream.addTrack(track));lineStream.getAudioTracks().forEach((track) => newStream.addTrack(track));return newStream;},//开始录像recoder() {let stream = this.mergeStream(),videoPlayer = this.$refs.videoPlayer,//注意要判断浏览器对webm的支持情况,有些时候video格式不对,在ondataavailable监听的时候会拿不到data数据(data的size为空)mime = MediaRecorder.isTypeSupported("video/webm; codecs=vp9")? "video/webm; codecs=vp9": "video/webm";this.recorder = new MediaRecorder(stream, {mimeType: mime,});this.recorder.ondataavailable = (e) => {this.videoData.push(e.data);};videoPlayer.play();this.recorder.start();},//结束录像endRecoder() {let videoPlayer = this.$refs.videoPlayer;this.recorder.stop();videoPlayer.pause();return new Promise((resolve) => {setTimeout(() => {let blob = new Blob(this.videoData, {type: "video/mp4",});let a = document.createElement("a");a.download = `file_${new Date().getTime()}.mp4`;a.href = window.URL.createObjectURL(blob);document.body.appendChild(a);a.click();a.remove();window.URL.revokeObjectURL(a.href);this.recorder = null;this.videoData = [];resolve();}, 0);});},},
};
</script><style lang="less" >
body {margin: 0;
}
canvas {/* border: 1px dashed black; *//* display: none; */position: absolute;top: 0;left: 0;
}#line {z-index: 9999;
}#myCanvas {display: none;
}
</style>

下面是实现效果:

前端利用Canvas+Video合并流实现截屏和录屏功能相关推荐

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

    功能: 实时播放视频流:采用xgplayer 实时点击按钮来截屏.录屏 注意点: 视频流播放时,获取每一帧,绘制canvas(界面上不显示). 注意canvas是有默认宽高的,要修改width.hei ...

  2. Android截屏和录屏Demo

    最近两天研究了一下安卓截屏和录屏功能的实现,基本的思路如下: 截屏:通过View绘制缓冲获得Bitmap,然后写到文件中,完成截屏的功能: 录屏:通过MediaRecorder进行video reco ...

  3. Android 音视频开发(六) -- Android Mediaprojection 截屏和录屏

    Android 音视频开发(一) – 使用AudioRecord 录制PCM(录音):AudioTrack播放音频 Android 音视频开发(二) – Camera1 实现预览.拍照功能 Andro ...

  4. 笔记本电脑截屏怎么截_电脑的截屏与录屏

    电脑的截屏与录屏 相信大家都知道 QQ的截屏和录屏快捷键:截屏:Ctrl+Alt+A录屏:Ctrl+Alt+S 这种方便快捷的方式非常受人欢迎,但是万一我们的电脑上没有下载或打开QQ,我们该怎么办呢? ...

  5. iOS 防止截屏、录屏技术

    0x00 直接看图 看图演示,可防止截屏和录屏 可以开启或者关闭,是否允许截屏和录屏 0x01 代码 JHNonRecordableView *view1 = [[JHNonRecordableVie ...

  6. Android 关于禁止应用截屏和录屏

    APP有时候为了保护用户的隐私安全会禁止用户录屏和截屏,录入说视频交友类的app,金融类的app等 可以在app的onCreate方法中添加这么一段代码: @Overrideprotected voi ...

  7. Android截屏、录屏,适配AndroidQ以上

    使用MediaProjectionManager,VirtualDisplay,AudioRecord,MediaCodec以及MediaMuxer等API实现屏幕录制功能.MediaProjecti ...

  8. 安卓java录屏_安卓实现截屏以及录屏功能Demo

    [实例简介]安卓实现截屏以及录屏功能Demo 安卓实现截屏以及录屏功能Demo [实例截图] [核心代码] package com.dzjin.screen.screenshotandrecordde ...

  9. android 实现手机录屏功能,基于MediaProjection实现Android移动手机截屏和录屏功能

    Android软件应用经常要求实现截屏和录屏的功能,那么如何实现Android软件截屏和录屏功能呢?本文将介绍基于MediaProjection实现Android移动手机截屏和录屏功能. MediaP ...

最新文章

  1. 错误处理:java.lang.NoSuchMethodException: org.apache.catalina.deploy.WebXml addFilter
  2. Vmware 中Windows和虚拟机共享文件--VMware-tools补丁安装
  3. 阿里巴巴技术专家三画:如何画好架构图
  4. 修改android virtual device路径
  5. 【论文解读】KDD20 | 图神经网络在生物医药领域的应用
  6. 机器学习实战(用Scikit-learn和TensorFlow进行机器学习)(四)
  7. 阿里云峰会|数据库也能自动驾驶?DAS全天候给你保驾护航!
  8. 计算机网络(二)——局域网硬件设备
  9. 硬盘格式化了的数据找到办法
  10. 初级第七课——模拟计算器
  11. 百度文档免费下载+PDF转word
  12. 韩立刚老师 -- 1、Linux 入门
  13. 新型城镇化3.0时代 数据交换是“智慧城市”的核心
  14. uniapp打开外部链接
  15. java拼图游戏(未补全)
  16. 联想电脑计算机无法正常启动怎么办,电脑蓝屏无法启动怎么办
  17. 【论文翻译】HCGN:面向集体分类的异构图卷积网络深度学习模型
  18. 英语berylite绿宝石BERYLITE绿柱石
  19. 1、esp32(arduino)接入阿里云MQTT及数据处理
  20. (银行简单的管理系统)java实训小型应用开发——数据库,GUI、客服端

热门文章

  1. 【重点突破】—— UniApp微信小程序开发教程学习Three
  2. ImportError: cannot import name ‘set_random_seed‘ from ‘tensorflow‘
  3. 连续状态转移算法(STA)的实现(python版)
  4. 学习笔记1--自动驾驶汽车介绍
  5. 销售订单_跨公司销售
  6. 调参侠级机器学习之股票预测初级阶段
  7. 苏州大学文正学院JAVA试卷_苏州大学文正学院试题库建设管理办法(试行)
  8. Chrome上最好用的广告拦截插件:AdBlock
  9. 微信小程序直播如何提升人气
  10. U盘下载系统之后剩余空间只剩32G?