Playback
The playback domain manages IPTV channels and their live stream origins on Castis Streamer nodes. Each channel maps to one or more .stream files on streamer nodes.
API base: /api/playback/
Pages: /channels, /channels/new, /channels/:id, /channels/:id/edit
Concepts
Channel — an IPTV service with a SID, name, quality, ingest config, and transcode config
Origin — a streamer node + stream ID pair hosting the channel
ingestConfig — how the streamer ingests the source (UDP URL, fallback playlist, timing)
transcodeConfig — video/audio encoding profiles applied per source
pl:/// playlist — file-loop fallback; streamer fetches files from SeaweedFS via http-remote
streamId — the .stream filename on the streamer, derived from the channel name
Testing Playback
List channels — /channels
Navigate to http://localhost:15173/channels.
- Table shows all channels with SID, quality, origin status dots, and created date
- Click a row to go to the channel preview page
- "Preview" button →
/channels/:id - "Delete" button → stops streams on all streamers and removes from DB
Channel list with origin health indicators
Create a channel — /channels/new
Navigate to http://localhost:15173/channels/new.
The form has five sections:
1. Identity
- Name — becomes the stream ID (
name.streamon the streamer) - SID — service ID integer
- Quality — FHD / HD / SD label
2. Origins
Each origin targets a separate streamer node with its own UDP ingest URL.
- Select streamer node from dropdown (populated from
/api/distribution/streamers) - Enter UDP multicast host (e.g.
239.0.0.8) and port (e.g.4444) - Click ⚡ Check signal to probe whether UDP packets are flowing on that group:port
- CoreAPI creates a temporary stream on the streamer, polls for
Runningstatus for 6s, then deletes it - Green = signal detected, Red = no signal (still OK to create — channel will wait for signal)
- Add multiple origins for redundancy (primary + failover)
Origins section with UDP probe button
3. Ingest options
- Chunk duration, fragment duration, net timeout
- Failover count — how many consecutive failures before switching source
- Backup URL — optional second UDP source
- Drop second audio track
4. Fallback playlist (optional)
Enable to add a pl:/// file-loop fallback. When the UDP source fails, the streamer loops the selected media files instead of going offline.
- Toggle on to reveal the media picker
- Search and select video files from the media domain
- Files are listed by title and thumbnail
- Selected files play in order — add multiple for a looped playlist
filePathin the payload usesstorageKeyonly (no bucket prefix)
Fallback playlist picker with media library
5. Transcode
- Drop video/audio source — re-encode rather than pass through
- Video profiles — one per ABR rung (codec, resolution, bitrate, GOP, preset)
- Click + Add profile for multiple rungs
- Apply presets (1080p 6mbps h264, 720p 3mbps h264, etc.)
- Fallback transcode — optional separate profile for
pl:///source
The Payload preview section at the bottom shows the exact JSON that will be sent to CoreAPI — useful for debugging.
Transcode profiles with preset selector
Channel preview — /channels/:id
After creating, click the channel row to view:
- Live/offline status per origin
- DASH/HLS player (Shaka Player) — click play to test the stream
- Switch between origins and protocols (DASH/HLS)
- Copy playback URLs for each origin
- Player event log — codec, quality switches, buffering events
- Stats strip — resolution, bitrate, buffer, dropped frames
Playback URL uses streamer.publicHost (not internal Docker hostname) so the browser can reach it:
http://localhost:19080/kitchen.stream/manifest.mpd ✅
http://streamer_ntb:18080/kitchen.stream/manifest.mpd ❌ (browser can't resolve Docker DNS)
Channel preview with Shaka player and stats
Edit a channel — /channels/:id/edit
Loads the existing channel config from DB into the same form as create. Change any field and save — updates DB only (does not push to streamer live). Use the sync handler (future) to push config changes to running streams.
Full Create + Test Flow
# 1. Start colorbars broadcaster (simulate UDP signal)
# Open http://localhost:9999 → create stream → start
# or via CoreAPI:
curl -X POST http://localhost:3000/api/v1/distribution/colorbars/streams \
-H "Content-Type: application/json" \
-d '{"id":"test1","multicastIp":"239.0.0.8","port":4444}'
curl -X POST http://localhost:3000/api/v1/distribution/colorbars/streams/test1/start
# 2. Create channel via UI at /channels/new
# - Name: kitchen
# - Origin: streamer-bkk, UDP 239.0.0.8:4444
# - Check signal (should detect Running)
# - Fallback: select kitchen.mp4 from media
# - Transcode: 1080p 6mbps h264 preset
# 3. Verify stream created on streamer
curl -s http://localhost:18081/api/streams/kitchen.stream | jq '{status, source}'
# 4. Test DASH playback
curl -I http://localhost:18080/kitchen.stream/manifest.mpd
# 5. Test HLS playback
curl -I http://localhost:18080/kitchen.stream/master.m3u8
# 6. Stop colorbars — stream falls over to pl:/// playlist
curl -X POST http://localhost:3000/api/v1/distribution/colorbars/streams/test1/stop
# 7. Verify fallback is playing
curl -s http://localhost:18081/api/streams/kitchen.stream | jq '{status, source}'
# source should show pl:///playlist
Key API Endpoints
GET /api/playback/channels list channels (paginated)
GET /api/playback/channels/:id get channel with origins + streamers
POST /api/playback/channels create channel → pushes streams to streamers
DELETE /api/playback/channels/:id delete channel + stop all streams
POST /api/distribution/streamers/:id/probe UDP signal probe