使用ffmpeg改变帧率

127

我想将一个视频剪辑(MP4,yuv420p)从30帧每秒转换为24帧每秒。帧数正确,因此我的输出应该从30 fps的20分钟变为24fps的25分钟。其他所有内容都应保持不变。

无论我尝试什么ffmpeg命令,都无法成功将帧速率转换而同时保持相同的持续时间,要么改变持续时间而不改变帧速率,要么改变帧速率而改变帧数。

因此,我一直在尝试诸如:

ffmpeg -y -r 30 -i seeing_noaudio.mp4 -r 24 seeing.mp4

(我是在Windows上进行此操作,但通常会在Linux上进行)。这会转换帧速率,但会丢失一些帧,因此总持续时间不会改变。

或者我尝试过

ffmpeg -y -i seeing_noaudio.mp4 -filter:v "setpts=1.25*PTS" seeing.mp4

更改视频的持续时间而不更改帧率。

我应该能够通过一个单独的FFmpeg命令完成这个操作,而无需重新编码甚至像一些人建议的那样返回原始帧。

请帮忙。


4
-y 标志表示“无需询问即覆盖输出文件”。我不得不去查找它的含义,以确保我没有误解什么。我认为这与手头的问题无关,因此应该删除以简化每个人的事情。 - osullic
对于 -r,文档中说:“作为输入选项,忽略文件中存储的任何时间戳,并生成假定恒定帧速率 fps 的时间戳。作为输出选项,复制或删除输入帧以实现恒定的输出帧速率 fps。” - osullic
对我来说,只有最后一个回答在2023年有效https://dev59.com/WVcO5IYBdhLWcg3wpjMz#68973714。太长不看,它使用`-itsscale`输入选项来重新调整输入时间戳。由于某种原因,几个回答提到的`-r`选项对我无效(`ffmpeg版本4.4.2`),其中一条点赞的评论也提到了这一点顺便说一下。 - Alex Martian
7个回答

116

重新编码后:

ffmpeg -y -i seeing_noaudio.mp4 -vf "setpts=1.25*PTS" -r 24 seeing.mp4

无需重新编码:

第一步 - 提取视频到原始比特流

ffmpeg -y -i seeing_noaudio.mp4 -c copy -f h264 seeing_noaudio.h264

使用新的帧率进行混流

ffmpeg -y -r 24 -i seeing_noaudio.h264 -c copy seeing.mp4

1
谢谢。第一个可行,但第二个不行,返回了一条错误信息,大致意思是“无法为输出文件#0编写头部(不正确的编解码器参数?):无效参数”。我不知道这是什么意思,但第一个可行。 - J Brand
1
通常使用-r选项。当您需要在应用进一步过滤器之前更改帧速率时,请使用该过滤器。 - Gyan
3
关于 setpts 的注意事项:若要将视频加速一倍,则使用 setpts=0.5*PTS。若要减慢视频,则需使用大于 1 的乘数:setpts=2.0*PTS(即减半速度)。参考链接 - lepe
4
这取决于输入帧率。PTS系数的基本公式是(旧帧率)/(新帧率)-r 就是新的帧率。23.97629.9759.94的确切值分别为24000/10013000/100160000/1001 - Gyan
3
如果你遇到了那个错误,就需要升级 ffmpeg。自 2016 年起,那个标识已经不再需要了。 - Gyan
显示剩余11条评论

90

您可以考虑使用fps过滤器。它不会改变视频播放速度:

ffmpeg -i <input> -filter:v fps=fps=30 <output>

在将FPS从59.6降至30方面起到了良好的作用。


6
这可以保持音频与视频同步,谢谢。 - PythonProgrammi
2
顺便说一下,原来你可以使用这个命令将OBS录制的大小减少约2倍,而不会牺牲质量或流畅性(如果使用60fps,请更改为fps = fps = 60)。 所有其他命令,包括使用x264 / nvenc以最佳质量重新编码,都会产生可见的伪影。 - leavittx
5
为什么要使用"fps=fps"这样的方式来指定帧率?感谢。 - ch271828n
4
@ch271828n,我是从示例中获取的。基本上,第一个“fps”是过滤器的名称,第二个“fps”是输入参数,看起来你可以跳过它。 - kelin
2
ffmpeg -i <输入视频> -filter:v fps=15 <输出视频> - Coreus
显示剩余2条评论

34

只需在输入文件之前的“-r”选项中指定所需帧速率:

ffmpeg -y -r 24 -i seeing_noaudio.mp4 seeing.mp4

选项会影响其之后的文件。在输入文件之前加上"-r",将强制重新解释文件头,就好像视频是以给定帧速率编码的一样。不需要重新压缩。曾经有一个小型工具avifrate.exe,可以直接修补avi文件头以更改帧速率。上面的ffmpeg命令本质上也是这样做的,但必须复制整个文件。


两个答案都不适用于我使用 VP8 WebM 文件(我正在尝试将其从 25 fps 减速到 6fps 而无需重新编码)。输出视频与输入相同。 - 7vujy0f0hy
14
对我来说,这不会改变输出文件的帧率。 - Elder Geek
这对我来说可以降低帧率,但会使音频失步。 - Ventolinmono
这会改变电影的速度,在我的情况下。 - Dev.Jaap
我已经成功地创建了一个长度为60秒、帧速率为1的虚拟视频文件,使用的是一张图片,命令如下:ffmpeg -loop 1 -r 1 -i image.jpeg -c:v libx264 -t 60 -pix_fmt yuv420p -vf scale=320:240 tmp.mp4。关键在于在图像之前使用 -r 选项,因为在之后使用它时无法正常工作。 - haridsv
显示剩余2条评论

5
据我所知,使用ffmpeg无法在不重新编码的情况下完成此操作。我有一个24fps的文件,我想要将其转换为25fps以匹配其他一些我正在处理的材料。我使用了命令ffmpeg -i inputfile -r 25 outputfile,对于webm、matroska输入,可以完美地工作,并生成了h264、matroska输出,使用的编码器是Lavc56.60.100。
您也可以以6fps的速度完成相同的事情,但是如您所指出的,持续时间不会改变(在大多数情况下这是件好事,否则您将失去音频同步)。如果这不符合您的要求,建议您尝试这个答案,虽然我的经验是它仍会重新编码输出文件。
为了获得最佳的帧精度,最好像之前建议的那样解码为原始流。我使用以下脚本来实现:
#!/bin/bash
#This script will decompress all files in the current directory, video to huffyuv and audio to PCM
#unsigned 8-bit and place the output #in an avi container to ease frame accurate editing.
for f in *
do
ffmpeg -i "$f" -c:v huffyuv -c:a pcm_u8 "$f".avi
done

显然,该脚本期望当前目录中的所有文件都是媒体文件,但可以轻松更改以限制处理到您选择的特定扩展名。请注意,当您解压缩为原始流时,文件大小将会增加相当大的因素。


4

通常情况下,要将视频的FPS设置为24,你几乎总是可以执行以下操作:

带音频而无需重新编码:

# Extract video stream
ffmpeg -y -i input_video.mp4 -c copy -f h264 output_raw_bitstream.h264
# Extract audio stream
ffmpeg -y -i input_video.mp4 -vn -acodec copy output_audio.aac
# Remux with new FPS 
ffmpeg -y -r 24 -i output_raw_bitstream.h264 -i output_audio.aac -c copy output.mp4

如果你想找到视频格式(在这种情况下是H264),你可以使用FFprobe,像这样:

ffprobe -loglevel error -select_streams v -show_entries stream=codec_name -of default=nw=1:nk=1 input_video.mp4

将会输出:

h264

详见如何分析文件并检测文件是否为H.264视频格式?


重新编码后:

ffmpeg -y -i input_video.mp4 -vf -r 24 output.mp4

1
我有一个使用h265编码的视频。使用ffmpeg -y -i input_video.mp4 -c copy -f hevc output_raw_bitstream.h264(将h264替换为hevc)提取了视频流。然后成功地重新混合了该流以达到所需的帧速率。谢谢! - Sergey P. aka azure
如果音频流存在时间戳间隔,您的第一组命令将消除这些间隔。此外,由于未重新定时,音频将不同步。 - Gyan
你重新编码的命令与 OP 想要的视频不符合("我的输出应该从 30fps 的 20 分钟变成 24fps 的 25 分钟),它会改变每秒帧数的“密度”到 24。但更大的问题在于语法有误 - ffmpeg 将报错中止。你已经添加了-vf,但它没有参数,因此 ffmpeg 解析器将使用-r作为参数并报错。 - Gyan
可以确认通过运行上述命令,音频完全混乱,因为流没有在正确的时间重新编码。 - jjisnow

3
您可以使用此命令,视频持续时间仍然不变。
ffmpeg -i input.mp4 -r 24 output.mp4

如果您不指定-r参数,在执行此操作后会截断您的视频。请在输入文件之前指定-r参数。 - zenw0lf
2
@zenw0lf 切掉怎么做?我测试过了,它运行得很好。这在ffmpeg的网站上有记录。 - Shayan
2
使用此选项后,我的视频从5分钟被拉伸到了10分钟。 - ashrasmun

3
您可以尝试例如(将25 fps转换为24 fps):
ffmpeg -itsscale 1.0416667 -i "您的输入文件" -vcodec copy "输出文件"
其中itsscale值为1.0416667,是ffmpeg中25/24的浮点变量(0.1234567是浮点值格式-不要使用1.04166666666666666667或双精度值:请注意,您不能在此处使用表达式/公式“25/24”)。这将将帧速率从25缩放到24 fps,保持相同的帧数,但使视频长度增加了1.0416667。
如果从23.976到24或从29.97到30,则该值将为0.999。
如果有音频,则应包括音频过滤器以通过使用atempo设置更改节奏来重新调整音频而不进行音高变化,您还应包括压缩器和比特率。对于字幕,您只需要包括-codec:s
因此,我们将有:
ffmpeg -itsscale 1.0416667 -i "输入文件" -filter:a atempo =“24/25” -codec:a ac3 -b:a 640k -vcodec copy -codec:s copy“输出文件”
我使用了音频编解码器ac3,比特率为640K和表达式“24/25”,这是允许的:注意:24/25是25/24(= 1.0416667)的倒数。
注意:如果您有多个流(视频、音频或字幕),则可能需要包括-map i:v:o(或0:a:0或0:s:0,其中“i”是输入流编号,“o”是要输出的流轨道)。

1
可能应该添加一条注释,atempo="value";其中的value是音频更改的速率。 - Georg
那是2023年对我有效的唯一答案(ffmpeg版本4.4.2)。它按照楼主的要求工作 - 无需重新编码,只需几秒钟。非常感谢!顺便说一句,我只需要改变视频的持续时间以匹配音频,所以我在提取的视频流上进行了操作。 - Alex Martian
由于某种原因,文件在mpv中播放正确,但(指定的)帧速率加倍,而我只延长了不到1%的持续时间。 - Alex Martian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接