如何在 FFmpeg 中生成帧精确的 concat 文件图像序列叠加?

如何在 FFmpeg 中生成帧精确的 concat 文件图像序列叠加?

我正在使用concat.txt如下文件:

ffconcat version 1.0
file 000001.png #13-24 0.433767-0.834167
duration 0.4004
file 000002.png #25-35 0.834167-1.2012
duration 0.367033
file 000003.png
...

以及以下命令行:

ffmpeg -i input.VOB -itsoffset 0.433767 -i concat.txt -filter_complex "[0]overlay=eof_action=pass,showinfo;[1]showinfo" -c:a copy output.VOB

这是使用 concat 文件的图像序列叠加。

这是我的 FFmpeg 版本:

ffmpeg version 4.4.1 Copyright (c) 2000-2021 the FFmpeg developers

我遇到的问题是我希望覆盖具有帧精度:第一张图像应覆盖第 13-24 帧,第二张图像应覆盖第 25-35 帧,等等。

我有每一帧的精确信息pts_time(来自 showinfo),并相应地设置了持续时间,但无法让覆盖在所需的帧开始/结束(通过调整持续时间,我只能管理一帧太早,或者一帧太晚)。

showinfo 输出给出了提示:

[Parsed_showinfo_1 @ 0x564719ed1600] config in time_base: 1/90000, frame_rate: 30000/1001
[Parsed_showinfo_1 @ 0x564719ed1600] config out time_base: 0/0, frame_rate: 0/0
[Parsed_showinfo_2 @ 0x564719ed1dc0] config in time_base: 1/25, frame_rate: 25/1
[Parsed_showinfo_2 @ 0x564719ed1dc0] config out time_base: 0/0, frame_rate: 0/0
[Parsed_showinfo_2 @ 0x564719ed1dc0] n:   0 pts:     11 pts_time:0.44    pos:        0 fmt:rgb24 sar:8/9 s:720x480 i:P iskey:1 type:I checksum:F8CA2959 plane_checksum:[F8CA2959] mean:[63] stdev:[35.9]

视频(input.VOB)为 29.97fps 流,而叠加层设置为默认 25fps

由于持续时间是手动设置的,我认为这frame_rate不是实际问题,而是time_base旁边是:1/25。叠加流中的第一幅图像的开始时间 (0.433767) 四舍五入到最接近的 1/25 (0.44),晚了一帧。其他图像也出现了同样的情况:

[Parsed_showinfo_1 @ 0x55c01673f600] n:  24 pts:  72072 pts_time:0.8008  pos:   301070 fmt:yuv420p sar:8/9 s:720x480 i:P iskey:1 type:I checksum:44EFD0BE plane_checksum:[17B5BDB9 3C4A8674 83098C82] mean:[67 138 118] stdev:[26.4 5.6 3.1]
[Parsed_showinfo_1 @ 0x55c01673f600] n:  25 pts:  75075 pts_time:0.834167 pos:   313358 fmt:yuv420p sar:8/9 s:720x480 i:P iskey:0 type:P checksum:44EFD0BE plane_checksum:[17B5BDB9 3C4A8674 83098C82] mean:[67 138 118] stdev:[26.4 5.6 3.1]
[Parsed_showinfo_2 @ 0x55c01673f600] n:   1 pts:     21 pts_time:0.84    pos:        0 fmt:rgb24 sar:8/9 s:720x480 i:P iskey:1 type:I checksum:C47D9BDA plane_checksum:[C47D9BDA] mean:[63] stdev:[36.3]
[Parsed_showinfo_1 @ 0x55c01673f600] n:  26 pts:  78078 pts_time:0.867533 pos:   315406 fmt:yuv420p sar:8/9 s:720x480 i:P iskey:0 type:P checksum:29E90C18 plane_checksum:[A845EC05 E0C99215 FFD58DE0] mean:[67 138 118] stdev:[26.6 5.8 3.3]
...
[Parsed_showinfo_1 @ 0x55c01673f600] n:  35 pts: 105105 pts_time:1.16783 pos:   354318 fmt:yuv420p sar:8/9 s:720x480 i:P iskey:0 type:P checksum:29E90C18 plane_checksum:[A845EC05 E0C99215 FFD58DE0] mean:[67 138 118] stdev:[26.6 5.8 3.3]
[Parsed_showinfo_2 @ 0x55c01673f600] n:   2 pts:     30 pts_time:1.2     pos:        0 fmt:rgb24 sar:8/9 s:720x480 i:P iskey:1 type:I checksum:7A31E90F plane_checksum:[7A31E90F] mean:[64] stdev:[36.7]
[Parsed_showinfo_1 @ 0x55c01673f600] n:  36 pts: 108108 pts_time:1.2012  pos:   358414 fmt:yuv420p sar:8/9 s:720x480 i:P iskey:1 type:I checksum:6A1DCE13 plane_checksum:[DC94840F 6D0ED2E5 F82E7710] mean:[69 138 118] stdev:[27.0 5.8 3.3]

四舍五入到最接近的 1/25。我尝试添加[1]settb=expr=1/90000,这导致 time_base 与输入视频匹配,如预期的那样,但对pts_time覆盖的粒度没有影响(但请注意pts从 11 变为 39600 的方式):

[Parsed_showinfo_1 @ 0x55ffb8a06940] config in time_base: 1/90000, frame_rate: 30000/1001
[Parsed_showinfo_1 @ 0x55ffb8a06940] config out time_base: 0/0, frame_rate: 0/0
[Parsed_showinfo_3 @ 0x55ffb8a07780] config in time_base: 1/90000, frame_rate: 25/1
[Parsed_showinfo_3 @ 0x55ffb8a07780] config out time_base: 0/0, frame_rate: 0/0
[Parsed_showinfo_3 @ 0x55ffb8a07780] n:   0 pts:  39600 pts_time:0.44    pos:        0 fmt:rgb24 sar:8/9 s:720x480 i:P iskey:1 type:I checksum:F8CA2959 plane_checksum:[F8CA2959] mean:[63] stdev:[35.9]

后续图片也是同样的情况。不知道接下来该怎么做。

使用连接文件在图像叠加中可以获得大于 1/25 秒的粒度吗?

答案1

有一个方法可以解决此限制。

问题是什么?

正如所提到的一个答案由 ffmpeg 的一位开发人员编写,concat 图像序列帧速率是硬编码的:

... 每幅图像的默认帧速率为 25 fps。目前无法更改。

我通过运行确认了这一点:

ffmpeg -i concat.txt -vf fps=250,showinfo concat.mkv

即使输出中可用的粒度为 1/250,帧持续时间也会四舍五入到最接近的 1/25=0.04 秒:

[Parsed_showinfo_1 @ 0xd00b60] config in time_base: 1/250, frame_rate: 250/1

有什么解决方法?

可以将连接文件的持续时间乘以某个将粒度保持在 25fps 以上的因子,然后在生成覆盖时将相同的因子除以 4。

一个方便使用的因素是时间尺度。将时间戳乘以时间刻度,我们得到...它们各自的pts值,所以我们可以直接使用这些值。由于pts值是整数,因此也可以安全地将它们除以 25。使用上面的示例 concat 文件,我们得到:

ffconcat version 1.0
file 000001.png #13-24 0.433767-0.834167
duration 1441.44 # 0.4004 * 90000 = 36036 (pts) /25
file 000002.png #25-35 0.834167-1.2012
duration 1321.32 # 0.367033 * 90000 = 33033 (pts) /25
file 000003.png #36
...

然后覆盖命令将是:

ffmpeg -i input.VOB -i concat.txt -filter_complex "[1]settb=1/90000,setpts=39039+PTS*25/90000[o];[0:v:0][o]overlay=eof_action=pass" -c:a copy -vsync passthrough output.VOB

请注意,-itsoffset已经移至过滤器(0.433767s pts=39039),然后通过分解时间尺度以及我们之前除以的额外 25 来提取精确的时间戳。

相关内容