如何将视频尺寸缩小到目标尺寸?

如何将视频尺寸缩小到目标尺寸?

我想减小视频的大小,以便能够通过电子邮件等发送。我看了这个问题:如何使用 ffmpeg 减小视频大小?在那里我得到了关于如何减少它的好建议。问题是我需要手动计算比特率。而且在执行此操作时,我必须通过手动尝试和错误来完成。

有没有什么好的方法可以使用ffmpeg(或其他工具)将视频的大小减小到目标尺寸?

请注意来自frostschutz 的评论:

过于担心大小并选择固定的文件大小,而不管视频长度/分辨率/帧速率/内容如何,​​大多数时候都不会给出令人满意的结果...将其上传到某处,通过电子邮件发送链接。如果必须对其进行编码,请设置质量级别,并使比特率保持动态。如果明显无法满足尺寸要求,请取消编码并相应地调整质量级别。冲洗并重复。

总体来说是好的建议,但它不适合我的用例。由于技术限制,上传到外部链接不是一种选择,其中之一是接收者没有 http 访问权限。

我也可以澄清一下,我不是在寻找确切地我指定的尺寸。如果它相当接近我想要的(可能低 5% 或 10% 左右)就足够了,但应该保证它不会超过我的目标极限。

答案1

@philip-couling 的答案很接近,但缺少几部分:

  • 所需大小(以 MB 为单位)需要乘以 8 才能获得比特率
  • 对于比特率,1Mb/s = 1000 kb/s = 1000000 b/s;乘数不是 1024(还有另一个前缀 KiB/s,即 1024 B/s,但 ffmpeg 似乎没有使用它)
  • 截断或向下舍入非整数length(而不是精确使用或向上舍入)将导致文件大小稍微超过目标大小
  • -b标志指定平均比特率:实际上,编码器会让比特率稍微跳跃,结果可能会超出您的目标大小。但是,您可以使用-maxrate和来指定最大比特率-bufsize

对于我的用例,我还想要一个硬编码的音频比特率,然后相应地调整视频(这似乎也更安全,我不能 100% 确定 ffmpeg 的-b标志是否一起指定视频和音频流的比特率)。

考虑到所有这些并严格限制为 25MB:

file="input.mp4"
target_size_mb=25 # 25MB target size
target_size=$(( $target_size_mb * 1000 * 1000 * 8 )) # target size in bits
length=`ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file"`
length_round_up=$(( ${length%.*} + 1 ))
total_bitrate=$(( $target_size / $length_round_up ))
audio_bitrate=$(( 128 * 1000 )) # 128k bit rate
video_bitrate=$(( $total_bitrate - $audio_bitrate ))
ffmpeg -i "$file" -b:v $video_bitrate -maxrate:v $video_bitrate -bufsize:v $(( $target_size / 20 )) -b:a $audio_bitrate "${file}-${target_size_mb}mb.mp4"

请注意,当-maxrate限制-bufsize最大比特率时,平均比特率必然会降低,因此在我的测试中视频将低于目标大小多达 5-10%(在各种目标大小的 20 秒视频上)。的值-bufsize很重要,上面使用的计算值(基于目标大小)是我的最佳猜测。太小了,它会大大降低质量,并且低于目标大小约 50%,但太大,我认为它可能会超过目标大小?

如果您没有严格的最大文件大小,为了给编码器提供更大的灵活性,删除-maxrate-bufsize会带来更好的质量,但在我的测试中可能会导致视频超出目标大小 5%。文档中的更多信息,您将在其中看到此警告:

注意:如果视频难以编码,限制比特率可能会导致输出质量低下。在大多数情况下(例如存储文件进行存档),让编码器选择合适的比特率是恒定质量或基于 CRF 的编码。


您可以使用以下代码来声明可重用的 bash 函数:

ffmpeg_resize () {
    file=$1
    target_size_mb=$2  # target size in MB
    target_size=$(( $target_size_mb * 1000 * 1000 * 8 )) # target size in bits
    length=`ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file"`
    length_round_up=$(( ${length%.*} + 1 ))
    total_bitrate=$(( $target_size / $length_round_up ))
    audio_bitrate=$(( 128 * 1000 )) # 128k bit rate
    video_bitrate=$(( $total_bitrate - $audio_bitrate ))
    ffmpeg -i "$file" -b:v $video_bitrate -maxrate:v $video_bitrate -bufsize:v $(( $target_size / 20 )) -b:a $audio_bitrate "${file}-${target_size_mb}mb.mp4"
}

ffmpeg_resize file1.mp4 25 # resize `file1.mp4` to 25 MB
ffmpeg_resize file2.mp4 64 # resize `file2.mp4` to 64 MB

答案2

声誉不够,无法发表评论。调整了@tobek 上面的代码以作为 windows/dos 批处理文件工作。

@echo off
setlocal

if "%~1" == "" (
    echo Usage:
    echo.
    echo %0 filename.mp4
    echo or
    echo %0 filename.mp4 25
    echo   (25 = 25MB^)
    goto end
)

set target_mb=%2
if "%~2" == "" (
    rem # default
    set target_mb=25
)

set file=%1
set /a target_size=%target_mb% * 1000 * 1000 * 8
for /f %%c in ('ffprobe -v error -show_entries format^=duration -of default^=noprint_wrappers^=1:nokey^=1 "%file%"') do set length=%%c

for /f "tokens=1 delims=." %%n in ('echo %length%') do set length_round_up=%%n
set /a length_round_up=%length_round_up% + 1

rem # 128k bit rate
set /a audio_bitrate=128 * 1000
set /a total_bitrate=%target_size% / %length_round_up%
set /a video_bitrate=%total_bitrate% - %audio_bitrate%
set /a bufsize=%target_size% / 20

for %%f in ("%file%") do (
    set filedrive=%%~df
    set filepath=%%~pf
    set filename=%%~nf
    set fileextension=%%~xf
)

set file_output=%filedrive%%filepath%%filename%-out%fileextension%

ffmpeg -i "%file%" -b:v %video_bitrate% -maxrate:v %video_bitrate% -bufsize:v %bufsize% -b:a %audio_bitrate% "%file_output%"
:end

答案3

免责声明:这似乎并不完美。它变得稍微大一点。

将其他两个答案放在一起相对简单:

假设您需要一个 10MB 文件(10485760 字节),您可以使用它ffprobe来查找持续时间并让 shell 执行计算。

请小心,因为 ffprobe 会报告小数位,这会导致 shell 算术出错。我曾经${length%.*}去掉小数位:

size=10485760 
length=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4)
ffmpeg -i input.mp4 -b $(( $size / ${length%.*} )) output.mp4

答案4

最补和小修正投票答案经过@托贝克

兆字节 (MiB) 等于 8,388.608 k = (1024^2) / (1000/8) 来源:ffmpeg 文档

2 小时视频比特率计算示例,音频 256kbits,目标达到 2000 MiB(Telegram 上传限制) 8,388.608 * 2000 / 7200 - 256= 2074k

因此,在组装命令时,video_bitrate 标志将如下所示:-b:v 2074K

相关内容