TL;DR 使用 ffpmpeg 在单行中执行 Loudnorm 2pass 的示例(ffmpeg-normalize 是单线程/进程,我无法将其他 ffmpeg 选项与其结合使用)
关于 Loudnorm 的例子并不多,因为它对于 ffmpeg 来说相对较新,我已经使用 ffmpeg 大约 10 年了。不过,我对 Loudnorm 还是个新手
我读了:
http://k.ylo.ph/2016/04/04/loudnorm.html
这很有帮助。但是我试图将多个 ffmpeg 条目合并为 1 个。使用 ffmpeg-normalize(python)库也会将您限制为单个线程/进程。这很慢
对于 ffmpeg x264 的 2 次传递,您可以简单地执行以下操作:
ffmpeg -y -i $FILE -c:v libx264 -b:v 4000k -pass 1 -c:a aac -b:a 256k -f mkv /dev/null && \
ffmpeg -i $FILE -c:v libx264 -b:v 4000k -pass 2 -c:a aac -b:a 256k $FILE.mkv
但 Loudnorm 似乎不存在。我看不出有什么办法可以用单次和两次传递来实现这一点?
目前我正在对视频进行编码,然后规范化音频,最后从文件中删除元数据和章节数据(如果存在)(无论是否存在)
这将创建 3 个一次性文件(包括原始文件)
能够在一行中完成 Loudnorm 可以帮助我添加其他内容,同时是否可以同时执行 2pass x264 和 2pass Loudnorm?就像让它处理两者然后在第二遍中将它们合并一样。
如果可能的话,我想要这些例子,而不是链接。我可以自己谷歌链接,而且已经用了好几个星期了。谢谢
答案1
无法仅使用 ffmpeg 自动运行两遍 Loudnorm 滤波器,但您可以使用ffmpeg-normalize
程序来帮你完成。我知道你提到过,但是如果你想同时编码视频 - 特别是两次编码 - 那么你将不得不处理一个中间文件,即:
- 第一次运行:
ffmpeg-normalize
在原始视频上,复制原始视频流。 - 第二次运行:对规范化的音频文件或原始文件的视频流进行 x264 编码(多线程)。
你想要实现的目标根本无法仅靠 ffmpeg 来完成。你需要编写自己的解决方案,尤其是当你想并行处理多个文件时。这肯定会加快进程,即使单个 ffmpeg 运行仅使用一个线程。
作为起点,还有一个更简单的 Ruby 脚本在 FFmpeg 存储库中。它执行两次 Loudnorm 传递,读取第一次运行的统计数据。您可能能够对其进行修改,以使用多线程额外运行两次传递 x264 编码,即在第一次运行中运行第一个 x264 传递,在第二次运行中运行第二个传递:
第一遍:
ffmpeg -y -i $FILE -c:v libx264 -b:v 4000k -pass 1 -filter:a loudnorm=print_format=json -f mkv /dev/null
从输出中读取 JSON 统计信息loudnorm
(例如,使用 Python 的 JSON 解析器,或任何其他工具,如grep
或awk
),然后运行第二遍:
ffmpeg -i $FILE -c:v libx264 -b:v 4000k -pass 2 -filter:a loudnorm=linear=true:measured_I=$input_i:measured_LRA=$input_lra:measured_tp=$input_tp:measured_thresh=$input_thresh -c:a aac -b:a 256k $FILE.mkv
其中$input_i
,$input_lra
,$input_tp
,$input_thresh
是从第一遍读取的值。
答案2
不需要其他程序。实际上,这可以仅使用 ffmpeg 来完成。我实际上创建了一个可以执行此操作的 bat 文件。
cls
echo off
ffmpeg -i %1 -filter_complex "[0:a]loudnorm=I=-16:TP=-1.5:LRA=11:print_format=summary" -f null x 2>%1.txt
@for /f "tokens=3" %%a in ('findstr /C:"Input Integrated" %1.txt') do (set II=%%a)
echo %II% is the Input Integrated
@for /f "tokens=4" %%a in ('findstr /C:"Input True Peak" %1.txt') do (set ITP=%%a)
echo %ITP% is the Input True Peak
@for /f "tokens=3" %%a in ('findstr /C:"Input LRA" %1.txt') do (set ILRA=%%a)
echo %ILRA% is the Input LRA
@for /f "tokens=3" %%a in ('findstr /C:"Input Threshold" %1.txt') do (set IT=%%a)
echo %IT% is the Input Threshold
@for /f "tokens=3" %%a in ('findstr /C:"Output Integrated" %1.txt') do (set OI=%%a)
echo %OI% is the Output Integrated
@for /f "tokens=4" %%a in ('findstr /C:"Output True Peak" %1.txt') do (set OTP=%%a)
echo %OTP% is the Output True Peak
@for /f "tokens=3" %%a in ('findstr /C:"Output LRA" %1.txt') do (set OLRA=%%a)
echo %OLRA% is the Output LRA
@for /f "tokens=3" %%a in ('findstr /C:"Output Threshold" %1.txt') do (set OT=%%a)
echo %OT% is the Output Threshold
@for /f "tokens=3" %%a in ('findstr /C:"Target Offset" %1.txt') do (set TO=%%a)
echo %TO% is the Target Offset
ffmpeg -i %1 -af loudnorm=linear=true:I=-16:LRA=11:tp=-1.5:measured_I=%II%:measured_LRA=%ILRA%:measured_tp=%ITP%:measured_thresh=%IT%:offset=%TO%:print_format=summary loudnorm.wav
它首先会分析音频并将结果保存到文本文件中。然后从文本文件中读取结果并将其存储为每个所需测量字段的变量。您所要做的就是在文件中根据自己的喜好调整 I= 和 TP=。
答案3
从...开始彼得姆建议,这对于 *nix 用户来说是等效的:
ffmpeg -i $input -filter:a loudnorm=print_format=json -f null /dev/null 2>&1 >/dev/null | sed -n '/{/,/}/p' > /tmp/info
ii=`grep \"input_i\" /tmp/info | cut -d: -f2 | tr -cd [:digit:].-`
itp=`grep \"input_tp\" /tmp/info | cut -d: -f2 | tr -cd [:digit:].-`
ilra=`grep \"input_lra\" /tmp/info | cut -d: -f2 | tr -cd [:digit:].-`
it=`grep \"input_thresh\" /tmp/info | cut -d: -f2 | tr -cd [:digit:].-`
to=`grep \"target_offset\" /tmp/info | cut -d: -f2 | tr -cd [:digit:].-`
ffmpeg -i $input -af loudnorm=linear=true:I=-16:LRA=11:tp=-1.5:measured_I=$ii:measured_LRA=$ilra:measured_tp=$itp:measured_thresh=$it:offset=$to:print_format=summary $output
用您的输入和输出文件替换 $input 和 $output。
首先ffmpeg命令生成一个带有最终 JSON 对象的详细输出,该输出将被过滤掉sed命令在管道中并存储在临时文件中。后面的五个命令中的每一个都将相关参数加载到变量中,以提供给第二个ffmpeg通过。过滤按管道的三个步骤进行:首先选择包含相关参数的行(grep),接下来的值由名称分隔(切),最后只整理出数字部分(tr)。由于该过程取决于 ffmpeg 命令的输出格式,因此它并不是 100% 可靠的。
答案4
更多适用于 ffmpeg Loudnorm 过滤器的 bash 脚本
#! /usr/bin/env bash
# ffmpeg-loudnorm-analyze.sh
# analyze loudness of audio streams
# write result to stdout in json format
# example output: see end of script
# TODO verify ffmpeg output format: Parsed_loudnorm_([0-9]+)
set -e
set -u
input_file="$1"
shift
# extra args. example: -map 0:a:0 # process only the first audio stream
extra_args=("$@")
# https://ffmpeg.org/ffmpeg-filters.html#loudnorm
ffmpeg -i "$input_file" -pass 1 "${extra_args[@]}" \
-filter:a loudnorm=print_format=json -f null -y /dev/null |
grep -E '^\[Parsed_loudnorm_([0-9]+) @ 0x[0-9a-f]+\]' -A12 |
perl -0777 -pe 's/}\s+\[Parsed_loudnorm_([0-9]+) @ 0x[0-9a-f]+\]\s+/ },\n "$1": /g' |
perl -0777 -pe 's/\[Parsed_loudnorm_([0-9]+) @ 0x[0-9a-f]+\]\s+/ "$1": /g' |
sed 's/^}/ }/' |
sed '1 i\{'
printf '}\n';
exit
example output:
{
"0": {
"input_i": "-30.05",
"input_tp": "-0.01",
...
},
"1": {
"input_i": "-30.05",
"input_tp": "-0.01",
...
}
}
#! /usr/bin/env bash
# ffmpeg-loudnorm-format-filters.sh
# take a json file generated by ffmpeg-loudnorm-analyze.sh
# and format audio filters for ffmpeg
# example output:
# loudnorm=linear=true:I=-14:LRA=7:tp=-1:measured_I=-30.05:measured_LRA=16.60:measured_tp=-0.01:measured_thresh=-41.55:offset=0.46:print_format=summary
# to use the audio filter in ffmpeg:
# ffmpeg -af "loudnorm=..."
# https://ffmpeg.org/ffmpeg-filters.html#loudnorm
# https://superuser.com/questions/1604545/ffmpeg-loudnorm-results-in-not-very-loud
# https://youlean.co/loudness-standards-full-comparison-table/
max_integrated=-14
max_true_peak=-1
# LRA: loudness range target. Range is 1.0 - 50.0. Default value is 7.0
#loudnorm_LRA=11
loudnorm_LRA=7
loudnorm_linear=true
loudnorm_print_format=summary
loudnorm_extra_args=
set -e
set -u
json_file="$1"
stream_ids=$(jq -r 'keys[]' "$json_file" | sort -n)
s='"'
s+='loudnorm='
s+='linear='$loudnorm_linear':'
s+='I='$max_integrated':'
s+='LRA='$loudnorm_LRA':'
s+='tp='$max_true_peak':'
s+='measured_I=\(.input_i):'
s+='measured_LRA=\(.input_lra):'
s+='measured_tp=\(.input_tp):'
s+='measured_thresh=\(.input_thresh):'
s+='offset=\(.target_offset):'
s+='print_format='$loudnorm_print_format
s+="$loudnorm_extra_args"
s+='"'
jq_script="$s"
# print one filter per line, sorted by loudnorm result index
for stream_id in $stream_ids; do
stream_json="$(jq -r ".\"$stream_id\"" "$json_file")"
ffmpeg_loudnorm_filter="$(echo "$stream_json" | jq -r "$jq_script")"
echo "$ffmpeg_loudnorm_filter"
done