Skip to content

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 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.stream on 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 Running status 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)

Channel create — origins section 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
  • filePath in the payload uses storageKey only (no bucket prefix)

Channel create — fallback section 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.

Channel create — transcode section 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 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