Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LUFS measurement doesn't always work #4387

Open
MikaSappi opened this issue Mar 4, 2025 · 2 comments
Open

LUFS measurement doesn't always work #4387

MikaSappi opened this issue Mar 4, 2025 · 2 comments
Labels

Comments

@MikaSappi
Copy link

MikaSappi commented Mar 4, 2025

Description

I've recently experienced issues with LUFS measurement. When I think I've found a way to fix the issue, on the next restart it doesn't work anymore. Liquidsoap is able to produce the Prometheus slot for the data, but is printing NaN to this slot. I'm confident this isn't a Prometheus issue, because I'm able to print NaN also on Liquidsoap logs. I've tested all relevant measurement windows and thread.run(every=n). What's most interesting is that I don't even need to change the .liq file, just restart it and get NaN again.

curl localhost:9090/metrics | grep lufs
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  2108  100  2108    0     0   390k      0 --:--:-- --:--:-- --:--:--  411k
#HELP liquidsoap_output_lufs_5s Audio LUFS Analysis
#TYPE liquidsoap_output_lufs_5s gauge
liquidsoap_output_lufs_5s{radio="main", type="output", name="radio_prod"} Nan

Steps to reproduce

  • Use LS 2.3.1
  • Use Prometheus 1.2
  • Use this script:
#!usr/bin/liquidsoap

#use correct prometheus settings

radio = sourcexyz

# Define the metric with consistent naming
audit_lufs_metric_create =
 prometheus.gauge(
   labels=["radio", "type", "name"],
   help=
     "Audio LUFS Analysis",
   "liquidsoap_output_lufs_5s"
 )

# Create the LUFS measurement with 5s window
radio_lufs = lufs(window=5., radio)

# Assign dedicated clock
clock.assign_new(id="radio_lufs", [radio_lufs])

# Create metric with matching labels
set_metric_audio_lufs = audit_lufs_metric_create(label_values=["main", "output", "radio_prod"])

# Run measurement
thread.run(every=5., {set_metric_audio_lufs(radio_lufs.lufs())})

output.icecast(
radio,
...
)

This is the exact script that Radio France uses. I chose to use this just to see if the issue is in my implementation. The or

The actual script has monitor.liq which has the audit_lufs_metric_create() and other functions. This file is %included after creating the music sources.

  • Start liquidsoap
  • start prometheus
  • curl prometheus and see NaN

This has worked before, only once:

  • Changing prometheus labels
  • Changing window sizes
  • Changing the definition order

Expected behavior

The expected behavior is to show a LUFS measurement value.

Liquidsoap version

Liquidsoap 2.3.1+dev
Copyright (c) 2003-2024 Savonet team
Liquidsoap is open-source software, released under GNU General Public License.
See <http://liquidsoap.info> for more information.

Liquidsoap build config

* Liquidsoap version  : 2.3.1+dev

 * Compilation options
   - Release build       : false
   - Git SHA             : (none)
   - OCaml version       : 4.14.0
   - OS type             : Unix
   - Libs versions       : angstrom=0.16.1 asetmap=0.8.1 astring=0.8.5 backoff=0.1.1 base=v0.16.3 base.base_internalhash_types=v0.16.3 base.caml=v0.16.3 base.shadow_stdlib=v0.16.3 base64=3.5.1 bigarray=[distributed with Ocaml] bigstringaf=0.10.0 bytes=[distributed with OCaml 4.02 or above] camlp-streams camomile.lib=2.0 cohttp=v6.0.0 cohttp-lwt=v6.0.0 cohttp-lwt-unix=v6.0.0 conduit=7.1.0 conduit-lwt=7.1.0 conduit-lwt-unix=7.1.0 cry=1.0.3 curl=0.9.2 domain-name=0.4.1 dtools=0.4.5 dune-build-info=3.17.2 dune-private-libs.dune-section=3.17.2 dune-site=3.17.2 dune-site.private=3.17.2 duppy=0.9.4 fdkaac=0.3.3 ffmpeg-av=1.2.2 ffmpeg-avcodec=1.2.2 ffmpeg-avdevice=1.2.2 ffmpeg-avfilter=1.2.2 ffmpeg-avutil=1.2.2 ffmpeg-swresample=1.2.2 ffmpeg-swscale=1.2.2 fileutils=v0.6.6 fmt=0.9.0 gen=1.1 http=v6.0.0 http.__private__.http_bytebuffer=v6.0.0 ipaddr=5.6.0 ipaddr-sexp=5.6.0 ipaddr.unix=5.6.0 lame=0.3.7 liquidsoap-lang=2.3.1 liquidsoap-lang.console=2.3.1 liquidsoap_builtins liquidsoap_core liquidsoap_fdkaac liquidsoap_ffmpeg liquidsoap_lame liquidsoap_mad liquidsoap_ogg liquidsoap_optionals liquidsoap_opus liquidsoap_oss liquidsoap_prometheus liquidsoap_runtime liquidsoap_shine liquidsoap_vorbis logs=0.7.0 logs.fmt=0.7.0 logs.lwt=0.7.0 lwt lwt.unix macaddr=5.6.0 mad=0.5.3 magic-mime=1.3.1 mem_usage=0.1.2 menhirLib=20240715 metadata=0.3.0 mm=0.8.6 mm.audio=0.8.6 mm.base=0.8.6 mm.image=0.8.6 mm.midi=0.8.6 mm.video=0.8.6 multicore-magic=2.3.1 multicore-magic.__private__.multicore_magic_atomic_array_ocaml4=2.3.1 ocplib-endian ocplib-endian.bigstring ogg=1.0.0 ogg.decoder=1.0.0 opus=1.0.0 opus.decoder=1.0.0 ppx_compare.runtime-lib=v0.16.0 ppx_hash.runtime-lib=v0.16.0 ppx_sexp_conv.runtime-lib=v0.16.0 prometheus=1.2 prometheus-app=1.2 re=1.12.0 saturn_lockfree=0.5.0 sedlex=3.3 seq=[distributed with OCaml 4.07 or above] sexplib0=v0.16.0 shine=0.2.3 str=[distributed with Ocaml] stringext=1.6.0 threads=[distributed with Ocaml] threads.posix=[internal] unix=[distributed with Ocaml] uri=4.4.0 uri-sexp=4.4.0 uri.services=4.4.0 vorbis=1.0.0 vorbis.decoder=1.0.0 xml-light=2.5
   - architecture        : arm64
   - host                : aarch64-unknown-linux-gnu
   - target              : aarch64-unknown-linux-gnu
   - system              : linux
   - ocamlopt_cflags     : -O2 -fno-strict-aliasing -fwrapv -pthread -fPIC
   - native_c_compiler   : gcc -O2 -fno-strict-aliasing -fwrapv -pthread -fPIC -D_FILE_OFFSET_BITS=64
   - native_c_libraries  : -lm

 * Configured paths
   - mode              : default
   - standard library  : (set by dune-site)
   - scripted binaries : (set by dune-site)
   - rundir            : (set by dune-site)
   - logdir            : (set by dune-site)
   - user cache        : $HOME/.cache/liquidsoap (override with $LIQ_CACHE_USER_DIR)
   - system cache      : (set by dune-site) (override with $LIQ_CACHE_SYSTEM_DIR)
   - camomile files    : (set by dune-site)

 * Supported input formats
   - MP3               : yes
   - AAC               : no (requires faad)
   - Ffmpeg            : yes
   - Flac (native)     : no (requires flac)
   - Flac (ogg)        : no (requires ogg)
   - Opus              : yes
   - Speex             : no (requires speex)
   - Theora            : no (requires theora)
   - Vorbis            : yes
   - WAV/AIFF          : yes (native)

 * Supported output formats
   - FDK-AAC           : yes
   - FFmpeg            : yes
   - MP3               : yes
   - MP3 (fixed-point) : yes
   - Flac (native)     : no (requires flac)
   - Flac (ogg)        : no (requires ogg)
   - Opus              : yes
   - Speex             : no (requires speex)
   - Theora            : no (requires theora)
   - Vorbis            : yes
   - WAV/AIFF          : yes (native)

 * Tags
   - AAC               : no (requires faad)
   - FFmpeg            : yes
   - FLAC (native)     : no (requires flac)
   - Flac (ogg)        : no (requires ogg)
   - Native decoder    : yes
   - Vorbis            : yes

 * Input / output
   - ALSA              : no (requires alsa)
   - AO                : no (requires ao)
   - FFmpeg            : yes
   - JACK              : no (requires bjack)
   - NDI               : no (requires ctypes-foreign)
   - OSS               : yes
   - Portaudio         : no (requires portaudio)
   - Pulseaudio        : no (requires pulseaudio)
   - SRT               : no (requires srt)

 * Audio manipulation
   - FFmpeg            : yes
   - LADSPA            : no (requires ladspa)
   - Lilv              : no (requires lilv)
   - Samplerate        : no (requires samplerate)
   - SoundTouch        : no (requires soundtouch)
   - StereoTool        : no (requires ctypes-foreign)

 * Video manipulation
   - camlimages        : no (requires camlimages)
   - FFmpeg            : yes
   - frei0r            : no (requires frei0r)
   - ImageLib          : no (requires imagelib)
   - SDL               : no (requires tsdl-image & tsdl-ttf)

 * MIDI manipulation
   - DSSI              : no (requires dssi)

 * Visualization
   - GD                : no (requires gd)
   - Graphics          : no (requires graphics)
   - SDL               : no (requires tsdl-image & tsdl-ttf)

 * Additional libraries
   - FFmpeg filters    : yes
   - FFmpeg devices    : yes
   - inotify           : no (requires inotify)
   - irc               : no (requires irc-client-unix)
   - jemalloc          : no (requires jemalloc)
   - lo                : no (requires lo)
   - memtrace          : no (requires memtrace)
   - osc               : no (requires osc-unix)
   - ssl               : no (requires ssl)
   - sqlite3           : no (requires sqlite3)
   - tls               : no (requires tls-liquidsoap)
   - posix-time2       : no (requires posix)
   - windows service   : no (requires winsvc)
   - YAML support      : no (requires yaml)
   - XML playlists     : no (requires xmlplaylist)

 * Monitoring
   - Prometheus        : yes

Installation method

From OPAM

Additional Info

opam --version
2.3.0

5.15.0-133-generic #144-Ubuntu SMP Sat Feb 8 14:13:21 UTC 2025 aarch64 aarch64 aarch64 GNU/Linux

Some additional information about our troubleshooting:

We've confirmed that:

  • Audio is flowing correctly (verified through Icecast output)
  • LUFS measurement point is correct (after all processing, before outputs)
  • Metrics are being exposed correctly to Prometheus (visible but showing NaN)

We've matched configuration with another working radio station:

  • Using same metric definition
  • Using same label structure
  • Using same window size (5s)
  • Using same clock assignment

Tried various configurations:

  • Different window sizes (1s, 5s, 3600s)
  • Different delays before measurement starts
  • Different measurement approaches (recurrent thread vs regular thread)
  • Separating LUFS measurement into its own variable with dedicated clock

System-level attempts:

  • Restarted Liquidsoap
  • Restarted Prometheus
  • Cleared Prometheus data
  • Rebooted entire server

Additional notes:

  • In a previous instance, removing 3s window measurement helped when we had both 3s and 1h measurements
  • Currently only using single LUFS measurement but still getting NaN
  • No configuration changes were made when this started happening
@MikaSappi MikaSappi added the bug label Mar 4, 2025
@toots
Copy link
Member

toots commented Mar 5, 2025

Hi!

Thanks for the report.

Are you able to test with the latest main? The LUFS processing was rewritten recently to be done directly in C. In our tests, it was returning the exact same value as ffmpeg and was also saving a lot of CPU usage.

It's possible that your bug is fixed there. If not, I'd love to see if I can reproduce your use-case and fix it.

Thanks!

@MikaSappi
Copy link
Author

Hi!

Thanks for your response and letting me know. I'm not in a hurry with this one (yet), but I'll let you know once I've had a chance to test this feature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants