使用 FFmpeg 保持纵横比

Posted

技术标签:

【中文标题】使用 FFmpeg 保持纵横比【英文标题】:Maintaining aspect ratio with FFmpeg 【发布时间】:2012-01-03 08:07:39 【问题描述】:

我需要使用 FFmpeg 转换一堆视频文件。我运行了一个可以很好地转换所有文件的 Bash 文件,但是如果转换的文件不是 16:9 格式,则会出现问题。

当我将屏幕尺寸固定为 -s 720x400 时,如果原始的纵横比是 4:3,FFmpeg 会创建一个 16:9 的输出文件,从而搞砸纵横比。

是否有允许将纵横比设置为主要参数,并调整大小(例如,仅通过固定 X 或 Y 尺寸)的设置?

【问题讨论】:

【参考方案1】:
-vf "scale=640:-1"

在遇到错误之前效果很好

[libx264 @ 0x2f08120] height not divisible by 2 (640x853)

所以最通用的方法是使用过滤器表达式:

scale=640:trunc(ow/a/2)*2

它取输出宽度(ow),除以纵横比(a),除以2,截断小数点后的数字并乘以2。它保证得到的高度可以被2整除。

感谢ffmpeg trac

更新

正如 cmets 指出的那样,更简单的方法是使用 -vf "scale=640:-2"。 感谢@BradWerth 提供优雅的解决方案

【讨论】:

或者,如果要指定高度,让ffmpeg按比例设置宽度,scale=trunc(oh*a/2)*2:min(480\,iw) @BradWerth 这在任何地方都有记录吗? @Adam 我不记得了。我相信在实施这个出色的答案时,我只是在阅读选项并使用设置。 次要注意:scale=640:trunc(ow/a/2)*2scale=640:-2 都是足够的答案,但不一定等效。前者的截断可以给出一个值,例如2 像素,较小取决于输入源的大小。 @AndersEmil 据我所知,它仅适用于 x264,并且与宽度和高度相关【参考方案2】:

例如:

1920x1080 纵横比 16:9 => 640x480 纵横比 4:3:

ffmpeg -y -i import.media -aspect 16:9 scale=640x360,pad=640:480:0:60:black output.media

纵横比 16:9,尺寸宽 640 像素 => 高 360 像素: 最终输出尺寸为 640x480,填充 60 像素黑色图像(顶部和底部):

"-vf scale=640x360,pad=640:480:0:60:black"

【讨论】:

我只需要从 640x480 到 1280x720 并使用 -aspect 16:9 -vf scale=960x720,pad=1280:720:160:0:white(我也需要白色填充而不是黑色)。谢谢! 我需要采用相反的方式,从 640x480 到 1920x1080,使用柱式拳击:-vf "scale=1440x1080,pad=1920:1080:240:0:black" 我可以使用它:ffmpeg -y -i 01.mp4 -vf "scale=640x360,pad=640:480:0:60:black" 02.mp4【参考方案3】:

我很久以前就问过这个问题,但实际上我有一个当时我不知道的解决方案——为了保持纵横比,你应该使用视频滤镜比例,即一个非常强大的过滤器。

您可以像这样简单地使用它:

-vf "scale=640:-1" 

这将固定宽度并提供保持纵横比所需的高度。但您也可以使用许多其他选项甚至数学函数,请查看此处的文档 - http://ffmpeg.org/ffmpeg.html#scale

【讨论】:

这很好,但我仍然发现如果高度不能被 2 整除,我可能会遇到问题。知道如何解决这个问题吗? 请注意这里的-vf "scale=640:-2" 答案。【参考方案4】:

尽管这些答案中的大多数都很棒,但我一直在寻找一个可以在保持纵横比的同时调整到目标尺寸(宽度 高度)的命令。我能够使用 ffmpeg 的 Expression Evaluation 来完成此操作。

这是相关的视频过滤器,目标尺寸为 512:

-vf "thumbnail,scale='if(gt(iw,ih),512,trunc(oh*a/2)*2)':'if(gt(iw,ih),trunc(ow/a/2)*2,512)'"


对于输出宽度:

'if(gt(iw,ih),512,trunc(oh*a/2)*2)'

如果宽度大于高度,则返回目标,否则返回比例宽度。


对于输出高度:

'if(gt(iw,ih),trunc(ow/a/2)*2,512)'

如果宽度大于高度,则返回比例高度,否则返回目标。

【讨论】:

-vf "scale='if(gt(iw,ih),512,-1)':'if(gt(iw,ih),-1,512)'" 要容易得多。 【参考方案5】:

使用force_original_aspect_ratio,来自ffmpeg trac:

ffmpeg -i input.mp4 -vf scale=720:400:force_original_aspect_ratio=decrease output.mp4

【讨论】:

当您的视频具有奇怪的尺寸(例如 1438x800)时,此操作会失败,然后结果将为 719x400 并给出错误,因为宽度不能被 2 整除... @DanielCarrascoMarín 是的,当您无法控制输入视频大小时,ffmpeg 本身无法解决被 2 整除的问题(不幸的是)。我已经通过计算 Python 脚本中的纵横比解决了这个问题,然后传递了我想要的 ffmpeg scale= 参数。 我刚刚使用 ffmpeg 3.4 和 3.2.9 对其进行了测试,它的魅力在于。视频变为:719x400,但没有错误。我可以用 VLC 媒体播放器播放它(看到右边的额外像素)。 Ubuntu 原生视频播放器也玩得很好。 ffmpeg 之后可以读取文件,并转换为另一个文件【参考方案6】:

如果您尝试拟合边界框,那么按照xmedeko's answer 使用force_original_aspect_ratio 是一个很好的起点。

但是,如果您的输入视频具有奇怪的大小并且您正在编码为要求尺寸可以被 2 整除的格式,这将不起作用,从而导致错误。

在这种情况下,您可以在 scale 函数中使用表达式求值,就像在 Charlie's answer 中使用的那样。

假设输出边界框为 720x400:

-vf "scale='trunc(min(1,min(720/iw,400/ih))*iw/2)*2':'trunc(min(1,min(720/iw,400/ih))*ih/2)*2'"

分解:

min(1,min(720/iw,400/ih) 找到适合边界框的比例因子(来自 here),将其限制为最大值 1 以确保它仅缩小比例,并且 trunc(<scaling factor>*iw/2)*2trunc(<scaling factor>*iw/2)*2 通过除以 2 确保维度可被 2 整除,使结果为整数,然后再乘以 2。

这消除了在编码之前查找输入视频尺寸的需要。

【讨论】:

【参考方案7】:

您可以使用 ffmpeg -i 获取原始文件的尺寸,并在您的命令中使用它进行编码。你在什么平台上使用 ffmpeg?

【讨论】:

嗨,我在 Linux 上使用它。理想情况下,我希望有一个无论纵横比如何都可以工作的 bash 脚本。我想我可以手动调整它以适应非宽屏分辨率,但是在 ffmpeg 中有无数的选项,我以为我只是不知道一个技巧设置...... 是的,这是一个答案,因为通过使用原始尺寸并通过简单的计算缩小(或放大)它们,OP 可以“手动”解决问题。 我发现它在任何情况下都很有用。我个人很高兴发布了这个答案。【参考方案8】:

如果存在“-aspect x:y”且输出文件格式为 ISO 媒体文件格式 (mp4),则 ffmpeg 将 pasp-atom (PixelAspectRatioBox) 添加到视频轨道的 stsd-box 中,以向玩家指示预期的 纵横比。播放器应分别缩放视频帧。 不需要在编码或转码之前缩放视频以适应宽高比,它应该由播放器执行。

【讨论】:

【参考方案9】:

由于 ffmpeg 要求宽度/高度可被 2 整除, 我想你想指定一个维度,这将是选项:

ffmpeg -i input.mp4 -vf scale=1280:-2 output.mp4

【讨论】:

【参考方案10】:

上面的答案很好,但大多数都假设特定的视频尺寸,而不是在通用纵横比上运行。

无论具体尺寸如何,您都可以使用以下方法填充视频以适应任何宽高比:

-vf 'pad=x=(ow-iw)/2:y=(oh-ih)/2:aspect=16/9'

在我的示例中,我使用了比率 16/9。上面是做一些更手动的事情的捷径:

pad='max(iw,(16/9)*ih)':'max(ih,iw/(16/9))':(ow-iw)/2:(oh-ih)/2

这可能会输出奇数(不是偶数)的视频尺寸,因此您可以确保输出是偶数,如下所示:

pad='trunc(max(iw,(16/9)*ih)/2)*2':'trunc(max(ih,iw/(16/9))/2)*2':(ow-iw)/2:(oh-ih)/2

但实际上你只需要pad=x=(ow-iw)/2:y=(oh-ih)/2:aspect=16/9

对于上述所有示例,如果 INPUT 视频具有奇数尺寸,您将收到错误消息。如果输入大小为奇数,即使 pad=iw:ih 也会出错。通常你不会有奇数大小的输入,但如果你有,你可以先使用这个过滤器来修复它:pad='mod(iw,2)+iw':'mod(ih,2)+ih'

【讨论】:

以上是关于使用 FFmpeg 保持纵横比的主要内容,如果未能解决你的问题,请参考以下文章

根据高度保持div的纵横比[重复]

更改高度时保持图像纵横比

gridstack.js - 保持纵横比

使用 flexbox 并保持 1:1 的纵横比,即使内容大小不同

使用 PHP 缩放图像并保持纵横比

ImageMagick 缩放和裁剪保持纵横比