示例:在H5中使用video结合canvas来录制视频和上传

1.背景

对于正在播放的视频,期望做到 录制这个视频流,并上传到后端服务。

2.实现思路:

1.通过 video 播放视频,不过video设置为不可见。
2.将 video里的视频帧展示在 canvas 上。
3.录制 canvas 上的绘制的内容 并生成 字节blob 包。
4.上传 字节数据包到 后端

3.实现方式

播放 video, 并将视频流 呈现在 canvas 上

写页面
注意 video 是不可见的,canvas 是可见的。

    <div style="text-align: center;margin-top:10px;">
      <canvas id="theCanvas" height=360 width=640 style="width:640px;margin:auto;"></canvas>
      <video src="hmbb.mp4" id="theVideo" autoplay=true style="display:none;"></video>
    </div>

点击播放按钮开始播放

1、初始视频操作
2、播放

其实就是获得 cavas 的绘制 context , 利用 requestAnimationFrame 的帧回调,不断的刷新和绘制 视频的内容到 canas

      $("#openBtn").click(function(){
        console.log("# 点击 openBtn");
        _chunks = [];
        _theVideo.play()
        _playID = playCanvas(_theVideo, _ctx);

        setRecorder();
      });


  // 一些初始化操作
    var init = function() {
      _theVideo = $("#theVideo").get(0);
      _theCanvas = $("#theCanvas").get(0);
      console.log(_theCanvas);
      const ctx = _theCanvas.getContext('2d');
      ctx.fillStyle = 'white';
      ctx.fillRect(0, 0, _theCanvas.width, _theCanvas.height);
      _ctx = ctx;

    };// end init

    // 通过类似定时器的方式,将 视频流的内容 逐帧写入到 canvas
    function playCanvas(srcvideo, ctx) {
      //console.log("# playCanvas... ");
      ctx.drawImage(srcvideo, 0, 0, 640, 360)
      _playID = requestAnimationFrame(() => {
        //console.log("# ctx.drawImage... id="+_playID);
        playCanvas(srcvideo, ctx)
      })
    }

录制

  1. 通过 _theCanvas.captureStream(60); 获得一个 视频流
  2. 将视频流作为参数,生成一个 MediaStreamRecorder 录制器。
  3. 调用 录制器 的 start() 方法开始录制。
  4. _mediaRecorder.ondataavailable 的回调方法中 追加保持字节。
  5. 将字节(录制的数据)上传
      $("#openBtn").click(function(){
        console.log("# 点击 openBtn");
        _chunks = [];
        _theVideo.play()
        _playID = playCanvas(_theVideo, _ctx);

        setRecorder();
      });

      $("#startBtn").click(function(){
        console.log("# 点击 startBtn");
        _mediaRecorder.start(); //录像
      });

      $("#stopBtn").click(function(){
        console.log("# 点击 stopBtn");
        _mediaRecorder.stop(); //停止录像
      });

  // 初始化录制器
    var setRecorder = function(mediaStream){
      console.log("# 初始化 mediaRecorder");
      _chunks = [];
      // 视频格式
      let VIDEO_FORMAT = 'video/webm';
      if(!MediaRecorder.isTypeSupported(VIDEO_FORMAT)){
            alert(format)
            alert("当前浏览器不支持该编码类型");
            return;
      }
      // 初始化 录像 mediaRecorder
      _mediaStream= _theCanvas.captureStream(60); // 60 FPS recording
      console.log(_mediaStream);
      _mediaRecorder = new MediaStreamRecorder(_mediaStream);
      _mediaRecorder.mimeType = VIDEO_FORMAT;
      _mediaRecorder.ondataavailable = function (data) {
          console.log("# 产生录制数据...");
          console.log(data);
          console.log("# ondataavailable, size = " + parseInt(data.size/1024) + "KB");
          _chunks.push(data);
      };
      _mediaRecorder.onstop = function(e) {
          console.log("# 录制终止 ...");
          const fullBlob = new Blob(_chunks);
          const blobURL = window.URL.createObjectURL(fullBlob);
          console.log("blob is ?, size="+parseInt(fullBlob.size/1024)+"KB. "); console.log(fullBlob);
          console.log("blobURL =" + blobURL);

          uploadFile(fullBlob);
        }
    }// end initMediaRecorder

方法:开始和停止动画(视频流)

播放

window.requestAnimationFrame() : 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。

该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

停止
问:怎么停止requestAnimationFrame?
答:使用 cancelAnimationFrame() 接收一个参数 requestAnimationFrame默认返回一个id,cancelAnimationFrame只需要传入这个id就可以停止了。

https://www.jianshu.com/p/fa5512dfb4f5

4. 我的示例代码

文字说明:
代码放在githb::https://github.com/vir56k/demo/blob/master/video2/public/index3.html

参考

https://developer.mozilla.org/zh-CN/docs/Web/API/Window/requestAnimationFrame

https://www.jianshu.com/p/fa5512dfb4f5

https://www.w3school.com.cn/jsref/dom_obj_video.asp

https://www.cnblogs.com/scarecrowlxb/p/9573976.html

最好建议看下这个
https://cloud.tencent.com/developer/article/1366886
下文的视频演示的源码值得一看
https://wendychengc.github.io/media-recorder-video-canvas/videocanvas.html

http://www.zuidaima.com/blog/3819727543307264.htm

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容