Skip to content

Releases: DatanoiseTV/tinyice

v2.6.3

19 May 19:06

Choose a tag to compare

Fix stale-burst replay, false-positive OggS page detection, and stale
Ogg state on transcoder restart. See the commits on this tag for the
full description.

v2.6.2

13 May 07:06

Choose a tag to compare

v2.6.2 - .deb + .rpm packages on every release

Adds Debian + RPM packaging via nFPM. Per-arch packages (amd64,
arm64) ship alongside the raw binaries on every tagged release.

The package creates a tinyice system user, installs a hardened
systemd unit, and MASKS the unit on install so it can't start
accidentally before the operator configures it.

No runtime / API changes.

v2.6.1 — chained Ogg-Opus + transcoder flap survival

12 May 21:02

Choose a tag to compare

Three-layer fix for the long-running auto-transcoder failure on robodj-style sources that rotate their Ogg logical stream between tracks. Pure Go end-to-end (no CGO introduced).

Fixed

  • Chained Ogg-Opus decoder. Replaced kazzmir/opus-go's PacketReader (which pins the bitstream serial on first page and errors out on every new BOS) with a small pure-Go Ogg page reader that handles RFC 3533 §3 chained logical streams transparently. Each BOS resets the per-stream state (channels, preskip) and re-initialises the decoder; audio decoding is continuous across track-boundary rotations.
  • Codec swap to libopus-transpiled, lenient with real-world packets. Initial cut used pion/opus for the codec. Strict RFC 6716 validation rejected several real-world packets per minute (code-1 even-payload, ≤120 ms duration, VBR overrun, CBR divisibility) — each reject was a silent ~20 ms gap audible as a skip. Switched to kazzmir/opus-go's codec, which is libopus transpiled to pure Go via ccgo (no cgo) and matches reference-C tolerance. pion/opus dropped from go.mod.
  • Transcoded outputs survive source flaps. HealthMonitor.check skips auto-remove for streams flagged IsTranscoded. A brief upstream disconnect (8-36 s is routine for robodj) no longer turns into an end-to-end outage with the player hitting 404 and giving up.
  • No stale audio burst on source-resume. New Stream.FlushAtHead bumps a per-stream flush generation, wakes every listener, and snaps MinListenerOffset to the current buffer head. Listeners (existing and any auto-reconnecting subscribers) skip the stale buffered MP3 from before the gap and pick up at the live edge.
  • Stalled-pump watchdog kept as a safety net (extended 8 s → 30 s) — chain rotation no longer triggers it, so it only fires on a genuine decoder hang.

Upgrade notes

Drop-in. No config changes, no public API changes.

Full changelog: https://github.com/DatanoiseTV/tinyice/blob/v2.6.1/CHANGELOG.md

v2.6.0

12 May 10:32

Choose a tag to compare

v2.6.0 — transcoder leak fix + map rewrite

Fixed:

  - Goroutine leak in transcoder retry loop. mirrorTranscodeMetadata
    was spawned with the outer transcoder ctx instead of a per-
    invocation child, so every source reconnect cycle leaked one
    mirror goroutine + its references. Production accumulated
    1052 leaked goroutines and 777 MB RSS over ~73 hours.
    Fix: per-invocation context, defer-cancel.

  - Transcoder auto-restart gap reduced from ~2 minutes to ~5 s.
    The decoder hub pump's exit didn't close the orphaned PCM
    fanout stream; subscribers stayed blocked until HealthMonitor
    swept the stale entry after 2 minutes. Pump's defer now
    RemoveStream's the PCM mount.

Changed:

  - Dashboard + kiosk maps rewritten on MapLibre GL JS pointing
    at openfreemap.org. The previous Leaflet + CARTO setup re-flew
    the camera and re-created markers on every SSE tick — never
    settled. New shared <LiveGeoMap> diffs markers in place and
    only refits the camera when the set of cities changes.

v2.5.0

09 May 09:58

Choose a tag to compare

v2.5.0 — security release

Patches a pre-auth stream-injection vulnerability in the WebRTC
source-ingest endpoint plus adjacent auth-gap and stability fixes.
See CHANGELOG.md.

Security (CVE pending):
  Missing authentication on /webrtc/source-offer let any
  network-reachable user publish arbitrary audio/video to any
  mount, replacing the legitimate broadcast. Affected:
  >= 0.8.95, <= 2.4.1. Fixed: 8067d6b. CWE-306.
  CVSS 3.1: 7.4 High (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:L).

Operators upgrading from any 0.8.x / 0.9.x / 0.10.x / 0.11.x /
2.0.x – 2.4.x release should also rotate per-mount source
passwords.

v2.4.1

08 May 12:06

Choose a tag to compare

v2.4.1 — fix listener handler stalling under load (WAL + async UA)

Recurring symptom: HTTPS goes unresponsive after burst listener
connects. Login UI hangs, /healthz times out, CLOSE_WAIT
accumulates. Journal showed /relay/history.go:126 INSERT INTO
user_agents serialising at >1 s each.

Root cause: synchronous RecordUA on the listener/source connect
hot path + SQLite default rollback-journal/full-fsync. Every
listener connect blocked the handler for the duration of a full-
file fsync; bursts queued behind the write lock; login (which
also touches the DB) waited behind them.

Fix:
  - PRAGMA journal_mode=WAL + synchronous=NORMAL + busy_timeout=5000
    on the history DB connection. Writers no longer fsync the
    whole file; per-write latency drops ~200x.
  - RecordUA now runs its INSERT in a fire-and-forget goroutine
    so the listener / source handler returns immediately.

v2.4.0

08 May 11:43

Choose a tag to compare

v2.4.0 — live listener map (CartoDB / DB-IP), quarter/year/lifetime t…

v2.3.2

08 May 11:22

Choose a tag to compare

v2.3.2 — fix recurring HTTPS hang (per-write listener deadline, per-r…

v2.3.1

07 May 15:44

Choose a tag to compare

v2.3.1 — perf microoptimisations

Broadcast() Ogg page-magic scanner: byte-by-byte loop replaced with
bytes.IndexByte('O') skip-ahead. ~16x fewer comparisons for a typical
4-8 KiB Broadcast on an Opus mount.

mirrorTranscodeMetadata: tick 2 s → 5 s. RLock acquisitions on each
input cut by 2.5x; latency for catching a track-change is unchanged
in practice (the YP / dir.xiph.org directory polls minute-cadence).

No behaviour change.

v2.3.0

07 May 15:33

Choose a tag to compare

v2.3.0 — shared per-input decoder hub

Each transcoder used to open its own PCMDecoder, so a single Opus
source feeding three auto-mp3 outputs and one opus output ran four
redundant decoders over the same input bytes.

DecoderHub keeps one decoder per input mount. The first transcoder
that wants the input triggers the pump goroutine: subscribe to
input, open decoder, broadcast PCM into an internal Stream named
<inputMount>/_pcm. Every subsequent transcoder for the same input
attaches a SubscribeInternal to that PCM stream, wraps it in a
readerDecoder shim (io.Reader + SampleRate()), and feeds its own
encoder.

Refcount-managed: the last transcoder release brings the pump down,
so the next Acquire pays a fresh decoder warm-up but idle inputs do
not hold one open.

Empirical CPU on the 3-source / 10-encoder configuration:

  v2.2.0 baseline   ~100 %
  v2.2.1            ~50  %
  v2.2.2            ~17  %
  v2.3.0            ~12  %

This is a MINOR bump rather than PATCH because the internal
relay/transcode contract changed (TranscoderManager now owns a
DecoderHub; performTranscode no longer opens PCMDecoders directly).
External API surface — config, HTTP routes, /admin/traffic, etc. —
is unchanged.