Резервирование источников¶
media.source.failover
У потока может быть несколько источников. Sapsan следит за активным источником и при его деградации автоматически переключается на следующий, а при восстановлении основного — возвращается на него. Эта логика называется LSI (Live Source Input).
Несколько входов¶
Входы перечисляются в порядке приоритета — первый в списке главный:
streams:
- name: tv1
inputs:
- udp:
host: 239.0.0.1
port: 5000
- tshttp:
url: http://backup-origin.example.com/tv1/mpegts
Как происходит переключение¶
Ключевые свойства алгоритма, важные для эфира:
- Источник становится активным только по готовности: он должен отдать параметры потока (MediaInfo) и подходящую стартовую точку (ключевой кадр, аудиокадр или границу фрагмента). До этого зрителям продолжает идти текущий источник.
- Возврат на основной после чистого окна: восстановившийся приоритетный вход проверяется в фоне. Для входов, которые открывает Sapsan, переключение требует подходящей стартовой точки и приёма без нарушений правил в течение
return_alive_secs. - Упавший вход переподключается с нарастающей задержкой (
reconnect_initial_delay→reconnect_delay_step→reconnect_max_delay); когда истекли все входы, ротация начинается с первого. - Источник, чей битрейт превысил
max_bitrate, принудительно останавливается. - Отдельные таймауты на отсутствие видео и аудио: пропавший звук при живом видео тоже считается деградацией.
Политика переключения¶
Секция lsi задаёт политику для потока в целом. В source_options конкретного входа можно переопределить его таймауты, ограничения битрейта и чистое окно; задержки переподключения и интервал фоновой проверки задаются только в 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
Таймауты и интервал фоновой проверки задаются в секундах, задержки переподключения — в миллисекундах. Основные параметры:
| Параметр | Описание |
|---|---|
source_timeout, video_timeout, audio_timeout |
таймауты отсутствия данных/видео/аудио |
max_bitrate |
ограничение битрейта источника |
reconnect_initial_delay, reconnect_delay_step, reconnect_max_delay |
политика переподключения (только lsi), миллисекунды |
recheck_secondary_inputs_interval |
интервал фоновой проверки неактивных входов (только lsi), секунды |
allow_if, deny_if (только source_options) |
включать/выключать вход по внешнему файлу-флагу |
allow_if/deny_if позволяют управлять входами извне без правки конфига: вход используется, только пока файл-флаг разрешает.
Состояние вторичного источника¶
Примените конфиг через central. Интервал фоновой проверки задайте явно в lsi; пример проверяет неактивные входы с интервалом 10 секунд:
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
Снимите состояние через management API central. В примере настроены два входа, поэтому inputs[1] — второй, а current показывает, кормит ли он стрим прямо сейчас:
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 — состояние последнего наблюдения, а не обещание бесшовного переключения. checked_at содержит время наблюдения RFC 3339 UTC; отсутствие поля означает, что проверки ещё не было. Значения состояния:
working— кадры идут, чистое окно без нарушений.starting— вход открыт, кадров ещё нет.degraded— кадры идут, нарушено правило качества.dead— нет кадров из-за ошибки или таймаута.unchecked— вход ещё не наблюдался.
Для причины смотрите problems, error и failure_cause. current: false само по себе не означает отказ: исправный резерв не выбран для эфира. Проверяйте давность checked_at и сравнивайте media_info обоих входов: разные кодеки или геометрия не дают основания ожидать бесшовную смену.
Время на основном и резервных входах¶
Подробную секцию input читайте прямым запросом к стримеру, который принимает tv1. Central отдаёт компактное текущее состояние и эту секцию не запрашивает у стримера:
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)}'
Эти накопительные значения считаются секундными тиками за время жизни статистики стрима. num_sec_on_secondary_input суммирует все входы после первого, а не только второй. num_sec_no_data считает секунды после таймаута отсутствия данных. Нулевые поля могут отсутствовать; // 0 допустим только внутри реально полученной секции input. Отсутствие самой секции не означает нулевое время.
Пример двух замеров:
{"t0":{"primary_s":3600,"secondary_s":120,"no_data_s":0},
"t1":{"primary_s":3640,"secondary_s":140,"no_data_s":0}}
За интервал основной вход использовался 40 с, резервные — 20 с; доля резерва среди времени с данными — 20 / (40 + 20) = 33.3%. Считайте разность последовательных снимков. Если счёт уменьшился после перезапуска или переноса стрима, начните новый интервал; не складывайте отрицательные разности.