TL;DR пример loudnorm 2pass в одну строку с использованием ffpmpeg (ffmpeg-normalize — это однопотоковый/процессный процесс, и я не могу объединить с ним другие параметры ffmpeg)
Не так много примеров на loudnorm, так как он относительно новый для ffmpeg, я использую ffmpeg около 10 лет. Я новичок в loudnorm, однако
Я прочел:
http://k.ylo.ph/2016/04/04/loudnorm.html
Как нормализовать звук с помощью ffmpeg?
которые полезны. Однако я пытаюсь объединить несколько записей ffmpeg в одну. Использование библиотеки ffmpeg-normalize (Python) также ограничивает вас одним потоком/процессом. Это медленно
для 2 проходов ffmpeg x264 вы можете просто сделать:
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 в одну строку помогла бы мне добавить к нему другие вещи. Также возможно ли сделать 2 прохода x264 и 2 прохода loudnorm одновременно? То есть заставить его обработать оба, а затем объединить их во втором проходе.
Если возможно, я хотел бы примеры этого, а не ссылки на вещи. Я могу гуглить ссылки самостоятельно и иметь их в течение нескольких недель. Спасибо
решение1
Невозможно автоматически запустить двухпроходный фильтр loudnorm с помощью только ffmpeg, но вы можете использовать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
выходных данных (например, с помощью анализатора JSON Python или любого другого инструмента, например 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 на ваши файлы in и out.
Первыйffmpegкоманда создает подробный вывод с конечным объектом JSON, который отфильтровывается с помощьюседкоманда в трубе и сохраняется во временном файле. Каждая из пяти команд, которые следуют, загружает в переменную соответствующий параметр, который будет передан второмуffmpegпроход. Фильтрация выполняется в три этапа конвейера: сначала выбирается строка, содержащая соответствующий параметр (грэп), затем значение отделяется именем (резать), и, наконец, сортируется только числовая часть (тр). Этот процесс не надежен на 100%, поскольку он зависит от выходного формата команды ffmpeg.
решение4
больше bash-скриптов для фильтра ffmpeg loudnorm
#! /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