当我有一个 HTML5 视频对象时,我可以设置其
currentTime
属性来跳转(寻找)到特定的时间位置。
还有一种方法可以跳转或寻找特定的帧编号吗?
或相关:有没有办法让暂停的视频步进或跳到下一帧或上一帧?
我的意思是,当它当前暂停或冻结在某个任意位置时,导致视频跳转到后一帧或前一帧。
正如评论中提到的,据我所知,除了提到的“seekToNextFrame”之外,没有定义标准来支持 HTML5 视频标签本身的帧精确搜索,该标准并未得到广泛支持,如下所示:https:// developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/seekToNextFrame
有商业 Javascript HTMl5 播放器提供此功能 - 例如:
您还可以在开源 Shaka 玩家讨论组中找到有关此主题的讨论。
它并不简单的原因是它通常需要播放器或浏览器(或客户端)首先找到最近的“I”帧,然后从中向前和/或向后解码以到达您想要的确切帧想。正如上面的示例实现所证明的那样,这在技术上当然是可行的,但我猜想浏览器或开源玩家社区中还没有优先考虑此功能的需求。
我有两种处理需要预扫描整个视频的帧的解决方案。
设置:
let video = document.getElementById("my-video")
视频是HTMLMediaElement
我们的工具包:
video.play()
和 video.pause()
video.currentTime
(以秒为单位)是我们视频的位置以及更改它的最佳方法。 (还有)。video.duration
(以秒为单位)是视频的总长度。video.playbackRate
默认1.0,最大16.0(至少对我来说)。video.getVideoPlaybackQuality()
给出 totalVideoFrames
、droppedVideoFrames
和 corruptedVideoFrames
let vfcId = video.requestVideoFrameCallback((now,metadata)=>{})
和 video.cancelVideoFrameCallback(vfcId)
在显示框架时调用该函数。video.onended = (event) => {}
(本质上是视频结束时的事件侦听器。)其他无用的工具:
video.fastSeek()
比 currentTime
准确但速度更快。video.muted = true
可以避免被烦人video.ontimeupdate
/ "timeupdate"
事件。无用,因为仅每隔几帧激活一次,这还不够好。策略:找到帧速率并用它来计算帧时间。
video.getVideoPlaybackQuality().totalVideoFrames
为我们提供了视频中迄今为止播放的帧总数,因此我们不能在视频中跳来跳去,而必须完整观看。尽管如此,totalVideoFrames / video.duration
仍将为我们提供帧速率。
let framerate = None;
function getFrameRate() {
video.playbackRate = 16;
video.currentTime = 0;
video.play();
video.onended = (event) => {
framerate = video.getVideoPlaybackQuality().totalVideoFrames / video.currentTime;
video.onended = (event) => (None); //could be cleaner
};
}
function timeForFrame(n) {
return Math.round(n/framerate *1000)/1000;
}
function goToFrame(n=currentFrame) {
video.currentTime = timeForFrame(n);
}
请注意,我将 playbackRate
设置为 x16 以使扫描速度更快。这使得视频播放器丢帧,但是,它们仍然按
totalVideoFrames
计算。
优点:x16播放速度相对较快,并且可以将预扫描变成加载预览。对于长视频,您可以使用超时来估计前几秒的帧速率。
缺点:如果视频帧速率随时间变化,此方法可能不准确。
解决方案 2:VideoFrameCallback(质量好但速度很慢)frame_times
的列表 (
currentTime
)。
VideoFrameCallback
回调每个显示的帧,其中包含大量时间元数据。
metadata.mediaTime
是框架的
currentTime
。注意:
requestVideoFrameCallback
不是事件监听器,您需要在每一帧中一次又一次地请求它。
let frame_times = [];
let vfcId = 0;
function getFrametimes() {
video.playbackRate = 1; // will start dropping frames if higher.
video.currentTime = 0;
video.requestVideoFrameCallback(frameTimePusher);
video.play();
video.onended = {
video.cancelVideoFrameCallback(vfcId);
video.onended = (event) => (None);
}
}
function frameTimePusher(now,metadata) {
frame_times.push(metadata.mediaTime); //mediaTime is currentTime
video.requestVideoFrameCallback(frameTimePusher); // !! you need to request again.
}
function goToFrame(n) {
video.currentTime = frame_times[n];
}
请注意,playbackRate
设置为 x1。这是因为如果设置得更高,视频将开始丢帧,并且 VideoFrameCallback 仅针对非丢帧激活。
优点:非常准确(如果以 x1 播放速率运行)
缺点:慢(如果您以 x1 播放速率运行)
解决方案 1 可以说更好。