我正在尝试改变 MP4 视频的帧速率(这是一个 120fps 的 GoPro 视频,我想以 30fps 的速度播放它以获得慢动作效果)。
我正在使用 avconv 来实现这一点:
avconv -i SourceMovie.mp4 -vf setpts=4.0*PTS -r 30 DestMovie.mp4
从技术上讲,这可行,但它会重新编码电影。除了速度慢之外,这显然是一个质量问题。从技术上讲,应该有一种方法可以在视频标题中设置 fps,我该如何更改它?(除了 avconv 之外的任何工具都可以。)
答案1
MP4盒可以做到。
命令
MP4Box -add SourceMovie.mp4#video -raw 1 -new test
创建文件test
和test_track1.h264
。现在,您可以创建一个具有您想要的任何支持的帧速率的 mp4 文件(此示例中为 30):
MP4Box -add test_track1.h264:fps=30 -new DestMovie.mp4
MP4Box 是该软件包的一部分gpac
,因此请确保它已安装。
答案2
更改视频(容器)标题中的帧速率对视频流(或音频流)本身没有影响。重新编码是唯一的选择。
视频流有时间戳,在大多数视频中,由于帧间压缩,帧相互依赖。有些播放器可以加快或减慢视频速度,但不能通过嵌入命令或其他方式。当容器命令要求时,没有播放器可以动态更改帧速率。视频文件最终会超出规格(即不符合标准),99.9% 的播放器会拒绝遵守。如果不重新编码,就不可能做你想做的事。当然,你可以等到编辑的最后一步再重新编码。
答案3
如果您的主要目的是慢动作播放,而不是将文件保留为 MP4,则可以使用 MKVmerge GUI 工具将其重新混合到 Matroska 容器中,这样可以轻松更改帧速率。重新混合比重新编码要好得多,因为它只会更改元数据,而不会更改流本身。
首先安装包
sudo apt-get install mkvtoolnix-gui
然后启动 MKVmerge GUI。你将看到如下窗口
只需按下添加按钮并选择您的文件,或者只需将文件拖放到“输入文件”区域即可。此时您应该能够选择视频中的视频流。您还可以删除其他流,因为它们无论如何都会妨碍您。
选择它之后,您应该会看到底部的选项卡变为活动状态:
更改为“格式特定操作”
您可以看到有一个字段“FPS”,您可以在其中输入每秒帧数的值。看起来您打算将速度减慢四倍,因此每秒 7 帧左右就是您的目标。您也可以使用“拉伸”选项。
之后,您只需更改输出文件的名称(如果需要)并按“开始多路复用”即可。
该程序将运行并且您将获得您的文件。
答案4
如果你有一个 .mp4 文件,并且想要更改视频播放器渲染帧的速率无需重新编码,您可以通过更改 stts atom 中的“样本持续时间”值来实现这一点。可能有包(MP4Box?)可以做到这一点,但如果您想自己做,使用十六进制编辑器(实际上无法扩展)或非常简单的 python 代码非常简单。无论哪种方式,都有三个简单的步骤:
- 识别 mdhd 原子中的“时间尺度”值。mdhd 原子的详细描述如下:https://developer.apple.com/documentation/quicktime-file-format/media_header_atom。在十六进制编辑器中,搜索“mdhd”将使您处于 mdhd 原子中“类型”字段的开头。时间标度是一个四字节字段,从类型字段开始处开始 16 个字节(即,跳过类型字段结束后的 12 个字节)。
- 通过将时间尺度除以所需的播放 fps 来计算所需的持续时间:将十六进制时间尺度转换为 int,除以 fps,取底线,然后转换回十六进制。例如,如果时间尺度是 00 00 40 00,则转换为 16384。如果您希望播放速度为 10 fps,则 floor(16384/10)=1638,即十六进制的 00 00 06 66。
- 现在您已准备好在 stts 原子中设置帧持续时间,如下所述:https://developer.apple.com/documentation/quicktime-file-format/time-to-sample_atom。搜索“stts”将使您处于 stts 原子中“类型”字段的开头。再往前八个字节是四字节“条目数”字段,它告诉您紧随其后的“采样时间”表中有多少个条目。该表中的每个条目(通常只有一个条目)由两个 4 字节值组成:采样计数和采样持续时间。将 4 字节采样持续时间更改为步骤 2 中计算出的十六进制值将为您提供所需的播放速率。
在python3中,它可能看起来像这样:
with open(filename, 'rb') as f:
mp4bytes = f.read()
pos_mdhd = mp4bytes.find(b'mdhd')
if pos_mdhd > 0:
time_scale = int.from_bytes(mp4bytes[pos_mdhd + 16:pos_mdhd + 20], 'big')
new_duration = (time_scale//new_fps).to_bytes(4, 'big')
pos_stts = mp4bytes.find(b'stts')
if pos_stts > 0:
num_entries = int.from_bytes(mp4bytes[pos_stts + 8:pos_stts + 12], 'big')
with open(filename, 'rb+') as g:
for i in range(num_entries):
g.seek(pos_stts + 16 + (i*8))
g.write(new_duration)
就这样,只要插入你想要的文件名和新FPS。这将重写所有帧持续时间以产生所需的播放 fps。请注意,它还会覆盖输入文件,因此请先进行备份。