aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorStefano Sabatini <stefasab@gmail.com>2023-04-05 01:10:37 +0200
committerStefano Sabatini <stefasab@gmail.com>2023-05-14 10:16:01 +0200
commit6f1368842d6d2608be32796f36b104d6ef674f15 (patch)
tree7d0bc9cf817607e14605c94462f1f170689a5cf0 /tools
parent28a73506df9c6bcfc6f1ba5b750709f15e4c5ea0 (diff)
downloadffmpeg-6f1368842d6d2608be32796f36b104d6ef674f15.tar.gz
tools/normalize: port to python3, extend syntax
Also add documentation and logging.
Diffstat (limited to 'tools')
-rwxr-xr-xtools/normalize.py106
1 files changed, 73 insertions, 33 deletions
diff --git a/tools/normalize.py b/tools/normalize.py
index 7d87c5e154..b36ff74ad0 100755
--- a/tools/normalize.py
+++ b/tools/normalize.py
@@ -1,33 +1,73 @@
-#!/usr/bin/env python2
-
-import sys, subprocess
-
-if len(sys.argv) > 2:
- ifile = sys.argv[1]
- encopt = sys.argv[2:-1]
- ofile = sys.argv[-1]
-else:
- print 'usage: %s <input> [encode_options] <output>' % sys.argv[0]
- sys.exit(1)
-
-analysis_cmd = 'ffprobe -v error -of compact=p=0:nk=1 '
-analysis_cmd += '-show_entries frame_tags=lavfi.r128.I -f lavfi '
-analysis_cmd += "amovie='%s',ebur128=metadata=1" % ifile
-try:
- probe_out = subprocess.check_output(analysis_cmd, shell=True)
-except subprocess.CalledProcessError, e:
- sys.exit(e.returncode)
-loudness = ref = -23
-for line in probe_out.splitlines():
- sline = line.rstrip()
- if sline:
- loudness = sline
-adjust = ref - float(loudness)
-if abs(adjust) < 0.0001:
- print 'No normalization needed for ' + ifile
-else:
- print "Adjust %s by %.1fdB" % (ifile, adjust)
- norm_cmd = ['ffmpeg', '-i', ifile, '-af', 'volume=%fdB' % adjust]
- norm_cmd += encopt + [ofile]
- print ' => %s' % ' '.join(norm_cmd)
- subprocess.call(norm_cmd)
+#!/usr/bin/env python3
+
+import argparse
+import logging
+import shlex
+import subprocess
+
+HELP = '''
+Normalize audio input.
+
+The command uses ffprobe to analyze an input file with the ebur128
+filter, and finally run ffmpeg to normalize the input depending on the
+computed adjustment.
+
+ffmpeg encoding arguments can be passed through the extra arguments
+after options, for example as in:
+normalize.py --input input.mp3 --output output.mp3 -- -loglevel debug -y
+'''
+
+logging.basicConfig(format='normalize|%(levelname)s> %(message)s', level=logging.INFO)
+log = logging.getLogger()
+
+
+class Formatter(
+ argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter
+):
+ pass
+
+
+def normalize():
+ parser = argparse.ArgumentParser(description=HELP, formatter_class=Formatter)
+ parser.add_argument('--input', '-i', required=True, help='specify input file')
+ parser.add_argument('--output', '-o', required=True, help='specify output file')
+ parser.add_argument('--dry-run', '-n', help='simulate commands', action='store_true')
+ parser.add_argument('encode_arguments', nargs='*', help='specify encode options used for the actual encoding')
+
+ args = parser.parse_args()
+
+ analysis_cmd = [
+ 'ffprobe', '-v', 'error', '-of', 'compact=p=0:nk=1',
+ '-show_entries', 'frame_tags=lavfi.r128.I', '-f', 'lavfi',
+ f"amovie='{args.input}',ebur128=metadata=1"
+ ]
+
+ def _run_command(cmd, dry_run=False):
+ log.info(f"Running command:\n$ {shlex.join(cmd)}")
+ if not dry_run:
+ result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE)
+ return result
+
+ result = _run_command(analysis_cmd)
+
+ loudness = ref = -23
+ for line in result.stdout.splitlines():
+ sline = line.rstrip()
+ if sline:
+ loudness = sline
+
+ adjust = ref - float(loudness)
+ if abs(adjust) < 0.0001:
+ logging.info(f"No normalization needed for '{args.input}'")
+ return
+
+ logging.info(f"Adjusting '{args.input}' by {adjust:.2f}dB...")
+ normalize_cmd = [
+ 'ffmpeg', '-i', args.input, '-af', f'volume={adjust:.2f}dB'
+ ] + args.encode_arguments + [args.output]
+
+ _run_command(normalize_cmd, args.dry_run)
+
+
+if __name__ == '__main__':
+ normalize()