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 128 forks source link

input.http.set_url(...) -> Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed #3338

Open oskarburman opened 1 year ago

oskarburman commented 1 year ago

Describe the bug When I change the url of a input.http with set_url(...) it sometimes can't play the new stream. In my real script it does not happen that often, but in this test it happens often.

mainStream = input.http("http://http-live.sr.se/p3-mp3-128")

def recToggle()
  mainStream.set_url("http://http-live.sr.se/p3-mp3-128")
  mainStream.stop()
  thread.run({mainStream.start()}, delay=2.0)
end

output.dummy(mainStream, fallible=true)
thread.run(recToggle, every=30.0)
2023/08/21 10:16:39 [startup:3] Loaded test-rec/test-switch.liq: 0.00s
2023/08/21 10:16:39 [video.converter:3] Using preferred video converter: ffmpeg.
2023/08/21 10:16:39 [audio.converter:3] Using samplerate converter: libsamplerate.
2023/08/21 10:16:39 [dummy:3] Content type is {audio=pcm(stereo)}.
2023/08/21 10:16:39 [video.text:3] Using sdl implementation
2023/08/21 10:16:39 [clock.main:3] Streaming loop starts in auto-sync mode
2023/08/21 10:16:39 [clock.main:3] Delegating synchronization to CPU clock
2023/08/21 10:16:39 [decoder.ffmpeg:3] Requested content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2023/08/21 10:16:39 [decoder.ffmpeg:3] FFmpeg recognizes "http://http-live.sr.se/p3-mp3-128" as audio: {codec: mp3, 48000Hz, 2 channel(s)}
2023/08/21 10:16:39 [decoder.ffmpeg:3] Decoded content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2023/08/21 10:16:39 [clock.main:3] Delegating synchronization to active sources
2023/08/21 10:17:09 [input.http:2] Feeding failed: Ffmpeg_io.Not_connected
2023/08/21 10:17:09 [dummy:3] Source failed (no more tracks) stopping output...
2023/08/21 10:17:09 [clock.main:3] Delegating synchronization to CPU clock
2023/08/21 10:17:09 [decoder.ffmpeg:3] Requested content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2023/08/21 10:17:09 [decoder.ffmpeg:3] FFmpeg recognizes "http://http-live.sr.se/p3-mp3-128" as audio: {codec: mp3, 48000Hz, 2 channel(s)}
2023/08/21 10:17:09 [decoder.ffmpeg:3] Decoded content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2023/08/21 10:17:09 [clock.main:3] Delegating synchronization to active sources
2023/08/21 10:17:39 [input.http:2] Feeding failed: Ffmpeg_io.Not_connected
2023/08/21 10:17:39 [dummy:3] Source failed (no more tracks) stopping output...
2023/08/21 10:17:39 [clock.main:3] Delegating synchronization to CPU clock
2023/08/21 10:17:39 [decoder.ffmpeg:3] Requested content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2023/08/21 10:17:39 [decoder.ffmpeg:3] FFmpeg recognizes "http://http-live.sr.se/p3-mp3-128" as audio: {codec: mp3, 48000Hz, 2 channel(s)}
2023/08/21 10:17:39 [decoder.ffmpeg:3] Decoded content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2023/08/21 10:17:39 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:39 [clock.main:3] Delegating synchronization to active sources
2023/08/21 10:17:41 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:43 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:45 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:47 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:49 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:51 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:53 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:55 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:57 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:17:59 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:01 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:03 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:05 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:07 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:09 [input.http:2] Feeding failed: Ffmpeg_io.Not_connected
2023/08/21 10:18:09 [dummy:3] Source failed (no more tracks) stopping output...
2023/08/21 10:18:09 [clock.main:3] Delegating synchronization to CPU clock
2023/08/21 10:18:09 [decoder.ffmpeg:3] Requested content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2023/08/21 10:18:09 [decoder.ffmpeg:3] FFmpeg recognizes "http://http-live.sr.se/p3-mp3-128" as audio: {codec: mp3, 48000Hz, 2 channel(s)}
2023/08/21 10:18:09 [decoder.ffmpeg:3] Decoded content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2023/08/21 10:18:09 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:09 [clock.main:3] Delegating synchronization to active sources
2023/08/21 10:18:11 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:13 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:15 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed
2023/08/21 10:18:17 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 102, characters 8-14: Assertion failed

In this log file it worked the first time but not after that.

Version details Liquidsoap 2.2.1+git@b9b936dbb Debian 12 amd64

Install method sudo apt install ./liquidsoap-b9b936d_2.2.1-debian-bookworm-1_amd64.deb

toots commented 11 months ago

Thanks for reporting. I'm looking at it right now. Due to the tricky nature of these asynchronous mechanisms, it's possible the fix will have to be for main only so we have time to test it before it comes out with a stable release..

oskarburman commented 5 months ago

Hi, I just tested on this version: Liquidsoap 2.2.5+git@0122d0be6 and it is still there.

This is more or less the same script but nicer :-)

s1 = "http://http-live.sr.se/p3-mp3-128"
s2 = "http://http-live.sr.se/p1-mp3-128"

audioStream1 = input.http(s1)

thread.run({
    audioStream1.stop()
    audioStream1.set_url(if audioStream1.url() == s1 then s2 else s1 end)
    audioStream1.start()
}, delay=20., every=20.)

output.dummy(mksafe(audioStream1))

It is in audioStream1.stop() the error comes.

2024/04/19 09:37:54 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 107, characters 8-14: Assertion failed

oskarburman commented 5 months ago

Can you think of any temporary workaround? Like would it matter if I used an external decoder or something like that?

oskarburman commented 5 months ago

I tried to wait for status "stopped" in a while loop before changing url and starting it again. It works a little better but I still get the same result sooner or later. Or the status never gets from "stopping" to "stopped".

toots commented 5 months ago

I added a rewrite of the operator's restart logic in main/2.3.x but found it too invasive to backport to the rolling release.

Any chance you could test there to see if the issue is fixed for you?

oskarburman commented 5 months ago

I would love to do that, but I have nerver successfully compiled from source. But maybe I never found the "right" instructions. :-)

toots commented 5 months ago

How do you install it otherwise?

oskarburman commented 5 months ago

I apt install the latest deb package.

toots commented 5 months ago

Ok. you can find debian packages on each github action build. As of now, the latest main build is here: https://github.com/savonet/liquidsoap/actions/runs/8826294139

Please note that main is the current development trunk. We have finished with the new feature and are in the process of stabilizing it. One thing that we are aware is increased in resource consumption that we plan on addressing very shortly.

If you're interested to be an early tester, tho, we should be able to give quick feedback and fixes on this branch.

vitoyucepi commented 5 months ago

Hi @toots, I've tested and found that switching source urls like this can sometimes not be reliable.

Version: 2.3+git@485d746

enc = %ffmpeg(format="mp3", %audio(codec="libmp3lame"))
output.icecast(enc, sine(220.), mount="m1", host="icecast")
output.icecast(enc, sine(880.), mount="m2", host="icecast")

urls=["http://icecast:8000/m1", "http://icecast:8000/m2"]
index = ref(0)
s = input.http("")
def change_source()
  url = list.nth(urls, index())
  index := (index() + 1) mod 2
  s.set_url(url)
  print("change_source #{url}")
  s.stop()
  s.start()
end

thread.run(change_source, every=20.)

output.icecast(enc, s, mount="m3", host="icecast", fallible=true)

I saw the following errors:

oskarburman commented 5 months ago

Thank you I did not know about that builder action for every commit. Very nice!

I see that you exerianced som trouble too.

I did not have time to test alot but… In my test script it only logs the asertion fail once. So I added output.icecast… and that disconnects after the first url change and never comes back.

I will test this version more on monday.

Just an additional question: Would it be possible to completely realocate the source every time I change url without loosing referenses to other parts of the script?

vitoyucepi commented 5 months ago

Hi @oskarburman, Maybe you should define all sources and switch between them? This change will make the result somewhat infallible.

s1 = input.http("http://icecast/m1")
s2 = input.http("http://icecast/m2")
# t = rotate([s1, s2])
t = switch([
  ({f(s1)}, s1),
  ({f(s2)}, s2)
])
output.icecast(enc, s, mount="m3", host="icecast", fallible=true)
oskarburman commented 5 months ago

Hi @oskarburman, Maybe you should define all sources and switch between them? This change will make the result somewhat infallible.

The switching would probably work well, but IP addresses can change and can be added. I also don't want to stream anything else than the current stream. That is to save bandwidth and for the sender to be able to tell if they are online by monitoring icecast connections.

toots commented 5 months ago

Thanks for the test @vitoyucepi! Could you try with https://github.com/savonet/liquidsoap/pull/3891 ? You won't need the explicit stop/start with it I believe.

vitoyucepi commented 5 months ago

@toots, It seems like there's no need for stop-start anymore, but reliability looks the same.

2024/04/29 17:36:59 [m3:2] Metadata update may have failed with error: 400, Bad Requ
est (HTTP/1.0)
2024/04/29 17:36:59 [m3:2] Error while sending data: could not write data to host: U
nix.Unix_error(Unix.EPIPE, "write", "")!
2024/04/29 17:36:59 [m3:3] Closing connection...
2024/04/29 17:36:59 [m3:3] Will try to reconnect in 3.00 seconds.
2024/04/29 17:37:03 [m3:3] Connecting mount m3 for source@icecast...
2024/04/29 17:37:03 [m3:3] Connection setup was successful.
2024/04/29 17:37:07 [input.http:2] Feeding failed: Ffmpeg_io.Not_connected
change_source http://icecast:8000/m1
2024/04/29 17:37:07 [input.http:2] Error while disconnecting: Avutil.Error(Container
 closed!)
2024/04/29 17:37:07 [m3:3] Source failed (no more tracks) stopping output...
2024/04/29 17:37:07 [m3:3] Closing connection...
2024/04/29 17:37:12 [decoder.ffmpeg:3] Requested content-type for "http://icecast:80
00/m1": {audio=pcm(stereo)}
2024/04/29 17:37:12 [decoder.ffmpeg:3] FFmpeg recognizes "http://icecast:8000/m1" as
 audio: {codec: mp3, 44100Hz, 2 channel(s)}
2024/04/29 17:37:12 [decoder.ffmpeg:3] Decoded content-type for "http://icecast:8000
/m1": {audio=pcm(stereo)}
2024/04/29 17:37:23 [m3:3] Connecting mount m3 for source@icecast...
2024/04/29 17:37:23 [m3:3] Connection setup was successful.
change_source http://icecast:8000/m2
2024/04/29 17:36:10 [m3:3] Source failed (no more tracks) stopping output...
2024/04/29 17:36:10 [m3:3] Closing connection...
2024/04/29 17:36:12 [decoder.ffmpeg:3] Requested content-type for "http://icecast:80
00/m2": {audio=pcm(stereo)}
2024/04/29 17:36:12 [decoder.ffmpeg:3] FFmpeg recognizes "http://icecast:8000/m2" as
 audio: {codec: mp3, 44100Hz, 2 channel(s)}
2024/04/29 17:36:12 [decoder.ffmpeg:3] Decoded content-type for "http://icecast:8000
/m2": {audio=pcm(stereo)}
2024/04/29 17:36:12 [input.http:3] Source created multiple tracks in a single frame!
 Sub-frame tracks are not supported and are merged into a single one..
2024/04/29 17:36:12 [m3:3] Connecting mount m3 for source@icecast...
2024/04/29 17:36:12 [m3:3] Connection setup was successful.

Is it possible to make set_url uninterruptible? liqduidsoap: 7dcff8841dd6a71cd255d5d7614a89d2142d1ddd

Build config ``` * Liquidsoap version : 2.3.0+dev * Compilation options - Release build : false - Git SHA : (none) - OCaml version : 5.1.1 - OS type : Unix - Libs versions : angstrom=0.16.0 asetmap=0.8.1 astring=0.8.5 base64=3.5.1 bigarray-compat=1.1.0 bigstringaf=0.9.1 bytes=[distributed with OCaml 4.02 or above] camlp-streams camomile.lib=2.0 cohttp=5.3.1 cohttp-lwt=5.3.0 cohttp-lwt-unix=5.3.0 conduit=6.2.1 conduit-lwt=6.2.1 conduit-lwt-unix=6.2.1 cry=1.0.3 ctypes=0.22.0 ctype s.stubs=0.22.0 curl=0.9.2 domain-name=0.4.0 dtools=0.4.5 dune-build-info=3.15.0 dune -private-libs.dune-section=3.15.0 dune-site=3.15.0 dune-site.private=3.15.0 duppy=0. 9.4 ffmpeg-av=1.2.0 ffmpeg-avcodec=1.2.0 ffmpeg-avdevice=1.2.0 ffmpeg-avfilter=1.2.0 ffmpeg-avutil=1.2.0 ffmpeg-swresample=1.2.0 ffmpeg-swscale=1.2.0 fileutils=0.6.4 fm t=0.9.0 gen=1.1 inotify=2.0-62-g5e58536 integers ipaddr=5.5.0 ipaddr-sexp=5.5.0 ipad dr.unix=5.5.0 jemalloc liquidsoap-lang=2.3.0 liquidsoap-lang.console=2.3.0 liquidsoa p_builtins liquidsoap_core liquidsoap_ffmpeg liquidsoap_jemalloc liquidsoap_memtrace liquidsoap_optionals liquidsoap_oss liquidsoap_posix_time liquidsoap_prometheus liq uidsoap_runtime liquidsoap_stdlib logs=0.7.0 logs.fmt=0.7.0 logs.lwt=0.7.0 lwt=5.7.0 lwt.unix=5.7.0 macaddr=5.5.0 magic-mime=1.3.1 mem_usage=0.1.1 memtrace=0.2.3 menhir Lib=20231231 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 ocplib-endian ocplib-endian.bigstring parsexp=v0.16.0 posi x-base=2.0.2 posix-time2=2.0.2 posix-time2.constants=2.0.2 posix-time2.stubs=2.0.2 p osix-time2.types=2.0.2 posix-types=2.0.2 posix-types.constants=2.0.2 ppx_sexp_conv.r untime-lib=v0.16.0 prometheus=1.2 prometheus-app=1.2 re=1.11.0 result=1.5 sedlex=3.2 seq=[distributed with OCaml 4.07 or above] sexplib=v0.16.0 sexplib0=v0.16.0 stdlib- shims=0.3.0 str=5.1.1 stringext=1.6.0 threads=5.1.1 unix=5.1.1 unix-errno=0.6.2 unix -errno.errno_bindings=0.6.2 unix-errno.errno_types=0.6.2 unix-errno.errno_types_dete cted=0.6.2 unix-errno.unix=0.6.2 uri=4.4.0 uri-sexp=4.4.0 uri.services=4.4.0 - architecture : amd64 - host : x86_64-pc-linux-musl - target : x86_64-pc-linux-musl - system : linux - ocamlopt_cflags : -O2 -fno-strict-aliasing -fwrapv -pthread -fPIC - native_c_compiler : gcc -O2 -fno-strict-aliasing -fwrapv -pthread -fPIC -D_FI LE_OFFSET_BITS=64 - native_c_libraries : -lpthread * Configured paths - mode : posix - standard library : /usr/share/liquidsoap/libs - scripted binaries : /usr/share/liquidsoap/bin - rundir : /var/run/liquidsoap - logdir : /var/log/liquidsoap - camomile files : /usr/share/liquidsoap/camomile * Supported input formats - MP3 : no (requires mad) - AAC : no (requires faad) - Ffmpeg : yes - Flac (native) : no (requires flac) - Flac (ogg) : no (requires ogg) - Opus : no (requires opus) - Speex : no (requires speex) - Theora : no (requires theora) - Vorbis : no (requires vorbis) * Supported output formats - FDK-AAC : no (requires fdkaac) - Ffmpeg : yes - MP3 : no (requires lame) - 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 : no (requires vorbis) * Tags - Taglib (ID3 tags) : no (requires taglib) - Vorbis : no (requires vorbis) * Input / output - ALSA : no (requires alsa) - AO : no (requires ao) - FFmpeg : yes - GStreamer : no (requires gstreamer) - JACK : no (requires bjack) - 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 : yes - irc : no (requires irc-client-unix) - jemalloc : yes - lastfm : no (requires lastfm) - lo : no (requires lo) - memtrace : yes - osc : no (requires osc-unix) - ssl : no (requires ssl) - sqlite3 : no (requires sqlite3) - tls : no (requires tls-liquidsoap) - posix-time2 : yes - windows service : no (requires winsvc) - YAML support : no (requires yaml) - XML playlists : no (requires xmlplaylist) * Monitoring - Prometheus : yes ```
toots commented 5 months ago

I think that you need to add a buffer if you want the switch to happen without a disconnection in your example. This is because input.http (and input.ffmpeg which it is baed on) operates without a buffer so when you switch the source, it is out of data until the new source has reconnected.

This seems to work here:

enc = %ffmpeg(format="mp3", %audio(codec="libmp3lame"))
output.icecast(enc, sine(220.), mount="m1", host="localhost")
output.icecast(enc, sine(880.), mount="m2", host="localhost")

urls=["http://localhost:8000/m1", "http://localhost:8000/m2"]
index = ref(0)

s = input.http("")
set_url = s.set_url

s = buffer(buffer=5., s)

def change_source()
  url = list.nth(urls, index())
  index := (index() + 1) mod 2
  set_url(url)
  print("change_source #{url}")
end

thread.run(change_source, every=20.)

output.icecast(enc, s, mount="m3", host="icecast", fallible=true)
vitoyucepi commented 5 months ago

@oskarburman, Could you revisit this issue and verify if the changes are sufficient, or you have any additional problems.

@toots,

add a buffer

Nah, that didn't work.

2024/05/01 16:57:24 [buffer.producer:3] Buffer emptied, start buffering...
2024/05/01 16:57:24 [m3:3] Source failed (no more tracks) stopping output...
2024/05/01 16:57:24 [m3:3] Closing connection...

And what about these errors?

  1. 2024/05/01 17:13:34 [change_source:3] switching to http://icecast:8000/m2
    2024/05/01 17:13:36 [output_m3:3] Source failed (no more tracks) stopping output...
    2024/05/01 17:13:36 [output_m3:3] Closing connection...
    2024/05/01 17:13:54 [change_source:3] switching to http://icecast:8000/m1
    2024/05/01 17:13:54 [http_input:2] Feeding failed: Ffmpeg_io.Not_connected
    2024/05/01 17:13:54 [http_input:2] Error while disconnecting: Avutil.Error(Container closed!)
    2024/05/01 17:13:54 [output_m3:3] Source failed (no more tracks) stopping output...
    2024/05/01 17:13:54 [output_m3:3] Closing connection...

    Looks like something went wrong in the second case.

  2. 2024/05/01 17:14:14 [change_source:3] switching to http://icecast:8000/m2
    2024/05/01 17:14:26 [output_m3:2] Error while sending data: could not write data to host: Unix.Unix_error(Unix.EPIPE, "write", "")!
    2024/05/01 17:14:26 [output_m3:3] Closing connection...
    2024/05/01 17:14:26 [output_m3:3] Will try to reconnect in 3.00 seconds.
    2024/05/01 17:14:30 [output_m3:3] Connecting mount m3 for source@icecast...
    2024/05/01 17:14:30 [output_m3:3] Connection setup was successful.

    I'm not sure why this is happening, but looks like an error.

  3. 2024/05/01 17:15:34 [change_source:3] switching to http://icecast:8000/m2
    2024/05/01 17:15:38 [http_input:3] Source created multiple tracks in a single frame! Sub-frame tracks are not supported and are merged into a single one..
    2024/05/01 17:15:38 [output_m3:3] Connecting mount m3 for source@icecast...
    2024/05/01 17:15:38 [output_m3:3] Connection setup was successful.

    I don't know what's happening here, but I suppose that tracks are mixed together.

Script ```ruby enc = %ffmpeg(format="mp3", %audio(codec="libmp3lame")) output.icecast(enc, sine(220.), mount="m1", host="icecast", id="output_m1") output.icecast(enc, sine(880.), mount="m2", host="icecast", id="output_m2") urls=["http://icecast:8000/m1", "http://icecast:8000/m2"] index = ref(0) s = input.http("", id="http_input") def change_source() url = list.nth(urls, index()) index := (index() + 1) mod 2 log.important("switching to #{url}", label="change_source") s.set_url(url) end thread.run(change_source, every=20.) # s = buffer(buffer=5., s) output.icecast(enc, s, mount="m3", host="icecast", fallible=true, id="output_m3") ```
oskarburman commented 4 months ago

I have not been able to run the latest code which don't need start/stop. (Have not found an artifact with that code, although the start/stop is not important for me (I can do it manually). Don't know if there is something else that I would benefit from)

I am running: Liquidsoap 2.3.0+git@24857a784

Adding a buffer makes it a lot better. At least when I switch between local streams like the sine waves. Does not work well if I use some for example Swedish Radio P1 and P3 "http://http-live.sr.se/p3-mp3-128" streams (only testing with those). I can only hear sound occasionally. Mostly silent = unable to connect to new stream.

But, if I use my own streams, created with liquidsoap from a sound card on a different computer it almost works all the time. If I reencode the Swedish Radio with liquidsoap it... well works better than direct from the source but not as good as if liquidsoap from sound card. My streams are 44.1kHz and P1 and P3 are 48kHz. I don't think that should matter though.

vitoyucepi commented 4 months ago

Unix.Unix_error(Unix.EPIPE, "write", "")!

3770

toots commented 4 months ago

Ok.

If the goal is to switch between http live sources without interruption, I don't think that setting the url is the way to go, with or without a buffer.

Http is a pretty payload/latency heavy protocol so it's hard to know how long it will take to get the next source ready and, as soon as you change the url, your current buffer stars running down.

The proper way to do it should be by toggling between two sources and making sure that the new source gets played when ready.

I think you could achieve with with a track insensitive switch like this:

# Set to `true` to switch to s1 and `false` to switch to s2
to_s1 = ref(true)
s1 = input.http(...)
s2 = input.http(...)

switch(track_sensitive=false, [
  (to_s1, s1),
  (to_s1, s2),
  ({not to_s1()}, s2),
  ({not to_s1()}, s1)
]

Then you can switch to s1 or s2 by changing this ref. Once you toggle it, the target source will start playing as soon as it's ready.

oskarburman commented 4 months ago

Hi, yes I know that is a much safer approach.

In my big script I always fade out to silence (or actually to low gain noise -> encoding complete silence has not worked well) with a switch like you proposed. And that works better than the testscript but sometimes when I stop the input.http() it goes into "[input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 114, characters 8-14: Assertion failed" and if that happens it is hard to get it back to working state again.

I need to be able to reuse input.http since I don't how many sources I need to switch between.

Could the reason it works better in the large script be because most of the time there is no consumer of input.http when I stop it? So if I set an even longer delay between "switch to silence" and "input.http.stop()", maybe it will work bettter. I will test this and report back.