在 linux 服务器上将动画 gif 转换为视频,同时保持帧速率
convert animated gif to video on linux server while preserving frame rate
如何在 linux 服务器上以编程方式将动画 gif 转换为视频(例如 h264@mp4)?
我需要这个来处理用户生成的内容,这些内容应该以几种定义的视频格式输出;因此,用户可能想要处理动画 gif 文件。我已经有一组工作 php 脚本使用 avconv 将视频文件转码为特定格式(如 vpx@webm 和 h264@mp4,缩放到特定分辨率),但因此我需要视频输入。
通常的方法似乎是提取gif的帧然后对其进行编码,例如
convert file.gif file%03d.png
avconv -i file%03d.png file.mp4
但这会丢弃由 gif 文件中的暂停信息决定的帧速率。可以使用 -r 为 avconv 定义一个帧率,但是
- 这不考虑帧之间的停顿,因为它们可能不同(例如第一帧 100 毫秒停顿,第二帧 250 毫秒停顿,第三帧 100 毫秒停顿,...)
- 由于输入来自用户,它甚至可能会有所不同,因为一些 gif 可能有 5fps 而其他的 30fps
我注意到 avconv 能够自行处理 gif,因此可能会尊重正确的暂停,但是当我这样做时(如 How to convert GIF to Mp4 is it possible? 中类似描述)
avconv -i file.gif -r 30 file.mp4
avconv 将只获取 gif 的第一帧,同时它至少将文件检测为视频:
Duration: 00:00:00.04, start: 0.000000, bitrate: N/A
Stream #0.0: Video: gif, pal8, 640x480, 25 tbn
(示例 gif 'file.gif' 有 15 帧,每帧有 100 毫秒暂停 => 1.5 秒持续时间,循环)
- 我错过了什么?怎么了?
- 这个用例可能有更好的工具吗?
- 什么是大型网站,例如9gag 用于将上传的 gif 转码为视频?
另一个 Avconv 错误 (YAAB)
ffmpeg
有更好的 GIF 解复用支持(和 improved GIF encoding). I recommend ditching avconv
and getting ffmpeg
(the real one from FFmpeg; not the old charlatan from Libav). A static build is easy, or you can of course compile.
例子
ffmpeg -i in.gif -c:v libx264 -pix_fmt yuv420p -movflags +faststart out.mp4
有关更多示例,请参阅 FFmpeg Wiki: H.264 Encoding Guide。
如果出于某种原因您需要使用 avconv 和 imagemagick,您可能想尝试这样的操作:
ticks_per_frame = subprocess.check_output('identify -verbose -format %T_ {0}'.format(gif_path).split()).split('_')[:-1]
ticks_per_frame = [int(i) for i in ticks_per_frame]
num_frames = len(ticks_per_frame)
min_ticks = min(ticks_per_frame)
subprocess.call('convert -coalesce {0} tmp%d.png'.format(gif_path).split())
if len(set(ticks_per_frame)) > 1:
num_dup = 0
num_dup_total = 0
for frame, ticks in enumerate(ticks_per_frame):
num_dup_total += num_dup
frame += num_dup_total
num_dup = 0
if ticks > min_ticks:
num_dup = (ticks / min_ticks) - 1
for i in range(num_frames + num_dup_total - 1, frame, -1):
orig = 'tmp%d.png' % i
new = 'tmp%d.png' % (i + num_dup)
subprocess.call(['mv', orig, new])
for i in range(1, num_dup + 1):
curr = 'tmp%d.png' % frame
dup = 'tmp%d.png' % (i + frame)
subprocess.call(['cp', curr, dup])
framerate = (100 / min_ticks) if min_ticks else 10
subprocess.call('avconv -r {0} -i tmp%d.png -c:v libx264 -crf {1} -pix_fmt yuv420p \
-vf scale=trunc(iw/2)*2:trunc(ih/2)*2 -y {2}.mp4'.format(framerate, quality, STORAGE_DIR + mp4_name).split())
subprocess.call(['rm'] + glob('tmp*.png'))
因此,获取 gif 每一帧的刻度(以厘秒为单位)(通过识别),转换为多个 png,然后在根据刻度值制作副本的同时遍历它们。不用担心,png 文件仍将保持连续顺序。使用真正的 FFmpeg 仍然是最好的方法。
如何在 linux 服务器上以编程方式将动画 gif 转换为视频(例如 h264@mp4)?
我需要这个来处理用户生成的内容,这些内容应该以几种定义的视频格式输出;因此,用户可能想要处理动画 gif 文件。我已经有一组工作 php 脚本使用 avconv 将视频文件转码为特定格式(如 vpx@webm 和 h264@mp4,缩放到特定分辨率),但因此我需要视频输入。
通常的方法似乎是提取gif的帧然后对其进行编码,例如
convert file.gif file%03d.png
avconv -i file%03d.png file.mp4
但这会丢弃由 gif 文件中的暂停信息决定的帧速率。可以使用 -r 为 avconv 定义一个帧率,但是
- 这不考虑帧之间的停顿,因为它们可能不同(例如第一帧 100 毫秒停顿,第二帧 250 毫秒停顿,第三帧 100 毫秒停顿,...)
- 由于输入来自用户,它甚至可能会有所不同,因为一些 gif 可能有 5fps 而其他的 30fps
我注意到 avconv 能够自行处理 gif,因此可能会尊重正确的暂停,但是当我这样做时(如 How to convert GIF to Mp4 is it possible? 中类似描述)
avconv -i file.gif -r 30 file.mp4
avconv 将只获取 gif 的第一帧,同时它至少将文件检测为视频:
Duration: 00:00:00.04, start: 0.000000, bitrate: N/A
Stream #0.0: Video: gif, pal8, 640x480, 25 tbn
(示例 gif 'file.gif' 有 15 帧,每帧有 100 毫秒暂停 => 1.5 秒持续时间,循环)
- 我错过了什么?怎么了?
- 这个用例可能有更好的工具吗?
- 什么是大型网站,例如9gag 用于将上传的 gif 转码为视频?
另一个 Avconv 错误 (YAAB)
ffmpeg
有更好的 GIF 解复用支持(和 improved GIF encoding). I recommend ditching avconv
and getting ffmpeg
(the real one from FFmpeg; not the old charlatan from Libav). A static build is easy, or you can of course compile.
例子
ffmpeg -i in.gif -c:v libx264 -pix_fmt yuv420p -movflags +faststart out.mp4
有关更多示例,请参阅 FFmpeg Wiki: H.264 Encoding Guide。
如果出于某种原因您需要使用 avconv 和 imagemagick,您可能想尝试这样的操作:
ticks_per_frame = subprocess.check_output('identify -verbose -format %T_ {0}'.format(gif_path).split()).split('_')[:-1]
ticks_per_frame = [int(i) for i in ticks_per_frame]
num_frames = len(ticks_per_frame)
min_ticks = min(ticks_per_frame)
subprocess.call('convert -coalesce {0} tmp%d.png'.format(gif_path).split())
if len(set(ticks_per_frame)) > 1:
num_dup = 0
num_dup_total = 0
for frame, ticks in enumerate(ticks_per_frame):
num_dup_total += num_dup
frame += num_dup_total
num_dup = 0
if ticks > min_ticks:
num_dup = (ticks / min_ticks) - 1
for i in range(num_frames + num_dup_total - 1, frame, -1):
orig = 'tmp%d.png' % i
new = 'tmp%d.png' % (i + num_dup)
subprocess.call(['mv', orig, new])
for i in range(1, num_dup + 1):
curr = 'tmp%d.png' % frame
dup = 'tmp%d.png' % (i + frame)
subprocess.call(['cp', curr, dup])
framerate = (100 / min_ticks) if min_ticks else 10
subprocess.call('avconv -r {0} -i tmp%d.png -c:v libx264 -crf {1} -pix_fmt yuv420p \
-vf scale=trunc(iw/2)*2:trunc(ih/2)*2 -y {2}.mp4'.format(framerate, quality, STORAGE_DIR + mp4_name).split())
subprocess.call(['rm'] + glob('tmp*.png'))
因此,获取 gif 每一帧的刻度(以厘秒为单位)(通过识别),转换为多个 png,然后在根据刻度值制作副本的同时遍历它们。不用担心,png 文件仍将保持连续顺序。使用真正的 FFmpeg 仍然是最好的方法。