Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ This program normalizes media files to a certain loudness level using the EBU R1
- Peak normalization — Adjust audio to a specific peak level
- Selective audio stream normalization — Normalize specific audio streams or only default streams
- Skip files already at target — Avoid re-encoding files already within a threshold of the target level
- Self-documenting output — Optionally record the effective encoder settings as an `ENCODER_SETTINGS` tag
- Video file support — Process video files while preserving video streams
- Docker support — Run via Docker container
- Python API — Use programmatically in your Python projects
Expand All @@ -34,6 +35,14 @@ This program normalizes media files to a certain loudness level using the EBU R1

## 🆕 What's New

- Version 1.42.0 can optionally write an **`ENCODER_SETTINGS`** tag on each normalized audio stream via `--write-encoder-settings`. The tag holds an ffmpeg-command-equivalent string — the audio codec, bitrate, sample rate and channels, plus the normalization filter including the measured loudnorm values — so a normalized file documents how it was produced. This is a Matroska/WebM convention and has no effect with `--metadata-disable`.

Example:

```bash
ffmpeg-normalize input.wav -c:a libopus -b:a 128k --write-encoder-settings -o output.mkv
```

- Version 1.41.0 automatically picks the correct output audio codec for the output container, so you no longer need to specify `-c:a`/`--audio-codec` unless you want to override the default. PCM is chosen for containers that support it; others will use teh default that ffmpeg picks. See [the usage guide](https://slhck.info/ffmpeg-normalize/usage/file-input-output/#how-the-output-audio-codec-is-chosen) for details.

- Version 1.40.0 can optionally **skip files that are already at the target level** via `--threshold` (e.g. `--threshold 0.5`, disabled by default). Such files are copied through unchanged instead of being re-encoded. The `--print-stats` output now includes a per-file `status` (`normalized`, `skipped`, or `error`, plus an `error` message on failure), and the exit code is non-zero if any file failed to process, so a script can tell what happened to each file.
Expand Down
3 changes: 2 additions & 1 deletion completions/ffmpeg-normalize-shtab.bash
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@



_shtab_ffmpeg_normalize_option_strings=('-h' '--help' '--input-list' '-o' '--output' '-of' '--output-folder' '--keep-mtime' '-f' '--force' '-d' '--debug' '-v' '--verbose' '-q' '--quiet' '-n' '--dry-run' '-pr' '--progress' '--version' '--preset' '--list-presets' '-nt' '--normalization-type' '-t' '--target-level' '-p' '--print-stats' '--replaygain' '--batch' '--threshold' '-lrt' '--loudness-range-target' '--keep-loudness-range-target' '--keep-lra-above-loudness-range-target' '-tp' '--true-peak' '--offset' '--lower-only' '--auto-lower-loudness-target' '--dual-mono' '--dynamic' '-as' '--audio-streams' '--audio-default-only' '--keep-other-audio' '-c:a' '--audio-codec' '-b:a' '--audio-bitrate' '-ar' '--sample-rate' '-ac' '--audio-channels' '-koa' '--keep-original-audio' '--keep-bit-depth' '--no-keep-bit-depth' '-prf' '--pre-filter' '-pof' '--post-filter' '-vn' '--video-disable' '-c:v' '--video-codec' '-sn' '--subtitle-disable' '-mn' '--metadata-disable' '-cn' '--chapters-disable' '-ei' '--extra-input-options' '-e' '--extra-output-options' '-ofmt' '--output-format' '-ext' '--extension')
_shtab_ffmpeg_normalize_option_strings=('-h' '--help' '--input-list' '-o' '--output' '-of' '--output-folder' '--keep-mtime' '-f' '--force' '-d' '--debug' '-v' '--verbose' '-q' '--quiet' '-n' '--dry-run' '-pr' '--progress' '--version' '--preset' '--list-presets' '-nt' '--normalization-type' '-t' '--target-level' '-p' '--print-stats' '--replaygain' '--batch' '--threshold' '-lrt' '--loudness-range-target' '--keep-loudness-range-target' '--keep-lra-above-loudness-range-target' '-tp' '--true-peak' '--offset' '--lower-only' '--auto-lower-loudness-target' '--dual-mono' '--dynamic' '-as' '--audio-streams' '--audio-default-only' '--keep-other-audio' '-c:a' '--audio-codec' '-b:a' '--audio-bitrate' '-ar' '--sample-rate' '-ac' '--audio-channels' '-koa' '--keep-original-audio' '--keep-bit-depth' '--no-keep-bit-depth' '-prf' '--pre-filter' '-pof' '--post-filter' '-vn' '--video-disable' '-c:v' '--video-codec' '-sn' '--subtitle-disable' '-mn' '--metadata-disable' '-cn' '--chapters-disable' '--write-encoder-settings' '-ei' '--extra-input-options' '-e' '--extra-output-options' '-ofmt' '--output-format' '-ext' '--extension')



Expand Down Expand Up @@ -53,6 +53,7 @@ _shtab_ffmpeg_normalize__mn_nargs=0
_shtab_ffmpeg_normalize___metadata_disable_nargs=0
_shtab_ffmpeg_normalize__cn_nargs=0
_shtab_ffmpeg_normalize___chapters_disable_nargs=0
_shtab_ffmpeg_normalize___write_encoder_settings_nargs=0


# $1=COMP_WORDS[1]
Expand Down
10 changes: 10 additions & 0 deletions completions/ffmpeg-normalize-shtab.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ Will attempt to copy video codec by default.
{-sn,--subtitle-disable}"[Do not write subtitle streams to output]"
{-mn,--metadata-disable}"[Do not write metadata to output]"
{-cn,--chapters-disable}"[Do not write chapters to output]"
"--write-encoder-settings[Write the effective encoder settings as an ENCODER_SETTINGS tag on each
normalized audio stream.

The tag holds an ffmpeg-command-equivalent string with the audio codec,
the bitrate, sample rate and channel count (when set), plus the
normalization filter including the measured loudnorm values.

This is a Matroska\/WebM tag convention\; other containers may store it
differently. Has no effect together with \`--metadata-disable\`.
]"
{-ei,--extra-input-options}"[Extra input options list.

A list of extra ffmpeg command line arguments valid for the input,
Expand Down
1 change: 1 addition & 0 deletions completions/ffmpeg-normalize.bash
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ _ffmpeg_normalize()
-sn --subtitle-disable \
-mn --metadata-disable \
-cn --chapters-disable \
--write-encoder-settings \
-ei --extra-input-options \
-e --extra-output-options \
-ofmt --output-format \
Expand Down
1 change: 1 addition & 0 deletions completions/ffmpeg-normalize.zsh
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ _ffmpeg_normalize() {
'(-sn --subtitle-disable)'{-sn,--subtitle-disable}'[Disable subtitles]'
'(-mn --metadata-disable)'{-mn,--metadata-disable}'[Disable metadata]'
'(-cn --chapters-disable)'{-cn,--chapters-disable}'[Disable chapters]'
'--write-encoder-settings[Write effective encoder settings as an ENCODER_SETTINGS stream tag]'

# Format Options
'(-ei --extra-input-options)'{-ei,--extra-input-options}'[Extra input options]:options:'
Expand Down
9 changes: 9 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Batch processing of several input files is possible, including video files.
- **Selective audio stream normalization** — Normalize specific audio streams or only default streams
- **Skip files already at target** — Avoid re-encoding files already within a threshold of the target level
- **Per-file outcome reporting** — `status` field in `--print-stats` plus exit codes for scripting
- **Self-documenting output** — Optionally record the effective encoder settings as an `ENCODER_SETTINGS` tag
- **Video file support** — Process video files while preserving video streams
- **Docker support** — Run via Docker container
- **Python API** — Use programmatically in your Python projects
Expand All @@ -33,6 +34,14 @@ Batch processing of several input files is possible, including video files.

## 🆕 What's New

- Version 1.42.0 can optionally write an **`ENCODER_SETTINGS`** tag on each normalized audio stream via `--write-encoder-settings`. The tag holds an ffmpeg-command-equivalent string — the audio codec, bitrate, sample rate and channels, plus the normalization filter including the measured loudnorm values — so a normalized file documents how it was produced. This is a Matroska/WebM convention and has no effect with `--metadata-disable`.

Example:

```bash
ffmpeg-normalize input.wav -c:a libopus -b:a 128k --write-encoder-settings -o output.mkv
```

- Version 1.41.0 automatically picks the correct output audio codec for the output container, so you no longer need to specify `-c:a`/`--audio-codec` unless you want to override the default. PCM is chosen for containers that support it; others will use teh default that ffmpeg picks. See [the usage guide](https://slhck.info/ffmpeg-normalize/usage/file-input-output/#how-the-output-audio-codec-is-chosen) for details.

- Version 1.40.0 can optionally **skip files that are already at the target level** via `--threshold` (e.g. `--threshold 0.5`, disabled by default). Such files are copied through unchanged instead of being re-encoded. The `--print-stats` output now includes a per-file `status` (`normalized`, `skipped`, or `error`, plus an `error` message on failure), and the exit code is non-zero if any file failed to process, so a script can tell what happened to each file.
Expand Down
12 changes: 12 additions & 0 deletions docs/usage/cli-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,18 @@ Do not write metadata to output

Do not write chapters to output

### `--write-encoder-settings`

Write the effective encoder settings as an `ENCODER_SETTINGS` tag on each normalized audio stream.

The tag holds an ffmpeg-command-equivalent string with the audio codec, the bitrate, sample rate and channel count (when set), plus the normalization filter including the measured loudnorm values, for example:

```
-c:a libopus -b:a 128000 -af loudnorm=i=-23.0:lra=7.0:tp=-2.0:offset=-0.1:measured_i=-19.4:measured_lra=6.1:measured_tp=-3.2:measured_thresh=-29.6:linear=true
```

This is a Matroska/WebM tag convention; other containers may store it differently. It has no effect together with `--metadata-disable`.

## Input/Output Format

### `-ei EXTRA_INPUT_OPTIONS, --extra-input-options EXTRA_INPUT_OPTIONS`
Expand Down
18 changes: 18 additions & 0 deletions src/ffmpeg_normalize/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,23 @@ def create_parser() -> argparse.ArgumentParser:
action="store_true",
help="Do not write chapters to output",
)
group_vcodec.add_argument(
"--write-encoder-settings",
action="store_true",
help=textwrap.dedent(
"""\
Write the effective encoder settings as an ENCODER_SETTINGS tag on each
normalized audio stream.

The tag holds an ffmpeg-command-equivalent string with the audio codec,
the bitrate, sample rate and channel count (when set), plus the
normalization filter including the measured loudnorm values.

This is a Matroska/WebM tag convention; other containers may store it
differently. Has no effect together with `--metadata-disable`.
"""
),
)

group_format = parser.add_argument_group("Input/Output options")
group_format.add_argument(
Expand Down Expand Up @@ -744,6 +761,7 @@ def _split_options(opts: str) -> list[str]:
subtitle_disable=cli_args.subtitle_disable,
metadata_disable=cli_args.metadata_disable,
chapters_disable=cli_args.chapters_disable,
write_encoder_settings=cli_args.write_encoder_settings,
extra_input_options=extra_input_options,
extra_output_options=extra_output_options,
output_format=cli_args.output_format,
Expand Down
10 changes: 10 additions & 0 deletions src/ffmpeg_normalize/_ffmpeg_normalize.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class FFmpegNormalize:
subtitle_disable (bool, optional): Disable subtitles. Defaults to False.
metadata_disable (bool, optional): Disable metadata. Defaults to False.
chapters_disable (bool, optional): Disable chapters. Defaults to False.
write_encoder_settings (bool, optional): Write the effective encoder settings (codec, bitrate, sample rate, channels and the normalization filter including the measured loudnorm values) as an ENCODER_SETTINGS tag on each normalized audio stream. Defaults to False.
extra_input_options (list, optional): Extra input options. Defaults to None.
extra_output_options (list, optional): Extra output options. Defaults to None.
output_format (str, optional): Output format. Defaults to None.
Expand Down Expand Up @@ -129,6 +130,7 @@ class FFmpegNormalize:
"subtitle_disable": False,
"metadata_disable": False,
"chapters_disable": False,
"write_encoder_settings": False,
"extra_input_options": None,
"extra_output_options": None,
"output_format": None,
Expand Down Expand Up @@ -173,6 +175,7 @@ def __init__(
subtitle_disable: bool = False,
metadata_disable: bool = False,
chapters_disable: bool = False,
write_encoder_settings: bool = False,
extra_input_options: list[str] | None = None,
extra_output_options: list[str] | None = None,
output_format: str | None = None,
Expand Down Expand Up @@ -257,6 +260,13 @@ def __init__(
self.subtitle_disable = subtitle_disable
self.metadata_disable = metadata_disable
self.chapters_disable = chapters_disable
self.write_encoder_settings = write_encoder_settings

if self.write_encoder_settings and self.metadata_disable:
_logger.warning(
"write_encoder_settings has no effect together with metadata_disable; "
"no metadata will be written to the output."
)

self.extra_input_options = extra_input_options
self.extra_output_options = extra_output_options
Expand Down
Loading
Loading