github savonet/liquidsoap rolling-release-v2.5.x

pre-release6 hours ago

This release provides liquidsoap assets before they are published as a new versioned release.

You can use it to install the latest stable code before it is published and test/prepare your production environment for it.

Rolling releases can also be useful for us to quickly detect and report bugs before the final published release!

⚠️ Warning ⚠️
Assets in this release will be deleted. If you are looking for permanent links to release assets, please head over to https://github.com/savonet/liquidsoap-release-assets/releases

For more details about our release process, please checkout https://github.com/savonet/liquidsoap#release-details

2.5.0 (unreleased)

New:

  • Added getter(t) type annotation syntax for getter types (#TODO).
  • Added source.content operator returning an associative list of frame field names to their content format, track.format returning the content format of a single track, and format.description returning a typed record description of a content format.
  • Renamed internal video content type from canvas to yuv420p to better reflect the actual content. The video content remains organized as a canvas (superposition of yuv420p layers) internally. Type annotations such as source(video=canvas) must be updated to source(video=yuv420p).
  • Added subtitles support as a dedicated content type. Includes native SRT decoding,
    FFmpeg subtitle encoding/decoding (%subtitle), passthrough for bitmap subtitles
    (%subtitle.copy), callbacks (on_subtitle), transformations (subtitles.map),
    and dynamic insertion (subtitles.insert) (#4860, #4861, #4862).
  • Video dimensions (video.frame.width/height) are now automatically detected
    from the first decoded video file. This can be disabled by setting settings.video.detect_dimensions
    to false or by explicitly setting the video dimensions.
  • Added support for XML .nfo sidecar files as a metadata source (enable_nfo_metadata,
    file.nfo.metadata) (#4910).
  • Added Icecast-compatible streaming server (icecast.server) with support for source
    authentication, mount points, relay, and per-listener encoding (#4915).
  • Added dedicated encoder mode to output.harbor: mount points can now be served with
    a single shared encoder instead of per-listener encoding (#5003).
  • Rewrote JACK I/O using native OCaml bindings, removing the dependency on the bjack
    C library (#5028).
  • Added settings.clock.child.max_buffer, the amount of data an operator such as
    crossfade or stretch may buffer from its child source. Operators sharing a child
    source must consume it at converging rates; past this value, an error names the operator
    that fell behind instead of letting the buffer grow without bound (#5267).
  • Source callbacks (on_track, on_metadata, on_frame, on_connect, ...) now return a
    release method taking the callback back. A function registering on a source it is given
    keeps the callback alive for as long as that source lives, so one that runs more than once
    needs to release what it registered. Past five callbacks on the same source, a log message
    says so. Existing scripts are unaffected: the returned value is still ignorable. See the
    new source callbacks page.
  • Added source.collect_callback_releases, which runs a function and gathers the callbacks
    it registered on a given list of sources into a single release. This is what switch and
    cross use, available to scripts calling their own functions more than once.
  • Clocks created by script code running after startup are now started by the code that
    created them, through an effect handler scoped to that code, instead of a global registry
    polled after every script application.
  • Added the source(_) type: a source whose content is unknown. Any source can be used where
    one is expected, so sources of different content can be held together, for instance in a
    list, without their content types being unified into one. The converse is rejected: nothing
    can be assumed about what a source(_) streams. source and source(...) are unchanged
    and still mean a source whose content is inferred.

Changed:

  • Bindings written without let accept the same targets as let: destructuring patterns, field paths and type
    annotations. (x, y) = (1, 2), r.field = 1 and (n : int) = 2 are all valid. A leading binding inside the
    { … } function shorthand still requires let, since { x = 1 } is a record literal. An invalid left-hand side
    reports what is allowed instead of a bare syntax error.
  • String interpolation accepts any expression: "#{m["key"]}", "#{ {a = 1}.a }" and nested interpolated strings
    now work. Error positions inside #{ … } point at the offending code rather than at the start of the string.
  • http.transport.ssl and http.transport.tls: key parameter is now optional in server mode when the certificate file also contains the private key.
  • switch, fallback, rotate, random: replaced the parallel list parameters
    (transitions, transition_length, override, track_sensitive,
    replay_metadata, single, weights) with per-source composition methods
    (composition_type, track_sensitive, replay_metadata, single, weight,
    on_select, on_leave) registered on every source. Switching behavior now
    defaults automatically based on whether a source is file-based or live. See the
    new source composition page, and
    doc/content/migrating.md for the parameter-by-parameter mapping (#5074).
  • Switching now fades by default. Previously a switch that cut into a playing
    track did so abruptly; the leaving source is now faded out over up to
    settings.source.composition.max_fade seconds (default 1.), when it carries
    only PCM audio. Handoffs that happen at a track boundary are unchanged: nothing
    was interrupted, so nothing is faded. Use
    source.composition.legacy_on_select to restore the previous behavior.
  • Deprecated fallback.skip. A source with track_sensitive set to false can be cut
    into mid-track and the file composition profile skips a source it was left in the
    middle of, so fallback([main, fallback_source.{track_sensitive = getter(false)}])
    now does natively what the operator polled for on every frame.
  • Simplified cross/crossfade implementation: replaced start_duration and end_duration
    with a single unified duration parameter. Removed autocue-specific code and
    assume_autocue setting. Metadata overrides liq_cross_start_duration and
    liq_cross_end_duration replaced by liq_cross_duration. Methods start_duration()
    and end_duration() replaced by cross_duration() (#4893).
  • Add metadata from all sources/tracks in add operators (#4892).
  • Make sure script fails on on_close errors in output.file to prevent
    fatal errors from being ignored. Use reopen_on_error to ignore errors
    from the callback.
  • Allow implicit casting of an integer as a float (#2887).
  • Removed settings.decoder.ffmpeg.max_interleave_delta. The setting was never read by
    the decoder; stream interleaving is bounded by
    settings.decoder.ffmpeg.max_interleave_duration alone.
  • Overhauled the FFmpeg filter graph implementation, fixing frame loss, dropped
    track marks and a hang. A graph is now a single source whose outputs share one
    buffer and one set of track marks, so a track boundary cuts all of them, id
    on any output names the graph, and outputs consumed at diverging rates raise
    past settings.ffmpeg.filter_max_buffer.

Fixed:

  • Callbacks a script registers on the sources switch and cross hand to on_select,
    on_leave and transition functions are now released when the selection or the crossing
    ends, instead of accumulating on those sources for as long as they live.
  • An FFmpeg filter graph is no longer finished off by an input that runs dry.
    A source is unavailable for all sorts of passing reasons, a buffer filling
    up or a queue waiting on a request, and liquidsoap has no way to tell those
    from a stream that is over, so the graph treats every one of them as the end:
    it flushes the filters, hands over the tail they were holding and tears the
    graph down. What is new is that this is no longer final. avfilter cannot
    reopen a graph that has seen end of file, so when the input comes back the
    graph is built again from what the script described, and each generation is
    announced as a new stream so its timestamps line up with the last. Graphs
    holding a lookahead, loudnorm in particular, went silent within a second of
    starting behind a buffer (#3944).
  • An FFmpeg filter graph dropped the tail its filters were holding at end of
    stream: the sink never asked its duration converter for what it had left. A
    5.00s source through loudnorm produced 2.08s, and now produces 4.98s.
  • Fixed the ffmpeg decoder leaving up to ~150ms of silence after a seek: the
    packets between the seek point and the target were dropped without being
    decoded, so codecs carrying state across packets, mp3 and its bit reservoir
    in particular, started cold. This is what made a CUE playlist click between
    tracks (#3971).
  • Anything logged while evaluating the script, deprecation warnings in
    particular, was silently dropped when liquidsoap exited before reaching the
    streaming loop, e.g. on No output defined, nothing to do. (#3375).
  • Liquidsoap no longer exits when started as root: it now logs a warning
    instead. Container detection through /proc/1/cgroup does not work under
    cgroup v2, which made the check fire on plain docker run (#3406).
  • Fixed output.file and the other piped outputs ignoring the delay returned by
    reopen_on_error when the failure happened while opening the file: the output
    stays Idle in that case and retried on every streaming cycle (#2437).
  • output.file reports Unix errors, e.g. Permission denied when creating the
    destination directory, as system errors, like it already did for Sys_error
    (#2437).
  • Fixed %mp3 segfaulting on musl-based systems such as Alpine: the lame
    bindings held their 144KB output buffer on the stack, more than a musl thread
    stack can take (#3936).
  • rotate and random name the switch they build internally after themselves,
    e.g. schedule_rotate, instead of leaving it with an anonymous id (#3851).
  • Fixed clock.create documenting and reporting sync modes, "CPU" and
    "unsynced", that it does not accept. The accepted values are "auto",
    "cpu", "none" and "passive".
  • Fixed file.watch losing track of a file that gets replaced rather than
    written in place: an inotify watch follows the inode, so a writer doing the
    usual write-to-temporary-then-rename silenced the watcher for good. The
    containing directory is watched instead. playlist with reload_mode="watch"
    also waits for a burst of events to settle before reloading, so a writer that
    truncates before writing no longer triggers two racing reloads and plays
    tracks out of order (#3343).
  • Fixed HLS segment boundaries drifting away from segment_duration: a segment
    closing on a stale split position re-anchored the next boundary on it instead
    of the segment grid. The drift rate depends on the encoder's frame size, so
    variants using different codecs, e.g. HE-AAC and AAC-LC, diverged from each
    other without bound (#5319).
  • The names liquidsoap's syntactic sugar expands to are no longer writable in a script: they now start with an
    underscore followed by a digit, which the lexer does not accept as an identifier, so user code cannot shadow what
    let eval, let json.parse, let xml.parse, let yaml.parse, let sqlite.row/sqlite.query or %argsof expand
    to. _null is unchanged, being a real callable builtin.
  • Record fields are evaluated when the record is built, whether or not they are ever invoked. Previously a field of a
    record that was invoked directly, as in {a = 1, b = f()}.a, was dropped along with its effects when running a
    script normally, but not under --interactive or in the standard library, and not if the record was bound to a name
    first.
  • Inline ffmpeg.encode.* operators report unrecognized codec options, as the
    container encoder does.
  • Fixed %ffmpeg copy encoder initializing the video stream twice and setting the
    average frame rate on the one it discarded, so copied video carried no frame rate.
  • Fixed inline ffmpeg.encode.* operators losing their codec options after the
    first track: the encoder consumed them from the format's own table, which was
    then empty when the encoder was rebuilt at the next track boundary.
  • Fixed %ffmpeg video streams being printed as %%video(...).
  • Fixed FFmpeg stream descriptions listing video and subtitle streams in reverse
    order.
  • Fixed delay getting stuck after its first track and dropping the following
    track's metadata (#5282).
  • Fixed sequence dropping the first chunk of a source, along with its
    metadata, when the source it follows leaves no room in the current frame.
    This showed up as the track after a cross/crossfade transition built with
    sequence keeping the previous track's metadata until the next track
    boundary (#TODO).
  • Fixed output.file.hls terminating the process on transient filesystem
    errors. The output now supports a reopen_on_error callback (#5259).
  • Fixed output.file.hls accepting unwritable output, temporary and
    persist_at directories during initialization (#5261).
  • Fixed ID3 timed metadata missing from first mpegts HLS segment: metadata is
    now registered before the segment is opened and before data is encoded (#5084).
  • Make active stereotool really be active.. (#4882)
  • Fixed fMP4 HLS support for audio+video streams (#4841)
  • Fixed crossfade clocks inconsistencies leading to weird caching audio glitches (#4851)
  • Fixed first frame of new track getting lost when crossfading with fade.in duration is set to 0. (#4887)
  • Fixed race condition when registering harbor endpoint and mounts (#5292)
  • Fixed crash with concurrent stop/start operations (#4849)
  • Fixed metadata leak from underlying ffmpeg ogg demuxer in ffmpeg stream
    decoder (#4848)
  • Make sure output.file does not create files without data (#4899)
  • Fixed deadlock when a GC finalizer logged from inside another log call: the log
    queue no longer allocates while holding its mutex.
  • Added an override parameter to insert_metadata. When false, metadata the
    source provides itself takes precedence over the inserted one. Used by the
    composition profiles so that replaying metadata on selection never overwrites a
    fresh track's own metadata.

Don't miss a new liquidsoap release

NewReleases is sending notifications on new releases.