在HTML5视频中获取帧数

17

我试图捕获视频的每一帧编号,然而似乎没有办法实现。所以我开始运行自己的时钟来匹配视频的帧数,但它们从未匹配,并且随着视频播放,差异不断增加。

请查看我的示例:http://jsbin.com/dopuvo/4/edit

我在 Adobe After Effect 中的每个帧上添加了帧编号,因此我有更准确的差异信息。视频以 29.97fps 运行,requestAnimationFrame 也设置为以相同的速率递增数字,但我不确定这种差异是从哪里产生的。

有时它们匹配,有时它们不匹配。我也尝试离线运行,但结果相同。有任何帮助吗?


谢谢Samuel,实际上这就是我遵循的解决方案,但我需要在视频向前移动时继续获取帧数。此外,我尝试过不舍入总帧数,但没有成功。 - SayedTaqui
如果您控制它正在绘制的画布,可能可以在那里保持计数器:http://www.kaizou.org/2012/09/frame-by-frame-video-effects-using-html5-and/ 尽管从我的角度来看,这也看起来像黑魔法哈哈哈,请参见https://dev59.com/R2Qn5IYBdhLWcg3wJ0i6 - rogerdpack
3个回答

16

我在github上找到了相关内容:https://github.com/allensarkisyan/VideoFrame

我已经在这个fiddle中实现了它:https://jsfiddle.net/k0y8tp2v/

var currentFrame = $('#currentFrame');
var video = VideoFrame({
    id : 'video',
    frameRate: 25,
    callback : function(frame) {
        currentFrame.html(frame);
    }
});

$('#play-pause').click(function(){
    if(video.video.paused){
        video.video.play();
        video.listen('frame');
        $(this).html('Pause');
    }else{
        video.video.pause();
        video.stopListen();
        $(this).html('Play');
    }
});

编辑:已更新fiddle以适应新视频,因此它可以正常工作。

编辑:正如指出的那样,该视频为25fps,因此我进行了更新,并在那里删除了对jQuery的依赖。
非jQuery版本:
https://jsfiddle.net/k0y8tp2v/1/

var currentFrame = document.getElementById('currentFrame');
var video = VideoFrame({
    id : 'video',
    frameRate: 25,
    callback : function(frame) {
        currentFrame.innerHTML = frame ;
    }
});

document.getElementById('play-pause').addEventListener('click', function(e){
    if(video.video.paused){
        video.video.play();
        video.listen('frame');
        e.target.innerHTML = 'Pause';
    }else{
        video.video.pause();
        video.stopListen();
        e.target.innerHTML = 'Play';
    }
});

有时候当视频停止时,可能会出现1帧的偏差,但这可能可以通过在视频暂停/停止时重新设置帧数HTML来修复。 - 2pha
看起来该库通过设置一个定时器,在“每个期望帧速率的两倍”处轮询它来完成帧速率跟踪,以供关注者使用 :) - rogerdpack
1
frameRate 应该与视频的实际帧率匹配。将其设置为 100 不会影响播放速度,但会影响帧计数。 - Dinesh Bolkensteyn
感谢您的代码。我使用FFmpeg软件和Quick Time Player进行了检查。有时,由VideoFrame给出的帧数与FFmpeg给出的帧数相差1-2。在JavaScript中是否有其他获取准确帧数的方法? - Soorajlal K G
2
@2pha感谢您提供的代码示例。当前视频(Big Buck Bunny)的帧速率为25,而不是29.97。当您进行更正后,将得到更准确的帧数。 - Dipu Raj
2
@Dipu Raj,啊,你说得对,我更新了fiddle和代码。哇,这个回答距离5年还差2天,仍然在帮助人们。这让我感到温暖和愉悦 :) - 2pha

9
问题在于,setTimeout不是非常可预测的,因此您无法确保每次运行函数时都会显示完全相同的新帧。您需要在更新帧显示时每次检查视频的currentTime并将其乘以帧速率。
这里有一个可行的示例:http://jsbin.com/xarekice/1/edit 它差了一帧,但看起来你可能在开头有两个标记为“000000”的帧。
关于视频元素,您可能需要了解以下几点:
  1. 正如您所发现的那样,没有可靠的方法确定帧速率,因此您必须在其他地方发现它并硬编码它。有一些有趣的事情正在发生,例如视频指标,但它们是非标准的,不受广泛支持,并且根据我的经验,在确定实际帧速率方面完全无效。

  2. currentTime并不总是完全代表屏幕上的内容。有时它会稍微超前,特别是在寻找时(这就是为什么在我的JSBin中,在寻找时我不更新帧的原因)。

我相信currentTime在与实际视频绘制不同的线程上更新,因此它就像自己的时钟一样运作。它是视频想要的位置,而不一定是它所在的位置。因此,您可以接近,但需要四舍五入帧计算结果,并且偶尔可能会误差一帧。

谢谢,这非常有帮助。 - SayedTaqui

7

从M83开始,Chromium有一个requestVideoFrameCallback() API,可能可以解决您的问题。 您可以使用mediaTime来获取一个一致的时间戳,详见这个Github问题。从那里开始,您可以尝试像这样的代码:

var frameCounter = (time, metadata) => {
   let count = metadata.mediaTime * frameRate;
   console.log("Got frame: " + Math.round(count));

   // Capture code here.

   video.requestVideoFrameCallback(frameCounter);
}

video.requestVideoFrameCallback(frameCounter)

这只会在新的帧上触发,但你偶尔可能会错过某个帧(可以通过 metadata.presentedFrames 计数不连续来检测)。你还可能稍微晚一点捕捉到帧(通常比视频帧可用时晚16ms或调用 window.requestAnimationFrame() 后一个调用)。

如果您对API有高级概述感兴趣,这里有一篇博客文章,或者您可以查看API的官方GitHub


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