英文:
HTML5 Video: How to get the index of current frame
问题
我有一个Web应用程序,使用这个 WebCodecs库逐帧处理mp4视频,并存储每个VideoFrame的呈现时间戳和持续时间。
然后,我想播放视频并将当前播放的帧与处理过的帧进行匹配。为此,我使用requestVideoFrameCallback。由于视频可能具有可变的帧速率,我不能仅仅使用currentTime / FPS
或甚至VideoFrameCallbackMetadata.mediaTime / FPS
。相反,我尝试找到具有Timestamp <= VideoFrameCallbackMetadata.mediaTime && Timestamp + Duration >= VideoFrameCallbackMetadata.mediaTime
的VideoFrame。但即使这样做也不一致,因为在某些视频上,第一帧的timestamp > 0
,但HTML5视频在视频开始时显示这一帧,当currentTime = 0
,甚至mediaTime = 0
。
是否有一种方法可以将VideoFrames与HTML视频元素中显示的帧进行匹配?我认为mediaTime
应该与VideoFrame时间戳一致,但事实并非如此。
编辑:我注意到第一个处理过的帧有时候timestamp > 0
,但运行FFProbe显示,第一帧应该有timestamp==0
。而且处理的帧数有时低于info.VideoTracks.nb_samples。所以我认为这可能是库中的错误。
编辑2:导致未处理所有帧的库错误现已修复。但是,第一帧的时间戳仍与使用FFProbe提取的时间戳不同ffprobe file.mp4 -select_streams v -show_entries frame=coded_picture_number,pkt_pts_time -of csv=p=0:nk=1 -v 0
。我将尝试将FFProbe输出与MP4Box命令行进行比较,看看时间戳差异出现在何处。另一个可能的原因是视频元素本身以不同的时间戳渲染帧。
编辑3:即使使用来自FFProbe的帧时间戳,帧时间仍然与视频元素不同步。
英文:
I have a web application that processes mp4 video frame-by-frame using this WebCodecs library and stores the presentation timestamp and duration of every VideoFrame.
Then i want to play the video and match the currently playing frame with the processed frames. For this i use the requestVideoFrameCallback. Since the video can have variable framerate, I cannot just use currentTime / FPS
or even VideoFrameCallbackMetadata.mediaTime / FPS
. Instead I try to find the VideoFrame that has Timestamp <= VideoFrameCallbackMetadata.mediaTime && Timestamp + Duration >= VideoFrameCallbackMetadata.mediaTime
. But even this is not consistent because on some videos the first frame has timestamp > 0
but the html5 video displays this frame on the start of the video when currentTime = 0
and even mediaTime = 0
.
Is there a way to match the VideoFrames to the frame that is displayed in the html video element? I thought the mediaTime
should be consistent with the VideoFrame timestamp but it is not.
EDIT: I noticed that the first processed frame sometimes has timestamp > 0
but running FFProbe shows that the first frame should have timestamp==0
. Also The number of frames that are processed is sometimes lower that the info.VideoTracks.nb_samples. So I think this is probably error in the library.
EDIT2: The bug in the library that caused not all frames to be processed is now fixed. However, The timestamp of the first frame still differs from the timestamp extracted using FFProbe ffprobe file.mp4 -select_streams v -show_entries frame=coded_picture_number,pkt_pts_time -of csv=p=0:nk=1 -v 0
. I'm going to try to compare FFProbe output to MP4Box command line to see where does the difference in timestamps occur. Another culrpit could be the video element itself rendering the frames with different timestamps.
EDIT3: Even when using the frame timestamps from FFProbe, the frame times are still not in sync with the video element.
答案1
得分: 1
从我的经验来看,requestVideoFrame 不够精确到帧级别。我认为原因是您无法控制视频元素如何操作底层编解码器。为了精确到帧级别工作,我自己操作了编解码器,不过我的目标不同(我构建了一个视频编辑器)。在您的情况下,可以在转向编解码器之前尝试使用 MediaStreamTrackProcessor API https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrackProcessor。在我的情况下,它比 requestVideoFrame 更准确,但仍然不够精确。在您的情况下,它可能会起作用:
const videoTrack = document.querySelector("video").videoTracks[0]; //原始视频
const trackProcessor = new MediaStreamTrackProcessor({ track: videoTrack }); //输入视频轨道
const trackGenerator = new MediaStreamTrackGenerator({ kind: "video" }); //输出视频轨道
const reader = trackProcessor.reader //从输入视频轨道获取流
const transformer = new TransformStream({
async transform(videoFrame, controller) {
const newFrame = myFunction(videoFrame, processedFrame) //在这里,您将生成一个新的视频帧,其中包含旧的处理过的帧与原始帧叠加或并排显示。
videoFrame.close();
controller.enqueue(newFrame);
},
});
trackProcessor.readable
.pipeThrough(transformer)
.pipeTo(trackGenerator.writable);
之后,您将不得不将输出轨道附加到不同的视频元素上,该元素将播放新的帧。
英文:
From my experience, requestVideoFrame is not precise to the frame level. The reason for that, I believe, is that you don't have control over the way the video element manipulates the underlying codecs. What I did to work precisely to the frame level was to manipulate the codecs myself, however, my goal was different (I built a video Editor). In your case, before going to the codecs themselfs, you can try to use the MediaStreamTrackProcessor API https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrackProcessor. In my case it was more accurate then requestVideoFrame, but still not accurate enough. In your case It might do the trick:
const videoTracks = document.querySelector("video").videoTracks[0]; //originl video
const trackProcessor = new MediaStreamTrackProcessor({ track: videoTrack }); //input videoTrack
const trackGenerator = new MediaStreamTrackGenerator({ kind: "video" }); //output videoTrack
const reader = trackProcessor.reader //get stream from input videoTrack
const transformer = new TransformStream({
async transform(videoFrame, controller) {
const newFrame = myFunction(videoFrame, processedFrame) //here you'll generate a new videoFrame with the old processed Frame overlaid or side-by-side with the original frame.
videoFrame.close();
controller.enqueue(newFrame);
},
});
trackProcessor.readable
.pipeThrough(transformer)
.pipeTo(trackGenerator.writable);
After that, you'll have to attach the output track to a differente video element, which will play the new frames.
答案2
得分: 0
最后,我通过将所有的帧时间戳向后偏移,以使第一帧从0:00开始(mp4box有时会输出大于0的时间戳,而ffmpeg不会)。这已经很接近了,但有些帧仍然会渲染在前一帧之后1帧。因此,当时间戳非常接近下一帧(大约5微秒)时,我会渲染下一帧。
虽然有些繁琐,但这似乎效果相当不错。
英文:
In the end I solved it by shifting all of the frame timestamps so that the first frame starts at 0:00 (mp4box sometimes outputs timestamp > 0 for the first frame, ffmpeg does not). This was close, but some frames were still rendering 1 frame behind. So when a timestamp is very close to next frame (about 5 microseconds) I render the next frame instead.
Although cumbersome, this seems to work pretty well.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论