我有一个 23.976 fps (24000/1001) 的 mkv (h264) 视频,但我想将其转换为 25fps,而无需重新编码和降低质量。我知道 mkvmerge 可以做到这一点(使用选项 --default-duration '0:25fps'),但如果可能的话,我想直接从 ffmpeg 执行此操作根据文档,这应该可行:
ffmpeg -i input.mkv -r 25 -vcodec copy output.mkv
但当我执行它时,我只得到相同的视频 fps。在 ffmpeg 中执行此操作的正确方法是什么(如果存在)?
答案1
这是使用当前版本的 FFmpeg 的方法。它依赖于 concat demuxer 不会在第一个文件之后重新缩放输入的 PTS,而只是应用固定偏移量。假设您有一个 30 fps 的流,其时间刻度为15360
(FFmpeg 输出的典型值)。这意味着帧0
具有 PTS0
并且帧30
具有 PTS 。如果我们可以将时间刻度更改为而不影响 PTS 值,15360
这将变成 45 fps 的流。23040
本质上,这就是下面的方法所做的。
1. 识别源属性。
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s,
30 fps, 30 tbr, 15360 tbn (default)
您需要注意源属性,尤其是分辨率和tbn
。
2a. (可选)将时间尺度更改为方便的尺度,使计算更简单。
ffmpeg -i in.mp4 -c copy -an -video_track_timescale 30 in-v30.mp4
这让我们
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1171 kb/s, \
30 fps, 30 tbr, 30 tbn (default
如果执行此步骤,新的时间尺度应该等于原始帧速率或为其整数倍。
2b。计算所需的时间尺度,以便对于目标帧速率x
,源中帧 # 的 PTSx
应具有与新值相同的值tbn
。如果您执行了步骤 2a,这非常简单,它只是新的帧速率。因此,对于目标 fps 45
,新的tbn
应该是45
。
3. 生成虚拟视频。
ffmpeg -f lavfi -i color=s=1280x720:r=45:d=1 -profile:v main -video_track_timescale 45 0.mp4
为了获得最佳效果,所有属性都应该相同,如分辨率、H.264 配置文件、像素格式、引用计数等。
4连接视频。
首先创建一个文本文件
file '0.mp4'
file 'in-v30.mp4'
然后,连接
ffmpeg -f concat -i list.txt -c copy -video_track_timescale 45 45fps.mp4
输出文件将以 45 fps 播放第二个视频。
5。现在,切掉假预卷
ffmpeg -ss 1.1 -i 45fps.mp4 -c copy -avoid_negative_ts make_zero in45.mp4
你有
Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1757 kb/s, \
45 fps, 45 tbr, 11520 tbn (default)
我确实说过这很复杂!
答案2
ffmpeg -itsscale 1.0427083 -i input.mp4 -codec copy output.mp4
这正确地将 Handbrake 从 PAL DVD 源创建的 25 fps mp4 减慢到 23.974 fps。原始节目是 NTSC。音频在现在 47 分钟的播放时间内始终保持同步。由于没有进行解码/编码,因此速度非常快。但是,整个过程中大约每隔 3 秒就会出现音频故障(丢失)。替换vcodec
编解码器的结果相同,只是虽然视频没有重新编码,但音频以原始比特率的一半重新编码,并且仍然有丢失故障。
ffmpeg -itsscale 1.0427083 -i input.mp4 -vcodec copy -filter:a "atempo=0.959041" output.mp4
这消除了音频丢失,但确实重新编码了音频。这比重新编码视频要快得多。剩下的缺点是它默认为原始音频比特率的一半。需要弄清楚如何设置重新编码的音频比特率。
答案3
使用-itsscale
可对输入视频进行调整,从而实现有效的帧率变化。它与 配合良好-vcodec copy
。
答案4
您可以使用比特流过滤器 来执行此操作setts
。这还避免了将原始流写入文件然后重新混合的麻烦。这样做是可行的,因为虽然您不能将普通过滤器与编解码器副本(-c:v copy
)一起使用,因为它们在解码的视频流上工作,但您可以使用在编码流上工作的比特流过滤器。
例如,要将帧速率更改为 60 fps,请插入
-bsf:v setts=ts=STARTPTS+N/TB_OUT/60
这应该设置pts
和dts
而不解码流。如果你有可变帧速率的视频,你可能需要像这样的东西
-bsf:v setts=ts='if(PREV_OUTPTS+9223372036854775808\,PREV_OUTPTS\,STARTPTS)+PREV_OUTDURATION*2'
这会修改帧的持续时间,而不是假设每个帧都有恒定的持续时间。看起来奇怪的if
表达式是为了确保第一帧偏移正确,因为在第一帧期间PREV_OUTPTS
设置为-9223372036854775808
(64 位整数的最小值),我们需要将其替换为零。
如果您有 B 帧,这可能会搞乱解码,因为dts
可能需要小于pts
,但无论如何它对我来说似乎工作正常。如果遇到问题,您可以尝试替换setts=ts
为。请参阅setts=pts
https://ffmpeg.org/ffmpeg-bitstream-filters.html#setts了解详情