HTML5视频转换成画布播放速度非常缓慢

3
我制作了一个HTML5视频播放器,将其加载到画布中进行操作,然后再将其返回到画布中进行显示。视频开始播放的速度相当缓慢,并且每次播放时帧率只会变得更差。目前,我正在视频中操作的只是颜色值,在视频暂停时进行操作,但将来将使用实时操作在未来发布的视频中进行操作。
我使用以下教程学习了这个技巧:https://www.youtube.com/watch?v=zjQzP3mOXdc 以下是相关代码,但可能会有其他地方的干扰,所以请随意查看下面链接中的源代码。
var v = document.getElementById('video');
var color = "#DA7AC1";
var processes={
  timerCallback:function() {
    if (this.v2.paused || this.v2.ended) {
      return;
    }
        this.ctxIn.drawImage(this.v2,0,0,this.width,this.height);
        this.pixelScan();
        var self=this;
        setTimeout(function() {
          self.timerCallback();
        }, 0);
  },
  doLoad:function(){
    this.v2=document.getElementById("video");
    this.cIn=document.getElementById("cIn");
    this.ctxIn=this.cIn.getContext("2d");
    this.cOut=document.getElementById("cOut");
    this.ctxOut=this.cOut.getContext("2d");
    var self=this;
    this.v2.addEventListener("playing", function() {
      self.width=self.v2.videoWidth;
      self.height=self.v2.videoHeight;
      cIn.width=self.v2.videoWidth;
      cIn.height=self.v2.videoHeight;
      cOut.width=self.v2.videoWidth;
      cOut.height=self.v2.videoHeight;
      self.timerCallback();
    }, false);
  },
  pixelScan: function() {
    var frame = this.ctxIn.getImageData(0,0,this.width,this.height);
    for(var i=0; i<frame.data.length;i+=4) {
      var grayscale=frame.data[i]*.3+frame.data[i+1]*.59+frame.data[i+2]*.11;
      frame.data[i]=grayscale;
      frame.data[i+1]=grayscale;
      frame.data[i+2]=grayscale;
    }
    this.ctxOut.putImageData(frame,0,0);
    return;
  }
}

任何帮助都将不胜感激!谢谢!

http://coreytegeler.com/ethan/

3个回答

8

原因 1

尝试调整计时器,避免将 0 作为超时值:

setTimeout(function() {
      self.timerCallback();
}, 34);

由于视频帧率通常不超过30 FPS(NTSC)或25 FPS(PAL),因此34ms已经足够了,即1000/30。如果您使用0,您会冒着堆积调用的风险,这意味着浏览器将忙于尝试清空事件队列。

如果您使用低于33-34ms的任何值,则最终会多次处理相同的帧,这当然是不必要的(您的视频实际上是29.97 FPS / NTSC,因此您可能需要考虑保持34ms)。

原因2

视频分辨率也是全高清(1920x1080),对于canvas和JS来说,这有点太多了以至于无法实时处理(对于典型的消费计算机)。尝试减小视频大小,使普通规格的计算机能够处理数据。

原因3(部分)

您不需要两个屏幕画布甚至一个屏幕视频。尝试动态创建这些标签,并将它们插入DOM中。在屏幕上使用单个画布并将结果绘制到其中(您可以从一个画布放置ImageData到另一个画布)。

原因4(部分)

理想情况下,将setTimeout替换为requestAnimationFrame方法,因为这样可以显着提高同步和效率。您可以实现一个切换来将FPS降低到例如30,因为您不需要两次处理每个帧(参考30 FPS视频帧率)。

更新

要动态创建这些元素(参见原因3),您可以像这样做:

var canvas = document.createElement('canvas'),
    video = document.createElement('video'),
    ctx = canvas.getContext('2d');

video.preload = 'auto';
video.addEventListener('canplay', start, false);

if (video.canPlayType('video/mp4')) {
    video.src = 'videoUrl.mp4';

} else if ...etc.

当视频加载了足够的数据时(在metadata或canplay事件触发时),您可以将屏幕外(和屏幕内)的画布元素设置为视频的大小:

canvas.width = video.videoWidth;
canvas.height = video.videoHeight;

然后,在播放过程中,对其缓冲区进行处理并将其复制到之前定义的屏幕画布上。

您不必拥有一个离屏画布 - 我只是提到了您在原始代码中使用了in和out画布。在这种情况下,您可以简单地使用一个屏幕画布和离屏视频,并绘制到视频帧,对其进行处理并将处理后的数据放回。应该也能正常工作。


哇,非常感谢您提供的所有信息,我已经修复了超时值,并且在初始加载时已经注意到速度更快。但是,在播放一两次后,它仍然会完全混乱。我将继续按照您的建议进行工作,我没有意识到我可以在不实际将其他画布和视频标签放到页面上的情况下实现我的预期目标。这应该能够解决所有问题! - Tomelower
我不确定我是否理解如何动态添加画布和视频标签,并添加所有相同的参数和源。 - Tomelower
刚刚删除了以下代码,一切似乎都清除了,我仍计划采取进一步措施以确保快速缓冲,但目前唯一的问题是当暂停时会退回几帧,猜测可以通过requestAnimationFrame来解决。
var self=this; setTimeout(function() { self.timerCallback(); }, 0);
- Tomelower
@CoreyTegeler 我更新了我的回答,详细说明了如何动态创建视频/画布元素。 - user1693593

1

我在Chrome中运行了一个性能分析,并指向第46行占用最多的CPU。

enter image description here

setTimeout(function() {
    self.timerCallback();
}, 0);

也许增加超时时间可以防止它出现延迟。

是的!将其更改为34确实消除了一些延迟。 - Tomelower

1
我曾经遇到相同的问题并尝试了多种解决方法。我使用的是Premier Elements,该软件无法导出为mp4格式,因此使用HandBrake进行转换。我还尝试使用FFMpeg进行转换,但两者都没有成功。
我的解决方法是切换到Kdenlive作为我的视频编辑器,它可以直接导出为MP4格式,而且该视频效果完美。
因此,如果您遇到了慢渲染的问题,很可能是视频编码的问题。最简单的解决方法是获取高质量的视频编辑器,例如Premier Pro、Final Cut或Kdenlive。Kdenlive是免费的,但它有一个巨大的学习曲线和较差的公共文档。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接