如何映射用 ffmpeg 提取的帧和视频的字幕? (帧精度问题)

How to map frame extracted with ffmpeg and subtitle of a video? (frame accuracy problem)

想为使用 ffmpeg 提取的帧生成文本文件,包含帧的字幕(如果有的话),在我也使用 ffmpeg 刻录字幕的视频上。

我使用 python 脚本和 pysrt 打开 subrip 文件并生成文本文件。 我正在做的是,每个帧都由 ffmpeg 以帧号命名,然后并且由于它们以恒定速率提取,我可以使用公式 t1 = fnum/fps 轻松检索帧的时间位置,其中 fnum 是用文件名检索的帧数,fps 是传递给 ffmpeg 以提取帧的频率。

即使我使用相同的字幕文件来检索时间轴中的文本位置,也就是视频中使用的那个文件,我仍然会出现准确性错误。大多数我有一些文本文件丢失或一些不应该存在。

因为在谈论帧时时间并不是真正连续的,我尝试使用带有硬编码字幕的视频的 fps 重新校准 t,我们称视频 fps 为 vfps(我已确保字幕刻录前后的视频 fps 相同)。我得到公式:t2 = int(t1*vfps)/vfps。 它仍然不是 100% 准确。

例如,我的视频是 30fps (vfps=30),我以 4fps (fps=4) 提取帧。 提取的第 166 帧 (fnum=166) 没有显示字幕。 subrip文件中,上一个字幕结束于t_prev=41.330,下一个字幕开始于t_next=41.400,也就是说t_sub应该满足:t_prev < t_sub and t_sub < t_next,但是我做不到这发生了。

我试过的公式:

t1 = fnum/fps  # 41.5 > t_next
t2 = int(fnum*vfps/fps)/vfps  # 41.5 > t_next
# is it because of a indexing problem? No:
t3 = (fnum-1)/fps  # 41.25 < t_prev
t4 = int((fnum-1)*vfps/fps)/vfps  # 41.23333333 < t_prev
t5 = int(fnum*vfps/fps - 1)/vfps  # 41.466666 > t_next
t6 = int((fnum-1)*vfps/fps + 1)/vfps  # 41.26666 < t_prev

使用的命令:

# burning subtitles
# (previously)
# ffmpeg -r 25 -i nosub.mp4 -vf subtitles=sub.srt withsub.mp4
# now:
ffmpeg -i nosub.mp4 -vf subtitles=sub.srt withsub.mp4
# frames extraction
ffmpeg -i withsub.mp4 -vf fps=4 extracted/%05.bmp -hide_banner

为什么会发生这种情况,我该如何解决?

我注意到的一件事是,如果我提取原始视频和字幕的帧,对帧进行差异处理,结果不仅是字幕,背景也有变化(这不应该'不会发生)。如果我用同一个视频做两次相同的体验,差异为零,这意味着帧提取是一致的。

差异代码:

ffmpeg -i withsub.mp4 -vf fps=4 extracted/%05.bmp -hide_banner
ffmpeg -i no_sub.mp4 -vf fps=4 extracted_no_sub/%05.bmp -hide_banner
for img in no_sub/*.bmp; do
    convert extracted/${img##*/} $img -compose minus -composite diff/${img##*/}
done

谢谢。

您可以提取具有准确时间戳的帧,因此

ffmpeg -i nosub.mp4 -vf subtitles=sub.srt,settb=AVTB,select='if(eq(n\,0)\,1\,floor(4*t)-floor(4*prev_t))' -vsync 0 -r 1000 -frame_pts true extracted/%08d.bmp

这将从每四分之一秒中提取第一帧。输出文件名的长度为 8 个字符,其中前 5 位数字是秒,后三位数字是毫秒。您可以根据最大文件持续时间更改字段大小。