5

我需要在使用 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/

有任何想法吗?问题是否足够清楚?

谢谢,凯文

4

3 回答 3

2

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

  1. 更改+++= 1: 为了与您之前的代码保持一致,并且因为引导 Doug Crockford 的仁慈幽灵可能有助于消除您的 javascript 代码中的故障。

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

  3. currentFrame 是否用 var 声明并在 processFrame 范围内?如果没有,只需放在 var currentFrame = 0;您定义的上方某处function processFrame(...

  4. 当您说“然后我就这样播放”时,您是使用承诺还是在所有 onloads 触发后等效地调用您的动画启动函数?一种方法是:您可以在每次触发 onload 时增加一个计数器,每次检查计数器是否等于所需的加载总数,然后才调用您的动画开始。 我的预感是这是一个缓存问题,因为由于内部结构不同,这些似乎是不同浏览器之间变化最大的。

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

  6. 可以使用 HTML5 视频标签进行 alpha 和透明度。只需使用一些 svg 转换。正如在这个 SO 答案中所说,这些很容易做到

于 2013-01-26T06:41:20.940 回答
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);
}

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

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

于 2013-01-26T19:55:12.483 回答
0

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

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

于 2013-01-26T23:19:35.763 回答