ffmpeg Loudnorm 2pass en una sola línea

ffmpeg Loudnorm 2pass en una sola línea

TL;DR un ejemplo de Loudnorm 2pass en una sola línea usando ffmpeg (ffmpeg-normalize es un solo subproceso/proceso y no puedo combinar otras opciones de ffmpeg)

Realmente no hay muchos ejemplos sobre Loudnorm ya que es relativamente nuevo para ffmpeg, he estado usando ffmpeg durante aproximadamente 10 años. Soy nuevo en Loudnorm sin embargo

He leído:

http://k.ylo.ph/2016/04/04/loudnorm.html

¿Cómo puedo normalizar el audio usando ffmpeg?

que son útiles. sin embargo, estoy tratando de consolidar varias entradas de ffmpeg en 1. El uso de la biblioteca ffmpeg-normalize (python) también lo limita a un solo subproceso/proceso. que es lento

para 2 pases de ffmpeg x264 simplemente puedes hacer:

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

Sin embargo, no parece existir para Loudnorm. ¿No veo la manera de hacerlo con una sola pasada y 2?

Actualmente estoy codificando video, luego normalizando el audio y finalmente eliminando metadatos y datos de capítulos de los archivos, si existen (independientemente de si existen o no).

esto crea 3 archivos desechables (incluido el original)

Ser capaz de hacer Loudnorm en una sola línea me ayudaría a agregarle otras cosas. ¿Es posible hacer 2 pases de x264 y 2 pasos de Loudnorm simultáneamente? como hacer que procese los dos y luego combinarlos en la segunda pasada.

Si es posible, me gustaría ver ejemplos de estos y no enlaces a cosas. Puedo buscar enlaces en Google por mi cuenta y lo he hecho durante varias semanas. Gracias

Respuesta1

No hay forma de ejecutar un filtro de norma ruidosa de dos pasos automáticamente con solo ffmpeg, pero puedes usar elffmpeg-normalizeprograma para hacerlo por usted. Sé que lo has mencionado, pero si quieres codificar vídeo al mismo tiempo – particularmente con dos pasadas – entonces tendrás que trabajar con un archivo intermedio, es decir:

  • Primera ejecución: ffmpeg-normalizeen el vídeo original, copiando la secuencia de vídeo original.
  • Segunda ejecución: codificación x264 (multiproceso) del archivo de audio normalizado o las transmisiones de video del archivo original.

Lo que desea lograr simplemente no se puede lograr solo con ffmpeg. Necesita programar su propia solución, especialmente si desea manejar varios archivos en paralelo. Sin duda, esto aceleraría el proceso, incluso si una sola ejecución de ffmpeg solo utiliza un subproceso.

Como punto de partida, también hay unscript Ruby más simpleen el repositorio de FFmpeg. Realiza dos pases en voz alta y lee las estadísticas de la primera ejecución. Es posible que pueda modificarlo para ejecutar adicionalmente la codificación x264 de dos pasos con subprocesos múltiples, es decir, ejecutar el primer paso x264 en la primera ejecución y el segundo en la segunda ejecución:

Primer pase:

ffmpeg -y -i $FILE -c:v libx264 -b:v 4000k -pass 1 -filter:a loudnorm=print_format=json -f mkv /dev/null

Lea las estadísticas JSON de la loudnormsalida (por ejemplo, usando el analizador JSON de Python o cualquier otra herramienta como grepo awk), luego ejecute la segunda pasada:

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

Donde $input_i,,, son $input_lralos valores leídos del primer pase.$input_tp$input_thresh

Respuesta2

No hay necesidad de otro programa. En realidad, esto PUEDE hacerse solo con ffmpeg. De hecho, creé un archivo bat que hará esto.

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

Lo que esto hace es primero analiza el audio y guarda los resultados en un archivo de texto. Luego lee los resultados del archivo de texto y los almacena como variables para cada campo de medición requerido. Todo lo que tienes que hacer es ajustar I= y TP= a tu gusto en el archivo.

Respuesta3

Empezando desdepetermgsugerencia, este es el equivalente para usuarios de *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

Reemplace $input y $output con sus archivos de entrada y salida.

La primeraffmpegEl comando produce una salida detallada con un objeto JSON final, que es filtrado por elsedcomando en la tubería y almacenado en un archivo temporal. Cada uno de los cinco comandos que siguen carga en una variable un parámetro relevante que se enviará al segundo.ffmpegaprobar. El filtrado opera en tres pasos de una tubería: primero se selecciona la línea que contiene el parámetro relevante (grep), a continuación el valor se separa por el nombre (cortar), y finalmente solo se ordena la parte numérica (tr). El proceso no es 100% confiable ya que depende del formato de salida del comando ffmpeg.

Respuesta4

más scripts bash para el filtro 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

información relacionada