HLS Output
Starting with liquidsoap 1.4.0
, it is possible to send
your streams as HLS
output.
The main operator is output.file.hls
. Here’s an example
using it, courtesy of srt2hls:
s = mksafe(playlist("playlist"))
aac_lofi =
%ffmpeg(format = "mpegts", %audio(codec = "aac", channels = 2, ar = 44100))
aac_midfi =
%ffmpeg(
format = "mpegts",
%audio(codec = "aac", channels = 2, ar = 44100, b = "96k")
)
aac_hifi =
%ffmpeg(
format = "mpegts",
%audio(codec = "aac", channels = 2, ar = 44100, b = "192k")
)
streams =
"aac_lofi", aac_lofi), ("aac_midfi", aac_midfi), ("aac_hifi", aac_hifi)]
[(
def segment_name(metadata) =
timestamp = int_of_float(time())
let {stream_name, duration, position, extname} = metadata
"#{stream_name}_#{duration}_#{timestamp}_#{position}.#{extname}"
end
output.file.hls(
playlist="live.m3u8",
segment_duration=2.0,
segments=5,
segments_overhead=5,
segment_name=segment_name,
persist_at="/tmp/path/to/state.config",
"/tmp/path/to/hls/directory",
streams,
s )
Let’s see what’s important here:
streams
describes the encoded streams. It’s a list of:(stream_name, encoder)
.stream_name
is used to generate the corresponding media playlists. Encoders can be any encoder supported by liquidsoap. However, the HLS RFC limits the list of possible codecs tomp3
andaac
. Furthermore, for the best possible compatible, it is recommended to send data encapsulated in aMPEG-TS
stream. Currently, the only encoder capable of doing this in liquidsoap is%ffmpeg
.persist_at
is used to allow liquidsoap to restart while keeping the existing segments and playlists. When shutting down, liquidsoap stores the current configuration atpersist_at
and uses it to restart the HLS stream when restarting.segments
andsegments_overhead
are used to keep track of the generated segments. Each media playlist will contain a number of segments defined bysegments
and an extra set of segments, defined bysegments_overhead
, is kept past the playlist size for those listeners who are still listening on outdated segments.
There are more useful options, in particular
on_file_change
, which can be used for instance to sync up
your segments and playlists to a distant storage and hosting service
such as S3.
Liquidsoap also provides output.harbor.hls
which allows
to serve HLS streams directly from liquidsoap. Their options should be
the same as output.file.hls
, except for harbor-specifc
options port
and path
. It is not recommended
for listener-facing setup but can be useful to sync up with a caching
system such as cloudfront.
Keyframes and segment length
In codec terminology, a keyframe
can be understood as a
piece of encoded data that contains enough information to start decoding
the stream. Keyframes are very common in video codecs and can exist in
audio codecs.
In order to make sure that a HLS playlist can be decoded starting from any segment, liquidsoap tries to split segments on keyframe boundaries. When not possible, you will see a warning in the logs.
Segment split is forced when reaching the value specified by
EXT-X-TARGETDURATION
to follow the HLS specifications. For
metadata and extra tags, segment split will occur at the next
keyframe.
To make sure that all these requirements operate correctly, you should make sure to set a keyframe frequency in your encoder’s settings that generates at lease one keyframe per segment.
For instance, if your segments are at most 2s
long and
encoded using libx264
, you can use the keyint
and keyint-min
parameters, which are expressed in number of
frames. If your video frame rate is 25fps
(liquidsoap’s
default), you should have at least one keyframe every
2 * 25 = 50
frames.
%ffmpeg(
...,%video(codec="libx264",
x264opts="keyint=50:min-keyint=50")
)
Metadata
HLS outputs supports metadata in two ways:
- Using the
%ffmpeg
encoder, through atimed_id3
metadata logical stream with thempegts
format. - Through regular ID3 frames, as requested by the HLS
specifications for
adts
,mp3
,ac3
andeac3
formats with the%ffmpeg
encoder and also natively using the%mp3
,%shine
or%fdkaac
encoders. - There is currently no support for in-stream metadata for the
mp4
format.
Metadata parameters are passed through the record methods of the streams’ encoders. Here’s an example
s = mksafe(playlist("playlist"))
output.file.hls(
"/tmp/path/to/directory",
["aac", %ffmpeg(format = "adts", %audio(codec = "aac")).{id3_version=3}),
(
("ts-with-meta",
%ffmpeg(format = "mpegts", %audio(codec = "aac")).{id3_version=4}
),"ts", %ffmpeg(format = "mpegts", %audio(codec = "aac")).{id3=false}),
(
("mp3",
%ffmpeg(format = "mp3", %audio(codec = "libmp3lame")).{replay_id3=false}
)
],
s )
Parameters are:
id3
: Set tofalse
to deactivate metadata on the streams. Defaults totrue
.id3_version
: Set theid3v2
version used to export metadatareplay_id3
: By default, the latest metadata is inserted at the beginning of each segment to make sure new listeners always get the latest metadata. Set tofalse
to disable it.
Metadata for these formats are activated by default. If you are
experiencing any issues with them, you can disable them by setting
id3
to false
.
Mp4 format
mp4
container is supported by requires specific
parameters. Here’s an example that mixes aac
and
flac
audio, The parameters required for mp4
are movflags
and frag_duration
.
radio = mksafe(playlist("playlist"))
aac_lofi = %ffmpeg(format="mp4",
%audio(
codec="aac",
channels=2,
ar=44100,
b="192k"
))
flac_hifi = %ffmpeg(format="mp4",
strict="-2",
%audio(
codec="flac",
channels=2,
ar=44100
))
flac_hires = %ffmpeg(format="mp4",
strict="-2",
%audio(
codec="flac",
channels=2,
ar=48000
))
streams = [("aac_lofi", aac_lofi),
"flac_hifi", flac_hifi),
("flac_hires", flac_hires)]
(
output.file.hls(playlist="live.m3u8",
"/tmp/path/to/directory",
streams, radio)