Switching a playlist to rtmp stream in Liquidsoap – Mikulski
Site Overlay

Switching a playlist to rtmp stream in Liquidsoap

DISCLAIMER:
I am not a programmer, but only a copy-paster-enthusiast who shares what he could figure out. Therefore, it is possible that knowledgeable experts may find some points or formulations incorrect or ridiculous.
The submission of this material assumes that you have a some level of knowledge of Liquidsoap.
Anyway, the following articles and tutorials will help you figure it out:
Creating a 24/7 stream (Liquidsoap)
Creating a 24/7 stream part 2(Liquidsoap)
Your own Restream Server(nginx_rtmp)
The syntax and methods described in this article should technically work in Liquidsoap versions 2.1.x - 2.2.x
My version at the time of publication is 2.2.3

Introduction and pre-setup

Well, in the post Creating a 24/7 stream part 2(Liquidsoap) I described the possibility of direct connection of audio streams to the stream via the input.harbor operator. However, Liquidsoap also provides a mechanism for connecting rtmp sources, for example, a stream from OBS.
In this article, I will show several examples of configuring such connections: using the built-in capabilities of Liquidsoap and by enabling the nginx-rtmp stream.
By default, rtmp connections use port 1935. Therefore, in order to avoid possible conflicts (for example, with the nginx-rtmp that will occupy it), I recommend configuring an additional port in the firewall, for example, 1969, which will be used in Liquidsoap.

input.rtmp

For clarity, let’s take a fragment of the script, based on previous articles: where we have a playlist with audio files and looped animation as a background, grouped into a single radio source:

audio = playlist(reload_mode="watch", "/home/user/radio/music")
audio = mksafe(audio)
background = single("/home/user/radio/background.gif")
radio = source.mux_video(video=background, audio)
#radio = mux_video(video=background, audio) for 2.1.x versions

Now let’s add the live source for rtmp connections here:

audio = playlist(reload_mode="watch", "/home/user/radio/music")
audio = mksafe(audio)
background = single("/home/user/radio/background.gif")
radio = source.mux_video(video=background, audio)
#radio = mux_video(video=background, audio) for 2.1.x versions

live = input.rtmp(listen=true, "rtmp://localhost:1969/live/key")

Let me explain what happens here:
The listen=true parameter means that Liquidsoap acts as an independent rtmp server and “listens” to the rtmp address specified next in order to pick up the stream from there when it connects.
In the address, the values of live and key can be substituted for any of your invented ones. You then specify this address in OBS to send a stream there, where localhost:1969 is your VPS IP and port, and key is the stream key.

Next, we combine radio and live into a stream source with a fallbackswitch so that the playlist automatically switches from playlist to live stream:

audio = playlist(reload_mode="watch", "/home/user/radio/music")
audio = mksafe(audio)
background = single("/home/user/radio/background.gif")
radio = source.mux_video(video=background, audio)
#radio = mux_video(video=background, audio) for 2.1.x versions

live = input.rtmp(listen=true, "rtmp://localhost:1969/live/key")
stream = fallback(track_sensitive=false, [live, radio])

The track_ensitive=false parameter means that the playlist will immediately switch to the rtmp source without waiting for the track to finish playing.
Set track_sensitive=true so that the sources switch only when the current track ends. But keep in mind that this can create additional load on the CPU and, accordingly, cause unwanted lags.

If you created your script based on my previous guides, then note that now you will need to specify stream in output.url() instead of radio:

audio = playlist(reload_mode="watch", "/home/user/radio/music")
audio = mksafe(audio)
background = single("/home/user/radio/background.gif")
radio = source.mux_video(video=background, audio)
#radio = mux_video(video=background, audio) for 2.1.x versions

live = input.rtmp(listen=true, "rtmp://localhost:1969/live/key")
stream = fallback(track_sensitive=false, [live, radio])

#rtmp+codec
url = "rtmp://localhost/live"
enc = %ffmpeg(format="flv",
%video(codec="libx264", width=1280, height=720, pixel_format="yuv420p",
b="750k", maxrate="750k", minrate="750k", bufsize="1500k", profile="Main", preset="veryfast", framerate=30, g=60),
%audio(codec="aac", samplerate=44100, b="128k"))

#output
output.url(fallible=true, url=url, enc, stream)

nginx-rtmp

If you use nginx-rtmp and want to take the stream from there, then there are a few nuances
[well, let’s assume that you left the default rtmp port in nginx settings – 1935]:
The listen parameter must be set to false

live = input.rtmp(listen=false, "rtmp://localhost:1935/live/key")

And in the nginx-rtmp config for the “listened” app, it is important to add the line play_restart on; so that nginx-rtmp sends additional signals about the beginning/end of the stream.
Otherwise, at the end of the live broadcast, Liquidsoap will simply hang, waiting for data from nginx.

application live {
            live on;
            record off;
            play_restart on;
            }

Calling functions

If you are a more advanced Liquidsoap user, then you will probably want to call any functions on the connect/disconnect stream.
The input.rtmp statement does not provide arguments for such purposes. However, if you dig into the source code libraries, you can understand that input.rtmp is a shortened version of the input.ffmpeg operator for convenience, where there is a richer assortment of all possible parameters.
Using the example of a small fragment with the simplest functions, it will look something like this:

def live_start()
log("LiveStream is Started!")
end

def live_stop()
log("LiveStream is Stopped!")
end

listen = true 
live = input.ffmpeg(on_connect=live_start, on_disconnect=live_stop, max_buffer=5., format="live_flv", self_sync=true, int_args=[("listen", listen ? 1 : 0)],  "rtmp://localhost:1969/live/key")

on_connect & on_disconnect – this is a call to the necessary functions.
listen now it is placed in a separate variable.
All other parameters (format, self_sync, max_buffer) are taken from the input.rtmp constructor.

0 comments
Inline Feedbacks
View all comments