Javascript:帧精确视频停止

tob*_*ora 8 javascript video html5-video webassembly webcodecs

我希望能够在视频到达某些指定帧时稳健地停止视频,以便根据使用 Blender、Manim 制作的视频进行口头演示...

我知道这个问题,但问题是视频没有完全停在好帧处。有时它会继续前进一帧,当我强制它返回到初始帧时,我们会看到视频向后退,这很奇怪。更糟糕的是,如果下一帧完全不同(不同的背景......),这将非常明显。

为了说明我的问题,我在这里创建了一个演示项目(只需单击“下一步”即可看到当视频停止时,有时会倒退)。完整的代码在这里

我正在使用的代码的重要部分是:

      var video = VideoFrame({
          id: 'video',
          frameRate: 24,
          callback: function(curr_frame) {
              // Stops the video when arriving on a frames to stop at.
              if (stopFrames.includes(curr_frame)) {
                  console.log("Automatic stop: found stop frame.");
                  pauseMyVideo();
                  // Ensure we are on the proper frame.
                  video.seekTo({frame: curr_frame});
              }
          }
      });
Run Code Online (Sandbox Code Playgroud)

到目前为止,我通过在结束前停止一帧然后使用seekTo(不确定这听起来如何)来避免这个问题,如此处所示。但正如您所看到的,有时当进入下一帧时,它会有点“冻结”:我想这是在seekTo.

PS:如果你知道 JS 中一种可靠的方法来了解给定视频的帧数,我也很感兴趣。

关于在桌面上预先剪切视频的想法,可以使用......但我过去在这方面有过不好的经验,特别是更改视频有时会产生一些故障。此外,它的使用可能会更复杂,这意味着视频应该手动剪切很多时间,重新编码......

编辑是否有任何解决方案,例如基于WebAssembly(与旧浏览器更兼容)或Webcodec(更高效,但尚未广泛传播)?Webcodec 似乎允许非常令人惊奇的事情,但我不知道如何使用它们。我很想听到基于它们的解决方案,因为 Firefox 尚不处理 webcodec。请注意,如果在此过程中音频不会丢失,那就太好了。如果我还可以根据要求显示控件,那就太好了。

编辑:我不确定这里发生了什么(源代码)...但它似乎做了一些接近我的需要的事情(我认为使用 WebAssembly),因为它设法在画布中播放视频,带有框架...这是另一个使用 Webcodec 做一些接近我需要的事情的网站。但我不确定如何使用网络编解码器可靠地同步声音和视频。

编辑:回答第一个问题

关于视频帧,确实我选择的帧速率很差,它是 25,而不是 24。但即使使用 25 的帧速率,我仍然无法在 Firefox 和 Chromium 上获得帧精确停止。例如,这是您的演示的录音(使用 OBS)(当我使用 25 而不是 24 时,我看到的与我的演示相同):

在此输入图像描述

一帧后,看到黄油“向后飞”(这在静态屏幕截图中可能不是很明显,但例如看看花中左下翼的位置):

在此输入图像描述

我可以看到三个潜在的原因:第一(我认为这是最有可能的原因),我听说并不video.currentTime总是准确地报告时间,也许它可以解释为什么这里失败?为了改变当前帧似乎相当准确(据我所知,我可以非常可靠地前进和后退一帧),但是人们在这里报告说这video.currentTime是使用音频时间而不是视频时间计算的Chromium,导致一些不一致(我在 Firefox 中观察到类似的不一致),这里它可能会导致帧发送到合成器的时间或帧实际在合成器中打印的时间(如果它是最新的) ,这可以解释我们有时会遇到的延迟)。这也可以解释为什么requestAnimationVideoFrame更好,因为它还提供了当前的媒体时间。

可以解释该问题的第二个原因是,setInterval可能不够精确......但是requestAnimationFrame并不是真的更好(requestAnimationVideoFrame在 Firefox 中不可用),而它应该每秒触发 60 次,这应该足够快。

我可以看到的第三个选项是,也许该.pause函数需要很长时间才能触发......并且在通话结束时视频还会播放另一帧。另一方面,您使用 requestAnimationVideoFrame https://mvyom.csb.app/requestFrame.html的示例似乎工作得非常可靠,并且它正在使用.pause! 不幸的是它只适用于 Chromium,但不适用于 Firefox。我看到你使用metadata.mediaTime而不是currentTime,也许这比当前时间更精确。

最后一个选项是,可能有一些关于垂直同步的微妙之处,如本页所述。它还报告说,expectedDisplayTime使用时可能有助于解决这个问题requestAnimationVideoFrame

onk*_*kar 5

该视频的帧率为 25fps,而不是 24fps: 在此输入图像描述

输入正确的值后,它就可以正常工作了:demo
VideoFrame api 严重依赖于您提供的 FPS。您可以离线查找视频的 FPS,并将其作为元数据与停止帧一起从服务器发送。


videoplayer.handmadeproducts.de网站使用window.requestAnimationFrame()来获取回调。


requestAnimationFrame 有一个更好的新替代方案。requestVideoFrameCallback ()允许我们对视频进行每个视频帧的操作。
同样的功能,在 OP 中是圆顶的,可以这样实现:

   const callback = (now, metadata) => {
      if (startTime == 0) {
        startTime = now;
      }
      elapsed = metadata.mediaTime;
      currentFrame = metadata.presentedFrames - doneCount;

      fps = (currentFrame / elapsed).toFixed(3);
      fps = !isFinite(fps) ? 0 : fps;

      updateStats();
      if (stopFrames.includes(currentFrame)) {
        pauseMyVideo();
      } else {
        video.requestVideoFrameCallback(callback);
      }
   };
   video.requestVideoFrameCallback(callback);
Run Code Online (Sandbox Code Playgroud)

这是演示的样子。
该 API 适用于基于 chromium 的浏览器,例如 Chrome、Edge、Brave 等。


有一个 JS 库,它可以从视频二进制文件中查找帧速率,名为mediainfo.js