Skip to content

Source failover

media.source.failover

A stream can have multiple sources. Sapsan monitors the active source, automatically switches to the next one when it degrades, and switches back when the primary recovers. This logic is called LSI (Live Source Input).

Multiple inputs

Inputs are listed in priority order — the first one is the primary:

streams:
  - name: tv1
    inputs:
    - udp:
        host: 239.0.0.1
        port: 5000
    - tshttp:
        url: http://backup-origin.example.com/tv1/mpegts

How switching works

Key properties of the algorithm that matter on air:

  • A source becomes active only when ready: it must deliver stream parameters (MediaInfo) and a suitable start point (keyframe, audio frame or fragment boundary). Until then viewers keep receiving the current source.
  • Failback after a clean window: a recovered higher-priority input is probed in the background. For inputs opened by Sapsan, it must reach a suitable start point and remain free of rule violations for return_alive_secs before switching.
  • A failed input reconnects with growing delays (reconnect_initial_delay → reconnect_delay_step → reconnect_max_delay); when all inputs are exhausted, rotation wraps back to the first.
  • A source whose bitrate exceeds max_bitrate is forcibly stopped.
  • Separate no-video and no-audio timeouts: lost audio with live video also counts as degradation.

Switching policy

The lsi section sets the stream-wide policy. An input's source_options can override its timeouts, bitrate limits and clean window; reconnection delays and the background recheck interval are set only in lsi:

streams:
  - name: tv1
    inputs:
    - udp: {host: 239.0.0.1, port: 5000}
    - udp:
        host: 239.0.0.2
        port: 5000
      source_options:
        source_timeout: 10
    lsi:
      source_timeout: 5
      video_timeout: 3
      audio_timeout: 3

Timeouts and the background recheck interval are in seconds; reconnection delays are in milliseconds. Main parameters:

Parameter Description
source_timeout, video_timeout, audio_timeout no-data/no-video/no-audio timeouts
max_bitrate source bitrate limit
reconnect_initial_delay, reconnect_delay_step, reconnect_max_delay reconnection policy (lsi only), milliseconds
recheck_secondary_inputs_interval background check interval for inactive inputs (lsi only), seconds
allow_if, deny_if (source_options only) enable/disable an input via an external flag file

allow_if/deny_if let you control inputs externally without editing the config: an input is used only while the flag file allows it.

Secondary source state

Apply the configuration through central. Set the background recheck interval explicitly in lsi; this example checks inactive inputs at a 10-second interval:

streams:
- name: tv1
  inputs:
  - udp: {host: 239.0.0.1, port: 5000}
  - udp: {host: 239.0.0.2, port: 5000}
  lsi:
    source_timeout: 5
    recheck_secondary_inputs_interval: 10

Read state from the central management API. With two configured inputs, inputs[1] is the second source and current tells whether it is currently feeding the stream:

curl -fsS -u "$ADMIN_USER:$ADMIN_PASSWORD" \
  http://central:8080/central/api-v4/streams/stats/tv1 \
  | jq '{active: [.inputs[] | select(.current)], secondary: .inputs[1] | {url, current, status, problems, checked_at, error, failure_cause}}'

status is the last observed health, not a guarantee of seamless switching. checked_at is the RFC 3339 UTC observation time; an absent field means no check has occurred. Health values:

  • working — frames arriving, clean window without violations.
  • starting — input opened, no frames yet.
  • degraded — frames arriving, a quality rule is violated.
  • dead — no frames due to an error or timeout.
  • unchecked — input not observed yet.

Inspect problems, error and failure_cause for the cause. current: false alone is not a failure: a healthy backup is not selected for delivery. Check the age of checked_at and compare both inputs’ media_info: different codecs or geometry do not imply seamless switching.

Time on primary and backup inputs

Read the detailed input section directly from the streamer ingesting tv1. Central serves compact current state and does not fetch this section from the streamer:

curl -fsS -u "$ADMIN_USER:$ADMIN_PASSWORD" \
  http://streamer:8080/streamer/api-v4/streams/stats/tv1 \
  | jq '.input | {primary_s: (.num_sec_on_primary_input // 0), secondary_s: (.num_sec_on_secondary_input // 0), no_data_s: (.num_sec_no_data // 0), switches: (.input_switches // 0), retries: (.retries // 0)}'

These cumulative values use one-second ticks over the lifetime of stream statistics. num_sec_on_secondary_input combines every input after the first, not only the second. num_sec_no_data counts seconds after the no-data timeout. Zero fields may be omitted; use // 0 only inside an actual returned input section. An absent section does not mean zero time.

Example of two samples:

{"t0":{"primary_s":3600,"secondary_s":120,"no_data_s":0},
 "t1":{"primary_s":3640,"secondary_s":140,"no_data_s":0}}

During the interval, the primary was used for 40 s and backups for 20 s; the backup share of time with data is 20 / (40 + 20) = 33.3%. Subtract successive snapshots. If counters decrease after a restart or stream reassignment, start a new interval; do not add negative deltas.

Next steps