Skip to content

fix(resolve,player): play HLS (.m3u8) radio streams via ffmpeg#259

Merged
bjarneo merged 3 commits into
bjarneo:mainfrom
robertofgama:feat/hls-radio
Jun 4, 2026
Merged

fix(resolve,player): play HLS (.m3u8) radio streams via ffmpeg#259
bjarneo merged 3 commits into
bjarneo:mainfrom
robertofgama:feat/hls-radio

Conversation

@robertofgama

@robertofgama robertofgama commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

HLS radio streams (.m3u8 playlists) failed to play. resolveM3U parsed the remote .m3u8 as a track list with an empty baseDir, so the master playlist's relative variant URI (chunklist_<uuid>.m3u8) was treated as a local file and playback died with open source: open chunklist_<uuid>.m3u8: no such file or directory. Chunklist names rotate per request and segments are relative, so this can't be fixed by parsing — the robust fix is to let ffmpeg (already a cliamp dependency for AAC/Opus) handle HLS end-to-end.

Two changes:

  • resolve.resolveM3U: detect HLS playlists (#EXT-X-* tags) and return a single stream Track with the original URL instead of parsing it as a track list.
  • player: route .m3u8 stream URLs to decodeFFmpegStream (ffmpeg-by-URL) so ffmpeg resolves the relative chunklist/segment URIs and follows the live segment window. This also covers stations added via radios.toml/favorites, which already hand the player a .m3u8 URL directly.

ffmpeg is already required for AAC/Opus, so no new dependency.

Screenshots / video

N/A — no UI changes.

How to test

Unit (no network):

go test ./resolve/ ./player/

Covers HLS detection (master/media/plain), resolveM3U returning a single stream for HLS vs. N tracks for a plain playlist, and isHLS.

Live playback (needs ffmpeg + network) — e.g. a Brazilian RBS/Wowza station:

cliamp "https://liverdgaupoa.rbsdirect.com.br/primary/gaucha_rbs.sdp/playlist.m3u8"
# State: playing; the track is no longer "chunklist_..."

ffmpeg decoding proof (the exact command cliamp now runs):

ffmpeg -i "https://liverdgaupoa.rbsdirect.com.br/primary/gaucha_rbs.sdp/playlist.m3u8" \
  -t 3 -f s16le -acodec pcm_s16le -ar 44100 -ac 2 -loglevel error pipe:1 | wc -c
# => 529200  (3s x 44100 x 2ch x 2 bytes), no errors

Checklist

  • make check passes
  • docs/ and site/index.html updated for user-facing changes

Note: in-band ICY stream titles aren't surfaced for HLS (ffmpeg opens the URL directly); acceptable since HLS carries timed metadata instead.

Summary by CodeRabbit

  • New Features

    • HLS (.m3u8) playback support for master/media playlists — ffmpeg is used to fetch and play HLS streams.
    • HLS live streams expose realtime behavior for live playlists.
  • Documentation

    • Added HLS streaming guide with usage examples and notes (ffmpeg required; now-playing titles for live HLS rely on timed metadata and may not update via ICY).

HLS playlists were parsed as track lists, so the master playlist's relative chunklist_*.m3u8 URI became a bogus local-file track (open source: ... no such file or directory). Detect HLS in resolveM3U and return a single stream Track with the original URL; route .m3u8 stream URLs to ffmpeg-by-URL in the player pipeline so ffmpeg resolves relative chunklist/segment URIs and follows the live segment window.
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 28545f30-8b6a-4664-9312-eecd1959e63b

📥 Commits

Reviewing files that changed from the base of the PR and between f06969a and 24eddfe.

📒 Files selected for processing (4)
  • docs/streaming.md
  • player/pipeline.go
  • resolve/resolve.go
  • site/index.html

📝 Walkthrough

Walkthrough

Detect .m3u8/HLS by extension and playlist markers, buffer remote playlists up to 1MB to classify HLS vs plain M3U, return HLS playlists as single stream entries with realtime inference, route HLS URLs to ffmpeg decoding to preserve relative segment resolution, and add tests/docs for the behavior.

Changes

HLS Stream Support

Layer / File(s) Summary
HLS format detection helpers
player/decode.go, resolve/resolve.go
isHLS checks for .m3u8 extension; isHLSPlaylist scans playlist bytes for #EXT-X-STREAM-INF, #EXT-X-TARGETDURATION, or #EXT-X-MEDIA-SEQUENCE markers.
Bounded M3U/HLS playlist resolution
resolve/resolve.go
resolveM3U buffers remote playlist responses up to 1MB, classifies HLS playlists via marker scanning, returns a single stream entry for HLS (sets Realtime based on #EXT-X-ENDLIST) or parses plain M3U into multiple tracks; imports bytes for in-memory scanning.
HLS URL routing to ffmpeg decoder
player/pipeline.go
buildPipelineAt detects URL inputs with .m3u8 and routes them to decodeFFmpegStream, returning a pipeline backed by ffmpeg-decoded stream and preserving the original URL so ffmpeg can resolve relative chunklists/segments and follow live windows; bypasses normal open/decoder flow.
Tests and user documentation
player/decode_test.go, resolve/resolve_test.go, docs/streaming.md, site/index.html
Add TestIsHLS and HLS-related resolveM3U tests (including HLS master playlist behavior and plain M3U regression), update streaming docs with an “HLS Streams” section describing .m3u8 usage and ffmpeg requirement, and update site feature text.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding HLS (.m3u8) radio stream playback support via ffmpeg across resolve and player packages.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- hoist formatExt so the HLS branch and the format fallback share one parse
- comment that EXT-X-ENDLIST detection is conservative behind master playlists
- mention HLS streaming on the site (docs/site sync convention)
- docs style: drop em dash
@bjarneo bjarneo merged commit 213ada6 into bjarneo:main Jun 4, 2026
1 check was pending
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants