目前我有 80mb 的电影,我想使用 ffmpeg 将其转换为大约 10mb 或 15mb。我知道质量会有所损失,但他们需要有声音。有没有办法指定文件大小或比我以前所做的更高的压缩
ffmpeg -i movie.mp4 -b 2255k -s 1280x720 movie.hd.ogv
目前每片大约 25mb
如果您的目标是特定的输出文件大小,最好的方法是使用H.264和双通道编码。
这里有一个很好的例子,但它太大了,无法复制粘贴: https://trac.ffmpeg.org/wiki/Encode/H.264#twopass
您使用
bitrate = target size / duration
计算目标比特率,然后启动 ffmpeg 两次:一次分析媒体,第二次进行实际编码:
ffmpeg -y -i input -c:v libx264 -preset medium -b:v 555k -pass 1 -c:a libfdk_aac -b:a 128k -f mp4 /dev/null && \
ffmpeg -i input -c:v libx264 -preset medium -b:v 555k -pass 2 -c:a libfdk_aac -b:a 128k output.mp4
编辑:H.265 (HEVC) 的压缩效果甚至更好(某些情况下是 H.264 大小的 50%),但支持尚未广泛,因此目前请继续使用 H.264。
受到Hashbrown的回答的启发。此版本保留了原始音频质量,并调整为目标大小。
新
grep
的依赖(-P
开关未在 OSX grep 中实现)#!/bin/bash
#
# Re-encode a video to a target size in MB.
# Example:
# ./this_script.sh video.mp4 15
T_SIZE="$2" # target size in MB
T_FILE="${1%.*}-$2MB.mp4" # filename out
# Original duration in seconds
O_DUR=$(\
ffprobe \
-v error \
-show_entries format=duration \
-of csv=p=0 "$1")
# Original audio rate
O_ARATE=$(\
ffprobe \
-v error \
-select_streams a:0 \
-show_entries stream=bit_rate \
-of csv=p=0 "$1")
# Original audio rate in KiB/s
O_ARATE=$(\
awk \
-v arate="$O_ARATE" \
'BEGIN { printf "%.0f", (arate / 1024) }')
# Target size is required to be less than the size of the original audio stream
T_MINSIZE=$(\
awk \
-v arate="$O_ARATE" \
-v duration="$O_DUR" \
'BEGIN { printf "%.2f", ( (arate * duration) / 8192 ) }')
# Equals 1 if target size is ok, 0 otherwise
IS_MINSIZE=$(\
awk \
-v size="$T_SIZE" \
-v minsize="$T_MINSIZE" \
'BEGIN { print (minsize < size) }')
# Give useful information if size is too small
if [[ $IS_MINSIZE -eq 0 ]]; then
printf "%s\n" "Target size ${T_SIZE}MB is too small!" >&2
printf "%s %s\n" "Try values larger than" "${T_MINSIZE}MB" >&2
exit 1
fi
# Set target audio bitrate
T_ARATE=$O_ARATE
# Calculate target video rate - MB -> KiB/s
T_VRATE=$(\
awk \
-v size="$T_SIZE" \
-v duration="$O_DUR" \
-v audio_rate="$O_ARATE" \
'BEGIN { print ( ( size * 8192.0 ) / ( 1.048576 * duration ) - audio_rate) }')
# Perform the conversion
ffmpeg \
-y \
-i "$1" \
-c:v libx264 \
-b:v "$T_VRATE"k \
-pass 1 \
-an \
-f mp4 \
/dev/null \
&& \
ffmpeg \
-i "$1" \
-c:v libx264 \
-b:v "$T_VRATE"k \
-pass 2 \
-c:a aac \
-b:a "$T_ARATE"k \
"$T_FILE"
Windows 版本,因为评论中的链接不再有效
@echo off
REM Windows implementation of Marian Minar's answer to "ffmpeg video compression / specific file size" https://stackoverflow.com/a/61146975/2902367
SET "video=%~1"
SET "target_video_size_MB=%~2"
SET "output_file=%~dpn1 %~2MB.mp4"
REM I usually don't see a big difference between two-pass and single-pass... set to anything but "true" to turn off two-pass encoding
SET "twopass=true"
REM We need a way to do floating point arithmetic in CMD, here is a quick one. Change the path to a location that's convenient for you
set "mathPath=c:\bin\Math.vbs"
REM Creating the Math VBS file
if not exist "%mathPath%" echo Wscript.echo replace(eval(WScript.Arguments(0)),",",".")>%mathPath%
echo Converting to %target_video_size_MB% MB: "%video%"
echo -^> "%output_file%"
pause
SETLOCAL EnableDelayedExpansion
REM Getting the (audio) duration. TODO: watch out for differing audio/video durations?
FOR /F "delims=" %%i IN ('ffprobe -v error -show_streams -select_streams a "%~1"') DO (
SET "line=%%i"
if "!line:~0,9!" == "duration=" (
SET "duration=!line:~9!"
)
)
REM Getting the audio bitrate
FOR /F "delims=" %%i IN ('ffprobe -v error -pretty -show_streams -select_streams a "%~1"') DO (
SET "line=%%i"
SET /A "c=0"
if "!line:~0,9!" == "bit_rate=" (
FOR %%a IN (!line:~9!) DO (
if "!c!" == "0" (
SET "audio_rate=%%a"
)
SET /A "c+=1"
)
)
)
REM TODO: Adjust target audio bitrate. Use source bitrate for now.
SET "target_audio_bitrate=%audio_rate%"
call:Math %target_video_size_MB% * 8192 / (1.048576 * %duration%) - %target_audio_bitrate%
SET "target_video_bitrate=%result%"
echo %target_audio_bitrate% audio, %target_video_bitrate% video
SET "passString="
if "%twopass%" == "true" (
echo Two-Pass Encoding
ffmpeg ^
-y ^
-i "%~1" ^
-c:v libx264 ^
-b:v %target_video_bitrate%k ^
-pass 1 ^
-an ^
-f mp4 ^
nul
SET "passString=-pass 2"
) else ( echo Single-Pass Encoding )
ffmpeg ^
-i "%~1" ^
-c:v libx264 ^
-b:v %target_video_bitrate%k ^
%passString% ^
-c:a aac ^
-b:a %target_audio_bitrate%k ^
"%output_file%"
pause
GOTO :EOF
:Math
REM echo Working : "%*"
for /f %%a in ('cscript /nologo %mathPath% "%*"') do set "Result=%%a"
GOTO :EOF
这是一种使用 bash 脚本自动完成此操作的方法
只需像
./script.sh file.mp4 15
那样拨打 15mB
bitrate="$(awk "BEGIN {print int($2 * 1024 * 1024 * 8 / $(ffprobe \
-v error \
-show_entries format=duration \
-of default=noprint_wrappers=1:nokey=1 \
"$1" \
) / 1000)}")k"
ffmpeg \
-y \
-i "$1" \
-c:v libx264 \
-preset medium \
-b:v $bitrate \
-pass 1 \
-an \
-f mp4 \
/dev/null \
&& \
ffmpeg \
-i "$1" \
-c:v libx264 \
-preset medium \
-b:v $bitrate \
-pass 2 \
-an \
"${1%.*}-$2mB.mp4"
注意我正在切断音频
使用 Windows 10、cmd 和 ffmpeg 将视频减小到预先确定的文件大小。
使用 H.264 和 Two-Pass 编码。
使用
bitrate = target file size / duration
计算您的比特率
目标文件大小(以千位为单位)。持续时间(以秒为单位)。 1 MB = 8192kb
将视频和音频的比特率分开,大约3/4视频,1/4音频。音频和视频码率之和不得超过计算码率。
-c:a libfdk_aac
对我不起作用。
ffmpeg -y -i input -c:v libx264 -b:v CALCULATED BITRATE HERE -pass 1 -an -f null nul && ^ffmpeg -y -i input -c:v libx264 -b:v CALCULATED BITRATE HERE -pass 2 -c:a aac -b:a CALCULATED BITRATE HERE output.mp4
ffmpeg -y -i 1.mp4 -c:v libx264 -b:v 555k -pass 1 -an -f null nul && ^ffmpeg -y -i 1.mp4 -c:v libx264 -b:v 555k -pass 2 -c:a aac -b:a 128k output.mp4
如果优先考虑音频,可以通过降低分辨率来将视频大小缩小到一小部分。
-过滤器:v“比例=-1:ih * 1/2:force_original_aspect_ratio =减少”-max_mushing_queue_size 1024
只需降低帧速率 - 如果您不需要高帧速率,就像在教程中一样 - 您将显着减小视频文件的大小,并且使用此过滤器您希望失去音频同步
ffmpeg -i 输入.mp4 -filter:v fps=fps=10 输出.mp4