단일 라인의 ffmpeg Loudnorm 2pass

단일 라인의 ffmpeg Loudnorm 2pass

TL;DR ffpmpeg를 사용하는 단일 라인의 Loudnorm 2pass의 예(ffmpeg-normalize는 단일 스레드/프로세스이므로 다른 ffmpeg 옵션을 결합할 수 없습니다)

ffmpeg가 상대적으로 새로운 것이기 때문에 Loudnorm에 대한 예는 많지 않습니다. 저는 ffmpeg를 10년 정도 사용해 왔습니다. 그러나 나는 Loudnorm을 처음 접했습니다.

나는 읽었습니다:

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

ffmpeg를 사용하여 오디오를 어떻게 정규화할 수 있나요?

도움이됩니다. 그러나 여러 ffmpeg 항목을 1로 통합하려고 합니다. ffmpeg-normalize(파이썬) 라이브러리를 사용하면 단일 스레드/프로세스로 제한됩니다. 느리다

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에는 존재하지 않는 것 같습니다. 1회 패스와 2회 패스로는 전혀 할 수 없는 방법이 있나요?

현재 저는 비디오를 인코딩하고 오디오를 정규화하고 마지막으로 파일에서 메타데이터와 챕터 데이터를 제거하고 있습니다(존재 여부에 관계없이).

이렇게 하면 3개의 일회용 파일(원본 포함)이 생성됩니다.

한 줄로 Loudnorm을 수행할 수 있으면 여기에 다른 항목을 추가하는 데 도움이 될 것입니다. 또한 x264의 2패스와 Loudnorm의 2패스를 동시에 수행할 수 있습니까? 두 가지를 처리한 다음 두 번째 패스에서 결합합니다.

가능하다면 이러한 예를 원하고 사물에 대한 링크는 원하지 않습니다. 나는 스스로 Google 링크를 검색할 수 있으며 몇 주 동안 사용할 수 있습니다. 감사해요

답변1

ffmpeg만으로는 2패스 Loudnorm 필터를 자동으로 실행할 수 있는 방법이 없지만 다음을 사용할 수 있습니다.ffmpeg-normalize당신을 위해 그것을 할 프로그램입니다. 당신이 언급했다는 것을 알고 있지만 동시에 비디오를 인코딩하려면(특히 두 개의 패스로) 하나의 중간 파일로 작업해야 합니다. 즉:

  • 첫 번째 실행: ffmpeg-normalize원본 비디오에서 원본 비디오 스트림을 복사합니다.
  • 두 번째 실행: 정규화된 오디오 파일 또는 원본 파일의 비디오 스트림의 x264 인코딩(멀티스레드).

당신이 달성하고 싶은 것은 ffmpeg만으로는 할 수 없습니다. 특히 여러 파일을 병렬로 처리하려는 경우 고유한 솔루션을 프로그래밍해야 합니다. 단일 ffmpeg 실행이 하나의 스레드만 사용하더라도 프로세스 속도가 확실히 빨라집니다.

출발점으로는더 간단한 Ruby 스크립트FFmpeg 저장소에 있습니다. 두 번의 Loudnorm 패스를 수행하여 첫 번째 실행의 통계를 읽습니다. 멀티스레딩을 사용하여 2패스 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 통계를 읽은 후 (예: Python의 JSON 파서 또는 또는 loudnorm같은 다른 도구 사용 ) 두 번째 단계를 실행합니다.grepawk

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 객체가 포함된 자세한 출력을 생성하며, 이는 다음을 통해 필터링됩니다.sed명령을 파이프에 저장하고 임시 파일에 저장합니다. 뒤따르는 5개의 명령 각각은 두 번째 명령에 제공될 관련 매개변수를 변수에 로드합니다.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

관련 정보