生成在特定时间由文件输入选择的视频文件的缩略图/快照

wil*_*lin 11 javascript jquery html5-video video-thumbnails html5-canvas

如何<input type="file">在背景中静默地获取视频中特定时间选择的视频文件的快照(即没有可见元素,闪烁,声音等)?

wil*_*lin 23

有四个主要步骤:

  1. 创建<canvas><video>元素.
  2. src生成的视频文件加载URL.createObjectURL<video>元素中,并通过侦听触发的特定事件等待加载.
  3. 将视频的时间设置为您要拍摄快照的位置并侦听其他事件.
  4. 使用画布抓取图像.

第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);
Run Code Online (Sandbox Code Playgroud)

请注意,视频元素具有该属性muted.不要放任何其他属性,如autoplaycontrols.还要注意他们都有班级snapshot-generator.这样我们就可以为它们设置样式,使它们不受影响:

.snapshot-generator {
  display: block;
  height: 1px;
  left: 0;
  object-fit: contain;
  position: fixed;
  top: 0;
  width: 1px;
  z-index: -1;
}
Run Code Online (Sandbox Code Playgroud)

有些浏览器设置为使用它们display: none,但是除非它们在页面上呈现,否则其他浏览器会出现严重问题,所以我们只是将它们设置得很小,这样它们基本上是不可见的.(不要将它们移到视口之外,否则你可能会在页面上看到一些丑陋的滚动条.)

第2步 - 加载视频

事情开始变得棘手.您需要收听事件以了解何时继续.不同的浏览器会触发不同的事件,不同的时间和不同的顺序,所以我会省你的努力.在视频准备好之前,有三个事件必须始终至少触发一次; 他们是:

  • 等待loadedmetadata
  • loadeddata
  • 暂停

为这些事件设置事件处理程序并跟踪已触发的事件数.一旦三人全部解雇,你就可以继续了.请记住,由于其中一些事件可能会多次触发,因此您只想处理触发的每种类型的第一个事件,并放弃后续触发.我用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);
Run Code Online (Sandbox Code Playgroud)

来源应该仅仅是通过创建对象的网址URL.createObjectURL(file),在那里file是文件对象.

第3步 - 设置时间

此阶段与上一阶段类似:设置时间然后侦听事件.在我们if之前的代码块中:

$video.one('seeked', function() {
    // Ready for next step
}).prop('currentTime', insert_time_here_in_seconds);
Run Code Online (Sandbox Code Playgroud)

幸运的是,这次只有一次活动,所以它非常简洁明了.最后...

第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();
Run Code Online (Sandbox Code Playgroud)

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

快照的值是一个数据URI,它基本上只是一个data:image/jpeg;base64以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);
Run Code Online (Sandbox Code Playgroud)

庆祝!

你有你的图像在base64!发送到服务器,把它作为src一个的<img>元素,或什么的.

例如,您可以将其解码为二进制文件并直接将其写入文件(首先修剪前缀),这将成为JPEG图像文件.

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