savonet / liquidsoap

Liquidsoap is a statically typed scripting general-purpose language with dedicated operators and backend for all thing media, streaming, file generation, automation, HTTP backend and more.
http://liquidsoap.info
GNU General Public License v2.0
1.4k stars 130 forks source link

Fallback transitions no longer work in 2.3.0 #4179

Open tatokis opened 1 week ago

tatokis commented 1 week ago

Description

Trying to modify the sources inside a transition callback function results in no audible change. Jingles won't play, and fade ins/outs are not performed. This worked in the past and has been bisected to 24084719fda3463386187f9e2b8d5bc57d9fcc96, which is the first broken commit.

Steps to reproduce

A stripped down minimal script that only does fades is as follows:

autodj = mksafe(playlist("/path/to/music"))
live = input.harbor("live", port=8001, password="1234", id="live")

def live_transition(a, b)
    add(normalize=false, [
        fade.in(duration=2., track_sensitive=false, b),
        fade.out(duration=2., track_sensitive=false, a)
    ])
end

def autodj_transition(_, b) =
    fade.in(duration=2., track_sensitive=false, b)
end

out_stream = fallback([live, autodj], transitions=[live_transition, autodj_transition], track_sensitive=false)

output.icecast(
    %mp3(bitrate=96), 
    host = icecast_host,
    port = icecast_port,
    password = icecast_pass,
    mount = "/test.mp3",
    public = false,
    out_stream
)
  1. Run the above script
  2. Connect to harbor and start streaming
  3. Wait for the harbor buffer to fill up
  4. Listen as there is no transition taking place

Expected behavior

The transitions are audible.

Liquidsoap version

Liquidsoap rolling-release-v2.3.x-15-g3cd23a1+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  : rolling-release-v2.3.x-15-g3cd23a1+dev

 * Compilation options
   - Release build       : false
   - Git SHA             : (none)
   - OCaml version       : 5.1.1
   - OS type             : Unix
   - Libs versions       : base=v0.15.1 base.base_internalhash_types=v0.15.1 base.caml=v0.15.1 base.shadow_stdlib=v0.15.1 bytes=[distributed with OCaml 4.02 or above] camlp-streams camomile.lib=2.0 cry=1.0.3 curl=0.9.2 domain_shims dtools=0.4.5 dune-build-info=3.16.0 dune-private-libs.dune-section=2.9.3 dune-site=2.9.3 duppy=0.9.4 fileutils=0.6.4 gen=1.1 lame=0.3.7 liquidsoap-lang=rolling-release-v2.3.x-15-g3cd23a1 liquidsoap-lang.console=rolling-release-v2.3.x-15-g3cd23a1 liquidsoap_builtins=rolling-release-v2.3.x-15-g3cd23a1-dirty liquidsoap_core=rolling-release-v2.3.x-15-g3cd23a1-dirty liquidsoap_lame=rolling-release-v2.3.x-15-g3cd23a1-dirty liquidsoap_ogg=rolling-release-v2.3.x-15-g3cd23a1-dirty liquidsoap_optionals=rolling-release-v2.3.x-15-g3cd23a1-dirty liquidsoap_oss=rolling-release-v2.3.x-15-g3cd23a1-dirty liquidsoap_runtime=rolling-release-v2.3.x-15-g3cd23a1-dirty liquidsoap_stdlib=rolling-release-v2.3.x-15-g3cd23a1-dirty liquidsoap_vorbis=rolling-release-v2.3.x-15-g3cd23a1-dirty magic-mime=1.3.1 mem_usage=0.1.1 menhirLib=20230608 metadata=0.3.0 mm=0.8.5 mm.audio=0.8.5 mm.base=0.8.5 mm.image=0.8.5 mm.midi=0.8.5 mm.video=0.8.5 ogg=0.7.4 ogg.decoder=0.7.4 ppx_compare.runtime-lib=v0.15.0 ppx_hash.runtime-lib=v0.15.0 ppx_sexp_conv.runtime-lib=v0.15.0 re=1.12.0 re.posix=1.12.0 saturn_lockfree=0.4.1 sedlex=3.2 seq=[distributed with OCaml 4.07 or above] sexplib0=v0.15.0 stdlib-shims=0.3.0 str=5.1.1 stringext=1.6.0 threads=5.1.1 unix=5.1.1 uri=3.0.0 vorbis=0.8.0 vorbis.decoder=0.8.0
   - architecture        : amd64
   - host                : x86_64-pc-linux-gnu
   - target              : x86_64-pc-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 -lpthread

 * 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               : no (requires mad)
   - AAC               : no (requires faad)
   - Ffmpeg            : no (requires ffmpeg)
   - Flac (native)     : no (requires flac)
   - Flac (ogg)        : no (requires ogg)
   - Opus              : no (requires opus)
   - Speex             : no (requires speex)
   - Theora            : no (requires theora)
   - Vorbis            : yes
   - WAV/AIFF          : yes (native)

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

 * Tags
   - AAC               : no (requires faad)
   - FFmpeg            : no (requires ffmpeg)
   - 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            : no (requires ffmpeg)
   - JACK              : no (requires bjack)
   - OSS               : yes
   - Portaudio         : no (requires portaudio)
   - Pulseaudio        : no (requires pulseaudio)
   - SRT               : no (requires srt)

 * Audio manipulation
   - FFmpeg            : no (requires ffmpeg)
   - 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            : no (requires ffmpeg)
   - 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    : no (requires ffmpeg)
   - FFmpeg devices    : no (requires ffmpeg)
   - inotify           : no (requires inotify)
   - irc               : no (requires irc-client-unix)
   - jemalloc          : no (requires jemalloc)
   - lastfm            : no (requires lastfm)
   - 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        : no (requires prometheus)

Installation method

From source/self-built

Additional Info

Note: While it is built with OCaml 5.1.1, this was only done due to bisecting. The issue is reproducible with 4.14.1 as well. The build is also bare bones to avoid compilation errors while bisecting.

In addition, the fade in (autodj_transition) was broken in commit 41b84f441765f321bb80d68bc49a6b32a770dfcb. After this commit, the audio cuts in with no fade, and if the live source connects the second time, Liquidsoap quits as follows:

2024/10/16 23:53:55 [main:3] Liquidsoap v2.2.0-349-g41b84f4+dev
[...]
[second connection]
2024/10/16 23:57:03 [live:3] Decoding...
2024/10/16 23:57:15 [switch.2:3] Switch to live with transition.
2024/10/16 23:57:15 [source.7:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [audio.add.2:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [fade_in.3:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [fade_scale.3:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [blank.4:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [on_metadata.4:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [on_track.4:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [replay_metadata.2:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [track_amplify.4:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [fade_out.2:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [on_frame.5:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [on_metadata.5:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [on_track.5:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [meh:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [safe_blank:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [track_amplify.5:3] Content type is {audio=pcm(stereo)}.
2024/10/16 23:57:15 [clock.main:2] Source /test_mp3 failed while streaming: File "src/core/sources/request_dynamic.ml", line 145, characters 18-24: Assertion failed!
2024/10/16 23:57:15 [clock.main:2] Raised at Request_dynamic.dynamic#generate_from_current_request in file "src/core/sources/request_dynamic.ml", line 145, characters 18-30
2024/10/16 23:57:15 [clock.main:2] Called from Request_dynamic.dynamic#generate_frame.fill in file "src/core/sources/request_dynamic.ml", line 163, characters 29-78
2024/10/16 23:57:15 [clock.main:2] Called from Request_dynamic.dynamic#generate_frame in file "src/core/sources/request_dynamic.ml", line 172, characters 16-37
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from Source.generate_from_multiple_sources#generate_frame in file "src/core/source.ml", line 927, characters 16-37
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from On_track.on_track#generate_frame in file "src/core/operators/on_track.ml", line 34, characters 16-27
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from On_metadata.on_metadata#generate_frame in file "src/core/operators/on_metadata.ml", line 34, characters 16-27
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from On_frame.on_frame#generate_frame in file "src/core/operators/on_frame.ml", line 35, characters 16-27
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from Muxer.muxer#generate_frame.(fun) in file "src/core/operators/muxer.ml", line 90, characters 22-38
2024/10/16 23:57:15 [clock.main:2] Called from Stdlib__List.fold_left in file "list.ml", line 123, characters 24-34
2024/10/16 23:57:15 [clock.main:2] Called from Muxer.muxer#generate_frame in file "src/core/operators/muxer.ml", line 88, characters 8-486
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from Add.base#generate_frames.(fun) in file "src/core/operators/add.ml", line 75, characters 50-66
2024/10/16 23:57:15 [clock.main:2] Called from Stdlib__List.fold_left in file "list.ml", line 123, characters 24-34
2024/10/16 23:57:15 [clock.main:2] Called from Add.audio_add#generate_frame in file "src/core/operators/add.ml", line 126, characters 19-39
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from Muxer.muxer#generate_frame.(fun) in file "src/core/operators/muxer.ml", line 90, characters 22-38
2024/10/16 23:57:15 [clock.main:2] Called from Stdlib__List.fold_left in file "list.ml", line 123, characters 24-34
2024/10/16 23:57:15 [clock.main:2] Called from Muxer.muxer#generate_frame in file "src/core/operators/muxer.ml", line 88, characters 8-486
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from Max_duration.max_duration#generate_frame in file "src/core/operators/max_duration.ml", line 69, characters 16-27
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from Source.generate_from_multiple_sources#generate_frame in file "src/core/source.ml", line 927, characters 16-37
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from Source.generate_from_multiple_sources#generate_frame in file "src/core/source.ml", line 927, characters 16-37
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#instrumented_generate_frame in file "src/core/source.ml", line 770, characters 45-64
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator.(fun) in file "src/core/source.ml", line 577, characters 41-73
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#peek_frame in file "src/core/source.ml", line 607, characters 12-17
2024/10/16 23:57:15 [clock.main:2] Called from Source.operator#get_partial_frame in file "src/core/source.ml", line 612, characters 20-35
2024/10/16 23:57:15 [clock.main:2] Called from Output.output#output in file "src/core/outputs/output.ml", line 174, characters 34-50
2024/10/16 23:57:15 [clock.main:2] Called from Clock.MkClock.clock#end_tick.(fun) in file "src/core/clock.ml", line 318, characters 16-24
2024/10/16 23:57:15 [clock.main:2] 
2024/10/16 23:57:15 [/test_mp3:3] Closing connection...
2024/10/16 23:57:15 [decoder:3] Unable to decode "meh/test.wav" using image decoder(s)!
2024/10/16 23:57:15 [clock:2] Error when leaving output /test_mp3: Invalid_argument("option is None")!
2024/10/16 23:57:15 [threads:3] Main loop exited
2024/10/16 23:57:15 [main:3] Shutdown started!
2024/10/16 23:57:15 [threads:3] Waiting for main threads to terminate...
2024/10/16 23:57:15 [live:2] Error while reading from client: Tutils.Exit
2024/10/16 23:57:15 [live:2] Feeding stopped: Ogg.End_of_stream.
2024/10/16 23:57:15 [clock.main:3] Streaming loop stopped.
2024/10/16 23:57:15 [threads:3] Main threads terminated.
2024/10/16 23:57:15 [threads:3] Shutting down scheduler...
2024/10/16 23:57:15 [threads:3] Scheduler shut down.
2024/10/16 23:57:15 [main:3] Cleaning downloaded files...
2024/10/16 23:57:15 [main:3] Freeing memory...

This error is not observed if autodj is a sine() instead of a playlist(), but the fade is broken in both cases.

The end intention is for this to function with autodj = input.pulseaudio(...).

toots commented 2 days ago

Hi! Thanks for this report. I will get to it asap.