Open oskarburman opened 1 year 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..
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
Can you think of any temporary workaround? Like would it matter if I used an external decoder or something like that?
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".
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?
I would love to do that, but I have nerver successfully compiled from source. But maybe I never found the "right" instructions. :-)
How do you install it otherwise?
I apt install the latest deb package.
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.
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
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?
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)
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.
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.
@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
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)
@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?
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.
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.
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.
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.
Unix.Unix_error(Unix.EPIPE, "write", "")!
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.
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.
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.
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