更改动画精灵表时,HTML5 Canvas挂起(逐帧视频播放)

时间:2013-01-15 07:57:58

标签: jquery html5 mobile video

我需要使用HTML5在移动设备上播放具有透明背景的视频。在Windows Phone上,我可以捕获视频标签的当前帧并在画布中显示。但这不适用于Android和iOS设备(我认为出于安全原因?)

我的解决方案是使用FFMPEG分割.flv,然后将这些帧拼接成大图像,如精灵表。

问题是当我切换到新的相框时动画'挂起'。我只是通过视觉和控制台检查了这一点(当我更改当前的精灵表行时通过记录。)我通过查看当我更改精灵表时它是如何挂起来测试它,以及当我更改它时它是如何挂起的只是一遍又一遍地循环同一张纸。

我预先加载了所有图片:

var frameImages = [];

for(var i = 0; i < 35; i++)
{
  frameImages.push(new Image());
  frameImages[i].src = 'frame' + i + '.png';

  console.log(frameImages[i].src);

  frameImages[i].onload = function()
  {
    // Notify us that it's been loaded.
    console.log("Image loaded");
  }
}

然后像这样玩:

processFrame = function()
{
    outputCanvas.width = outputCanvas.width;
    output.drawImage(frameImages[currentFrame], (curCol*153), (curRow*448), 153, 448, 0, 0, 153, 448);
    curCol += 1;

    if(curCol >= maxCol)
    {
      curCol = 0;
      curRow += 1;

      if(curRow >= maxRow)
      {
        curRow = 0;
        currentFrame++;
      }
    }
  }
}

var mozstart = window.mozAnimationStartTime;

step = function(timestamp) {

  var diff = (new Date().getTime() - start) - time;
  if(diff >= frameDelay)
  {
    processFrame();
    time += frameDelay;
  }
}

我在使用Win 7计算机的Chrome v 23.0.1271.97 m和使用Chrome的Nexus 7上尝试过此操作。

在此见到它:
http://savedbythecode.com/spokes/mozanimation.php - 这是使用mozAnimationStartTime
http://savedbythecode.com/spokes/newplayer.php - 这是使用每个步骤调整的常规JS计时器(来自http://www.sitepoint.com/creating-accurate-timers-in-javascript/

有什么想法吗?问题是否足够明确?

谢谢, 凯文

3 个答案:

答案 0 :(得分:2)

酷代码。为什么你不能只使用视频标签(参见选项6)?无论如何,我只会列出我能想到的问题并希望它能帮到你:

  1. ++更改为+= 1:与您之前的代码保持一致,并且因为channeling Doug Crockford's benevolent ghost可能有助于消除您的JavaScript代码中的故障。

  2. 将一堆图像拼接成一张“精灵表”。当您想要下一张表时,代码会挂起。因此,因为您的代码还没有工作,作为测试,删除循环遍历每个帧中的子图像的所有循环,并直接将每个渲染中的frameImages数组中的每个帧输出到画布以查看它是否仍然挂起。结果将告诉您代码是否可以到达frameImages中的下一帧,或者它是否可以。这可能会使您更接近错误的来源。

  3. currentFrame是用var声明的,并且在processFrame的范围内?如果没有,就放 var currentFrame = 0;位于您定义function processFrame(...

  4. 之上的某处
  5. 当你说“然后我像这样播放”你是否使用了promise或在所有onloads被触发后等效地调用你的动画启动功能? 执行此操作的方法是:您可以使用每个启动的onload递增计数器一次,并且每次检查计数器是否等于所需的负载总数,然后再调用动画启动。 我的预感是这是一个缓存问题,因为由于内部不同,这些似乎是不同浏览器之间变化最大的。

  6. 尝试在drawImage调用和更新currentFrame之间清除画布,因为在更改图像时,它可能有助于重置画布的内部状态。

  7. 可以使用HTML5视频代码执行Alpha和透明度。只需使用一些svg转换。这些很容易实现,如this SO answer

  8. 中所述

答案 1 :(得分:2)

这是另一种不那么正统的解决方案 - 使用视频作为画布输入,并使用getImageData在每个帧中绿屏。

基本上你已经有了视频标签和画布

<video id='source' style='display: none;'>
   <source>whatever.mp4</source>
   <source>whatever.ogg</source>
</video>
<canvas id='screen' width='videoWidth' height='videoHeight></canvas>

然后你设置绘制功能

var t = null;
document.getElementById('source').addEventListener('playing', function(){
   var vid = this;   
   t = setInterval(function(){
       if (vid.paused || vid.ended){
          clearInterval(t);
          return
       } else {
          drawFrame();
       }
   }, 20)
}, false)

并且您的并条机功能是

function drawFrame(){
   var ctx = document.getElementById('screen').getContext('2d'),
       vid = document.getElementById('source');

   ctx.drawImage(vid, 0, 0);
   var tolerance = 0.2,
       bgColor = [0, 255, 0], //or whatever 
       imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height),
       len = imageData.data.length;

   for (var i=0; i<len; i+=4){
      var r = imageData.data[i],
          g = imageData.data[i+1],
          b = imageData.data[i+2];

      //There's lots of different color comparing methods, just check google
      var dif = colorCompare([r, g, b], bgColor);
      if (dif < tolerance){
         imageData.data[i+3] = 255;
      }
   }

   ctx.putImageData(imageData, 0, 0);
}

您可以使用它的不同部分来达到您想要的性能水平。对于相对较小(尺寸方面)的视频,您的表现应该不是问题。您可能必须使用缓冲画布来避免屏幕闪烁,但我对此表示怀疑。您可以调整公差变量以避免任何光环效应。

并不是说它是最好的解决方案,但它绝对是一个可行的解决方案,如果您只想更改输入视频而无需额外工作就可以更改视频,那么该解决方案就可以使用。

答案 2 :(得分:0)

不确定你是否看过这个......但你有没有想过利用jquery队列?当我把它加载到我的浏览器中时,我看到一些帧没有显示,或者空白,然后回来等等等。我之前用过队列做同步处理说ajax调用我想“链“在一个特定的顺序中......这可能是一个区域,您可以尝试以同步的方式加载每个”图像框架“,确保每个框架以正确的顺序加载?

http://api.jquery.com/queue/