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

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

Open
oskarburman opened this issue Aug 21, 2023 · 22 comments · May be fixed by #3891
Assignees

Comments

@oskarburman
Copy link

oskarburman commented Aug 21, 2023

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@b9b936d
Debian 12 amd64

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

@toots
Copy link
Member

toots commented Oct 31, 2023

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
Copy link
Author

oskarburman commented Apr 19, 2024

Hi, I just tested on this version: Liquidsoap 2.2.5+git@0122d0b 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
Copy link
Author

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

@oskarburman
Copy link
Author

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
Copy link
Member

toots commented Apr 25, 2024

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
Copy link
Author

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

@toots
Copy link
Member

toots commented Apr 25, 2024

How do you install it otherwise?

@oskarburman
Copy link
Author

I apt install the latest deb package.

@toots
Copy link
Member

toots commented Apr 26, 2024

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
Copy link
Collaborator

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:

  • [m3:2] Error while sending data: could not write data to host: Unix.Unix_error(Unix.EPIPE, "write", "")!
    Closing connection...
    [m3:3] Will try to reconnect in 3.00 seconds.
    
  • [input.http:3] Source created multiple tracks in a single frame! Sub-frame tracks are not supported and are merged into a single one..
    
  • [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 114, characters 8-14: Assertion failed
    

@oskarburman
Copy link
Author

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
Copy link
Collaborator

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
Copy link
Author

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
Copy link
Member

toots commented Apr 29, 2024

Thanks for the test @vitoyucepi! Could you try with #3891 ? You won't need the explicit stop/start with it I believe.

@vitoyucepi
Copy link
Collaborator

@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: 7dcff88

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
Copy link
Member

toots commented Apr 30, 2024

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)

@toots toots linked a pull request Apr 30, 2024 that will close this issue
@vitoyucepi
Copy link
Collaborator

@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
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
Copy link
Author

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@24857a7

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
Copy link
Collaborator

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

#3770

@toots
Copy link
Member

toots commented May 9, 2024

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
Copy link
Author

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.

@oskarburman
Copy link
Author

Hi, I don't seem to have this problem any more.
Running this script:

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))

With Liquidsoap 2.3.0+git@1dc6fac
Does produce some of this:

2024/10/08 09:46:45 [decoder.ffmpeg:3] Requested content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2024/10/08 09:46:45 [decoder.ffmpeg:3] FFmpeg recognizes "http://http-live.sr.se/p3-mp3-128" as audio: {codec: mp3, 48000Hz, 2 channel(s)}
2024/10/08 09:46:45 [decoder.ffmpeg:3] Decoded content-type for "http://http-live.sr.se/p3-mp3-128": {audio=pcm(stereo)}
2024/10/08 09:46:45 [mksafe:3] Switch to input.http with transition.
2024/10/08 09:46:45 [replay_metadata.20:3] Content type is {audio=pcm(stereo)}.
2024/10/08 09:47:05 [input.http:2] Feeding failed: Ffmpeg_io.Not_connected
2024/10/08 09:47:05 [input.http:2] Error while disconnecting: Avutil.Error(Container closed!)
2024/10/08 09:47:05 [mksafe:3] Switch to safe_blank with transition.
2024/10/08 09:47:05 [decoder.ffmpeg:3] Requested content-type for "http://http-live.sr.se/p1-mp3-128": {audio=pcm(stereo)}
2024/10/08 09:47:05 [decoder.ffmpeg:3] FFmpeg recognizes "http://http-live.sr.se/p1-mp3-128" as audio: {codec: mp3, 48000Hz, 2 channel(s)}
2024/10/08 09:47:05 [decoder.ffmpeg:3] Decoded content-type for "http://http-live.sr.se/p1-mp3-128": {audio=pcm(stereo)}
2024/10/08 09:47:05 [input.http:2] Decoding failed: File "src/core/io/ffmpeg_io.ml", line 116, characters 8-14: Assertion failed
2024/10/08 09:47:07 [decoder.ffmpeg:3] Requested content-type for "http://http-live.sr.se/p1-mp3-128": {audio=pcm(stereo)}
2024/10/08 09:47:07 [decoder.ffmpeg:3] FFmpeg recognizes "http://http-live.sr.se/p1-mp3-128" as audio: {codec: mp3, 48000Hz, 2 channel(s)}
2024/10/08 09:47:07 [decoder.ffmpeg:3] Decoded content-type for "http://http-live.sr.se/p1-mp3-128": {audio=pcm(stereo)}
2024/10/08 09:47:07 [mksafe:3] Switch to input.http with transition.

But it keeps working or recovers = Perfect!. I did see a segfault twice but that may be something unrelated.

If you ask me you can close this. But I saw that there is a related Issue, so maybe not. I just wanted to let you know where I am in this.

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

Successfully merging a pull request may close this issue.

3 participants