在javascript中从视频复制到画布

Posted

技术标签:

【中文标题】在javascript中从视频复制到画布【英文标题】:Copying from video to canvas in javascript 【发布时间】:2021-11-03 05:50:33 【问题描述】:

我正在尝试加载视频并将其缩略图绘制到画布中。我遇到的问题是,当视频图像准备好复制时,我找不到触发的事件。当加载数据事件触发时,大小设置正确,但画布图像为空白。如果我取消注释计时器以延迟一秒钟,则画布被填充,但没有延迟它不是。是否有一些我忽略的事件,或者其他方式来告诉视频元素何时能够复制?我尝试了 load、canplay、canplaythrough、loadeddata、progress,但似乎都没有在正确的时间出现。

<html>
  <head>
    <script>
      var video, image, width, height;
      function copy(src) 
        video = document.getElementById('video');
        video.addEventListener('loadeddata', x => finishCopy()); 
        video.src = src;
      
      function finishCopy() 
        width = document.defaultView.getComputedStyle(video).width.replace(/[^.0-]/g, '');
        height = document.defaultView.getComputedStyle(video).height.replace(/[^.0-]/g, '');
        image = document.getElementById('image');
        image.width = width;
        image.height = height;
        //setTimeout(draw, 1000);
        draw();
      

      function draw() 
        let ctxt = image.getContext('2d');
        ctxt.drawImage(video, 0, 0, width, height);
      

    </script>
  </head>
  <body >
    <video id="video" autostart="false" ></video>
    <canvas id="image"></canvas>
  </body>
</html>

【问题讨论】:

【参考方案1】:

虽然loadeddata 事件会在当前帧的数据加载后立即触发,但它并没有给出任何暗示这实际上意味着什么。

然而,在实践中,这意味着在某些情况下,或者在某些浏览器中,它可能能够呈现第一帧 - 尽管您不能依赖它。

解决方案是寻找一个事件,该事件稍后在加载/处理视频的过程中触发。

这些事件发生的顺序如下:

    加载开始 持续时间变化 加载元数据 加载数据 进展 可以玩

因此,如果我们只使用canplay 事件而不是loadeddata,事情应该会按预期工作。

这是一个例子:

var video, image, width, height;

function copy(src) 
  video = document.getElementById('video');
  video.addEventListener('canplay', x => finishCopy());
  video.src = src;


function finishCopy() 
  width = parseInt(document.defaultView.getComputedStyle(video).width);
  height = parseInt(document.defaultView.getComputedStyle(video).height);
  image = document.getElementById('image');
  image.width = width;
  image.height = height;
  draw();


function draw() 
  let ctxt = image.getContext('2d');
  ctxt.drawImage(video, 0, 0, width, height);


copy("https://www.w3schools.com/html/mov_bbb.mp4")
<video id="video" autostart="false"></video>
<canvas id="image"></canvas>

编辑

在使用不同浏览器进行一些测试后,我的解决方案似乎在 Chrome 中失败了。尽管如此,我还是找到了一种在 Chrome 中也可以使用的解决方法。

您可以将一个相当未记录的参数附加到视频的 URL,让您可以指定视频的开始位置甚至范围 (#t=)。

现在,如果您将起始位置设置为 0.000001 之类的较低值,您仍会看到视频的第一帧,并且可以在画布上进行绘制。

但是我的测试也表明,如果您使用 canplaythrough 事件而不是 canplayloadeddata,这仅适用于 FireFox 和 Chrome。

这是修改后的示例:

var video, image, width, height;

function copy(src) 
  video = document.getElementById('video');
  video.addEventListener('canplaythrough', x => finishCopy());
  video.src = src;


function finishCopy() 
  width = parseInt(document.defaultView.getComputedStyle(video).width);
  height = parseInt(document.defaultView.getComputedStyle(video).height);
  image = document.getElementById('image');
  image.width = width;
  image.height = height;
  draw();


function draw() 
  let ctxt = image.getContext('2d');
  ctxt.drawImage(video, 0, 0, width, height);


copy("https://www.w3schools.com/html/mov_bbb.mp4#t=0.000001")
<video id="video" autostart="false"></video>
<canvas id="image"></canvas>

【讨论】:

感谢您的回复,但很抱歉,这对我不起作用。我之前尝试过“canplay”事件,现在还测试了您的示例代码。我检查了画布,然后添加了一个实心边框以确保我正在寻找正确的空间。画布大小是正确的,但它是空白的。我还在 firefox 和 safari 上试过它——它确实在 Safari 上工作,但在 Chrome 或 Firefox 上却不行。我认为它要么是特定于浏览器的,要么可能是时间问题。我笨拙的解决方法确实适用于所有 3。 抱歉 russel - 我只是忘了在 Chrome 中测试它。不过,我找到了另一种解决方法并更新了我的答案。 太棒了!非常感谢您的努力 - 不仅仅是为了解决方案,而是为了了解正在发生的事情。这将使支持其他浏览器变得更加容易。【参考方案2】:

如果有人有更好的解决方案,我会很欢迎,但我确实找到了一种方法来做我想做的事,方法是加载元数据,进行搜索,然后在“搜索”事件侦听器中复制图像。我正在使用 Chrome,并且从我所读到的内容中怀疑这可能不是一个通用的解决方案,但由于目前我只支持 Chrome,这似乎可以满足我的需求。

<html>
  <head>
    <script>
      var video, image, width, height;
      function copy(src) 
        video = document.getElementById('video');
        video.addEventListener('loadedmetadata', x => video.currentTime = 0);
        video.addEventListener('seeked', x => finishCopy());
        video.src = src;
      

      function finishCopy() 
        width = document.defaultView.getComputedStyle(video).width.replace(/[^.0-9]/g, '');
        height = document.defaultView.getComputedStyle(video).height.replace(/[^.0-9]/g, '');
        image = document.getElementById('image');
        image.width = width;
        image.height = height;
        //setTimeout(draw, 1000);
        draw();
      

      function draw() 
        let ctxt = image.getContext('2d');
        ctxt.drawImage(video, 0, 0, width, height);
      
    </script>
  </head>
  <body >
    <button onclick="copy('asd.mp4')">Copy asd.mp4</button>
    <br />
    <video id="video" preload="metadata" autostart="false" ></video>
    <canvas id="image"></canvas>
  </body>
</html>

【讨论】:

以上是关于在javascript中从视频复制到画布的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript将视频并入画布框

如何在 JavaScript 中从末尾切分字符串? [复制]

在基于 Java 的 Web 应用程序中从数据库到前端的实时数据复制

Java中从字符串到sql日期的日期转换给出不同的输出? [复制]

如何在 JavaScript 中从 html 引用 iframe

如何从具有所有属性的页面复制canvas元素?