← Back to the blog
Guides #radio

Recipe: run a 24/7 radio station with zero transcode

A looping playlist pushed as RTMP, remuxed straight to HLS with no re-encode, playing around the clock. The passthrough path, the trade-offs it makes, and the autoDJ loop that feeds it.

3 min read
Leer en español

Why a passthrough radio

A 24/7 station is the one workload where you do not want the server transcoding: the audio (and a static cover image) is already encoded, the CPU should sit near idle, and it must never stop. StreamHub's RTMP passthrough remuxes your feed straight to HLS with ffmpeg -c copy — no re-encode, minimal CPU — so one small box can carry a station that runs forever.

Architecture in words

Passthrough is a distinct path that bypasses LiveKit: an incoming RTMP (H.264 video + AAC audio) is remuxed to HLS segments on disk and served publicly. There is no WebRTC room, no adaptive ladder, no server recording — you trade all of that for near-zero CPU and a rock-steady HLS output. The source is an "autoDJ": a looping ffmpeg push of your playlist plus a station image, which is a technique you run, not a StreamHub feature.

1. Enable passthrough on the server

Passthrough is off by default. Set two env vars and restart the core:

STREAMHUB_RTMP_PASSTHROUGH_ENABLE=on
STREAMHUB_RTMP_PASSTHROUGH_PORT=1937      # NOT LiveKit's 1935

2. Put the app in passthrough mode

Apply the preset (it sets rtmp.passthrough on and turns transcode, adaptive and recording off), or patch the config directly:

export BASE="https://your-server.example.com/api/v1"
export TOKEN="sk_...."

curl -s -X POST $BASE/apps/radio/presets/rtmp-passthrough/apply -H "Authorization: Bearer $TOKEN"
# or: curl -s -X PATCH $BASE/apps/radio/config -H "Authorization: Bearer $TOKEN" \
#       -H 'Content-Type: application/json' -d '{"rtmpPassthrough": true}'

3. Create the passthrough ingress

curl -s -X POST $BASE/apps/radio/ingress -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"inputType":"rtmp","room":"fm1","mode":"passthrough"}'

The response gives you a passthrough push URL (rtmp://your-server.example.com:1937/radio/<key>) and the public playback URLs:

hls_url:    /hls/radio/fm1/index.m3u8
player_url: /play/radio/fm1

4. Feed it with an autoDJ loop

Loop your playlist forever with ffmpeg, pairing the audio with a still cover image so the output is a valid H.264/AAC stream the remux can copy:

ffmpeg -re -stream_loop -1 -i playlist.mp3 \
  -loop 1 -i cover.png \
  -c:v libx264 -tune stillimage -pix_fmt yuv420p -r 2 \
  -c:a aac -b:a 128k -shortest -f flv \
  rtmp://your-server.example.com:1937/radio/<key>

OBS with a media source set to loop works just as well. If the source drops, the remux manager re-arms and a looping source reconnects on its own.

What you should see

The station plays at /hls/radio/fm1/index.m3u8 with roughly two segments of latency, CPU stays near idle because nothing re-encodes on the server, and the stream keeps running as long as the loop pushes.

Troubleshooting

  • 400 on ingress create — passthrough rejects transcoding, backup keys, audio-only, WHIP and URL inputs; it must be a plain RTMP (or SRT) push in mode: passthrough.
  • 503 on ingress create — the server feature is off; set STREAMHUB_RTMP_PASSTHROUGH_ENABLE=on and restart.
  • No playback — the source must already be H.264/AAC (there is no transcode safety net); the cover-image ffmpeg command above guarantees that.
  • You need WebRTC, chat or recording — those do not exist on the passthrough path. If you want them plus low CPU, use the LiveKit path with transcoding.enabled: false instead of rtmp.passthrough.

Interactive audio instead?

If you actually want a live, talking radio (a host on a mic, sub-second, with a public "listen" embed), that is the separate radio plugin — WebRTC audio with a public listen-token. Passthrough is for a continuous file-fed broadcast; the radio plugin is for a live show.

Closing

A passthrough station is about as cheap as streaming gets, and it is all yours. If you want scheduling, ad insertion, or a branded now-playing page wired into this, our team builds those around the open-source core as a consulting engagement.