我有一个 ffmpeg 命令在输入视频上设置自定义点。
它适用于 .mkv 文件,但不适用于 .mp4 文件。当对 mp4 文件运行以下命令时,大多数帧都会被丢弃。虽然我的pts是单调的并且一直在增加。
ffmpeg -i video.mp4 -filter_complex "[v]setpts='0*eq(N,0)+160*eq(N,1)+191*eq(N,2)+211*eq(N,3)...'[out];" -map [out] out_video.mp4
是否可以对 mp4 文件进行设置?
谢谢!
我们可以添加
-fps_mode passthrough
,显式设置 MP4 时基,并将 PTS 除以时基:ffmpeg -y -i input.mp4 -fps_mode passthrough -filter_complex "[v]setpts='(0*eq(N,0)+160*eq(N,1)+191*eq(N,2)+211*eq(N,3))/TB/1000'[out]" -video_track_timescale 100000 -map [out] out_video.mp4
-fps_mode passthrough
- 每个帧及其时间戳从解复用器传递到复用器(防止帧重复和丢失)。/TB
- 除以时基用于将 PTS 转换为秒。/TB/1000
- 用于将 PTS 转换为毫秒(假设数字适用于毫秒)。-video_track_timescale 100000
- 按此处所述为 MP4 设置显式时基。在大多数情况下,这不是必需的,但在某些情况下,由于时基精度较低,时间戳可能会被舍入。
测试:
创建示例输入视频进行测试:
ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=100:duration=0.04 input.mp4
使用过滤器执行命令
setpts
:ffmpeg -y -i input.mp4 -fps_mode passthrough -filter_complex "[v]setpts='(0*eq(N,0)+160*eq(N,1)+191*eq(N,2)+211*eq(N,3))/TB/1000'[out]" -video_track_timescale 100000 -map [out] out_video.mp4
out_video.mp4
使用FFprobe查看PTS时间戳:ffprobe -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries frame=pts_time out_video.mp4
输出:
有一个“陷阱”:
如果输入帧的“帧周期”太长,上述解决方案将不起作用。
命令:
ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=100:duration=0.04 input.mp4
,以 100fps (100Hz) 生成视频。“帧周期”为 1/100 = 10 毫秒。
例如,当创建 1Hz 视频时:
ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1:duration=4 input_1fps.mp4
,“帧周期”为 1 秒。input_1fps.mp4
我们可以检查使用FFprobe的“帧周期” :ffprobe -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries packet=duration_time input_1fps.mp4
输出:
现在,
setpts
使用 1fps 视频执行:ffmpeg -y -i input_1fps.mp4 -fps_mode passthrough -filter_complex "[v]setpts='(0*eq(N,0)+160*eq(N,1)+191*eq(N,2)+211*eq(N,3))/TB/1000'[out]" -video_track_timescale 100000 -map [out] out_video.mp4
和 FFprobe:
ffprobe -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries frame=pts_time out_video.mp4
。输出不符合预期:
为什么不起作用???
setpts
设置 PTS 时间戳,但不修改“帧周期”。为什么
setpts
不修改“帧周期”?原因是“帧周期”是编码流的一部分(例如 H.264),而时间戳是容器的一部分(例如 MP4)。
如何修改“帧周期”?
我们可以使用比特流过滤器来修改“帧周期”。
注意:有设置过滤器,但看起来不允许修改“帧周期”......
看起来最简单的解决方案是将帧转换为 PNG 图像序列:
ffmpeg -i input_1fps.mp4 frame_%08d.png
使用过滤器转换为 MP4
setpts
:ffmpeg -y -framerate 1000 -i frame_%08d.png -fps_mode passthrough -filter_complex "[v]setpts='(0*eq(N,0)+160*eq(N,1)+191*eq(N,2)+211*eq(N,3))/TB/1000'[out]" -map [out] out_video.mp4
检查:
ffprobe -select_streams v -of default=noprint_wrappers=1:nokey=1 -show_entries frame=pts_time out_video.mp4
输出: