如何在特定时间无声地在后台(即没有可见元素,闪烁,声音等)在特定时间捕获通过<input type="file">选择的视频文件的快照?

最佳答案

有四个主要步骤:

  • 创建<canvas><video>元素。
  • src生成的视频文件的URL.createObjectURL加载到<video>元素中,并通过侦听被触发的特定事件来等待其加载
  • 将视频的时间设置为要拍摄快照并收听其他事件的时间。
  • 使用 Canvas 捕获图像。


  • 第1步-创建元素

    这非常简单:只需创建一个<canvas>和一个<video>元素,然后将它们附加到<body>(或者在任何地方,这都没有关系):
    var canvasElem = $( '<canvas class="snapshot-generator"></canvas>' ).appendTo(document.body)[0];
    var $video = $( '<video muted class="snapshot-generator"></video>' ).appendTo(document.body);
    

    请注意,video元素具有muted属性。不要放置任何其他属性,例如autoplaycontrols。还要注意,它们都具有snapshot-generator类。因此,我们可以为它们两者设置样式,以使它们不受影响:
    .snapshot-generator {
      display: block;
      height: 1px;
      left: 0;
      object-fit: contain;
      position: fixed;
      top: 0;
      width: 1px;
      z-index: -1;
    }
    

    有些浏览器将它们设置为display: none,但是其他浏览器除非将它们呈现在页面上,否则都会遇到严重的问题,因此我们将它们缩小以使它们基本上不可见。 (不过,请勿将它们移到视口(viewport)之外,否则您可能会在页面上看到一些难看的滚动条。)

    第2步-加载视频

    这是开始变得棘手的地方。您需要收听事件以了解何时继续。不同的浏览器将触发不同的事件,不同的时间和顺序,因此,我将节省您的精力。在视频准备就绪之前,必须至少触发一次三个事件:他们是:
  • 已加载元数据
  • loadingdata
  • 暂停

  • 为这些事件设置事件处理程序,并跟踪触发了多少事件。一旦三个都被解雇,您就可以继续。请记住,由于其中一些事件可能会触发多次,因此您只想处理每种触发类型的第一个事件,并丢弃后续触发。 我使用了jQuery的.one,它可以解决这个问题。
    var step_2_events_fired = 0;
    $video.one('loadedmetadata loadeddata suspend', function() {
        if (++step_2_events_fired == 3) {
            // Ready for next step
        }
    }).prop('src', insert_source_here);
    

    源应该只是通过URL.createObjectURL(file)创建的对象URL,其中file是文件对象。

    步骤3-设定时间

    该阶段与上一个阶段相似:设置时间,然后监听事件。在上一个代码的if块中:
    $video.one('seeked', function() {
        // Ready for next step
    }).prop('currentTime', insert_time_here_in_seconds);
    

    幸运的是,这一次是唯一的事件,因此非常清楚简洁。最后...

    步骤4-抓取快照

    这一部分仅使用<canvas>元素来截取屏幕截图。在seeked事件处理程序中:
    canvas_elem.height = this.videoHeight;
    canvas_elem.width = this.videoWidth;
    canvas_elem.getContext('2d').drawImage(this, 0, 0);
    var snapshot = canvas_elem.toDataURL();
    
    // Remove elements as they are no longer needed
    $video.remove();
    $(canvas_elem).remove();
    

    Canvas 需要匹配视频的尺寸(元素的而不是)以获取正确的图像。另外,我们设置 Canvas 的内部<video>.height属性,而不是 Canvas 的高度/宽度CSS样式值。

    快照的值是数据URI,它基本上只是一个以.width开头的字符串,然后是base64数据。

    我们最终的JS代码应如下所示:
    var step_2_events_fired = 0;
    $video.one('loadedmetadata loadeddata suspend', function() {
      if (++step_2_events_fired == 3) {
        $video.one('seeked', function() {
          canvas_elem.height = this.videoHeight;
          canvas_elem.width = this.videoWidth;
          canvas_elem.getContext('2d').drawImage(this, 0, 0);
          var snapshot = canvas_elem.toDataURL();
    
          // Delete the elements as they are no longer needed
          $video.remove();
          $(canvas_elem).remove();
        }).prop('currentTime', insert_time_here_in_seconds);
      }
    }).prop('src', insert_source_here);
    

    庆祝!

    您的镜像在base64中!将其发送到您的服务器,将其作为data:image/jpeg;base64元素的src或其他内容。

    例如,您可以将其解码为二进制文件,然后直接将其写入文件(首先删除前缀),该文件将成为JPEG图像文件。

    您还可以使用此功能在上传视频时提供视频预览。如果将其作为<img>src放置,请使用完整数据URI (不要删除前缀)

    10-05 20:49
    查看更多