如何使用FFmpeg在给定时间戳之前获取最近关键帧的时间戳?

jac*_*ode 22 ffmpeg

我想要一个快速准确的 FFmpeg 搜索命令。我找到了这个

解决方案是我们同时申请-ss输入(快速搜索)和输出(精确搜索)。但是:如果输入的搜索不准确,我们如何确定搜索位置是准确的?


例如:如果我们要搜索到 00:03:00,命令是这样的:

ffmpeg -ss 00:02:30 -i <INPUT> ... -ss 00:00:30 <OUTPUT>
Run Code Online (Sandbox Code Playgroud)

第一个-ss将寻求其他地方,而不是00:02:30,比如说00:02:31。在应用第二次搜索之后,最终结果将是00:03:01-不是我们想要的。那是对的吗?

第一个-ss寻求到哪里去?它是否寻找最接近 的关键帧00:02:30

如果是这样,这就是我的想法——如果我错了,请纠正我:在第一次寻找之后,我们得到结果的时间戳(在这个例子中:)00:02:31,然后我们在适当的时间应用第二次寻找,在这种情况下00:00:29

问题是:我们如何获得第一个搜索结果的时间戳?

slh*_*hck 21

从字面上回答您的标题问题:您可以获得 I 帧列表

ffprobe -select_streams v -show_frames <INPUT> 
Run Code Online (Sandbox Code Playgroud)

您可以通过添加-show_entries frame=pkt_pts_time,pict_type.

要查看哪个帧最接近(紧随其后)某个时间戳,您首先需要找出关键帧的所有时间戳,例如使用awk.

首先,定义您要查找的时间,例如,2:30m 等于 150s。

ffprobe -select_streams v -show_frames \
        -show_entries frame=pkt_pts_time,pict_type -v quiet input.mp4 |
awk -F= '/pict_type=/ { if (index($2, "I")) { i=1; } else { i=0; } }
         /pkt_pts_time/ { if (i && ($2 >= 150)) print $2; }
        ' |
head -n 1
Run Code Online (Sandbox Code Playgroud)

例如,这将返回150.400000.


请注意,当使用-ssbefore 时-i,FFmpeg 将定位查找点之前的关键帧,然后为所有后续帧分配负 PTS 值,直到它到达查找点。播放器应该解码但不显示带有负 PTS 的帧,并且视频应该正确启动。

一些玩家没有正确尊重这一点,会显示黑色视频或垃圾。在这种情况下,上述脚本可用于您的搜索点之后找到关键帧的 PTS ,并使用它从关键帧开始搜索。然而,这将是不准确的。

请注意,如果您想在搜索时保持超级准确——并保持与许多播放器的兼容性——您可能应该将视频转换为任何无损、仅帧内格式,您可以随时剪切,然后重新编码。但这不会很快。

  • 你很接近,`select_streams` 选项是 [2012 年 10 月添加的](http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=3d189d41c10ed710a13135ba11a0859e6b7e4faf)。:) 你可以不用它,但是你也会得到音频帧的信息,混合在两者之间。 (2认同)
  • 请注意,您可以将其添加到 ffmpeg 行以使其仅输出必要的 2 个字段,而不是 awk 丢弃的大量内容:-show_entries frame=pkt_pts_time,pict_type (2认同)

Hin*_*d-D 12

我知道这个问题已经有好几年了,但最新版本的 ffprobe 具有跳过帧的能力。您只能传入-skip_frame nokey以报告关键帧(I 帧)的信息。这可以为您节省很多时间!在一个 2GB 的 1080p MP4 文件上,它过去需要 4 分钟,而没有跳帧。添加跳过参数只需要 20 秒。

命令:

ffprobe -select_streams v -skip_frame nokey -show_frames \
        -show_entries frame=pkt_pts_time,pict_type test.mp4
Run Code Online (Sandbox Code Playgroud)

结果:

[FRAME]
pkt_pts_time=0.000000
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=3.753750
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=7.507500
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=11.261250
pict_type=I
[/FRAME]
[FRAME]
pkt_pts_time=15.015000
pict_type=I
[/FRAME]
Run Code Online (Sandbox Code Playgroud)

所以结果将只包含有关关键帧的信息。