Захват потока по HLS¶
Sapsan следует за чужим HLS-плейлистом в режиме клиента (pull): опрашивает плейлист, забирает сегменты и отдаёт кадры в тракт наравне с RTSP, SRT или MPEG-TS. Так отдаётся почти всякий агрегатор, всякая чужая CDN и всякий партнёр, которому не разрешили открыть UDP или SRT.
Настройка¶
streams:
- name: chan1
inputs:
- hls:
url: https://cdn.example.com/live/index.m3u8
| Параметр | Умолчание | Описание |
|---|---|---|
url |
— | адрес плейлиста ровно в том виде, как его записал оператор |
headers |
нет | заголовки ко всем запросам к источнику: плейлист, сегменты, ключи |
variant_mode |
single |
single — один вариант мастера, ladder — вся лесенка |
variant_bandwidth |
нет | к какому битрейту подбирать вариант (ближайший снизу); без него берётся старший по BANDWIDTH |
skew_threshold_ms |
500 |
с какого расхождения рендишенов лесенка считается несинхронной |
skew_policy |
report |
что делать при вердикте skewed: report — продолжать и показывать, fallback_to_single — откатиться на один вариант |
connect_timeout_ms |
5000 |
таймаут установления соединения |
read_timeout_ms |
15000 |
таймаут получения тела: плейлиста, сегмента, ключа |
max_body_bytes |
64 МиБ | потолок размера одного ответа |
peer_timeout_ms |
общий | сколько ждать кадров, прежде чем считать источник потерянным |
Незаданное поле означает умолчание сервера, а не «выключено».
Схему допустимо записать тремя способами, все они равнозначны:
https://host/path.m3u8— распознаётся по расширению пути, query не мешает;hls://host/path.m3u8— то же, чтоhttp://;hlss://host/path.m3u8— то же, чтоhttps://.
Как добавить источник в консоли¶
Откройте стрим, вкладка Источники, кнопка + Добавить источник, протокол — HLS. Обязательное поле одно: URL — адрес плейлиста.

Остальные поля не обязательны, пустое поле означает умолчание сервера: таймауты соединения, тела и пира, потолок размера тела, битрейт варианта.
Если источник требует токен, нажмите Добавить заголовок и заполните имя, затем значение. Строка становится настоящим заголовком в тот момент, когда у неё появляется имя: заголовок без имени сервер не примет и на провод он не уйдёт.

Флажок Принимать лесенку целиком открывает Порог расхождения и выбор Если рендишены разошлись. Флажок отдельный, а не следствие того, что источник отдал мастер: приём лесенки кратно умножает и трафик приёма, и число дорожек стрима, а на стриме с архивом во столько же раз укорачивает глубину записи.

Не забудьте Сохранить: правка входа уезжает на сервер только по кнопке.
Дальше состояние источника видно на этой же вкладке. Карточка в блоке Здоровье источников показывает вердикт лесенки и худшее расхождение:

А построчная разбивка по рендишенам — заявленное и фактическое расхождение, тренд, непарные сегменты и расхождение по ключевым кадрам — лежит ниже, в блоке Расхождение рендишенов:

Правило старта¶
Всё, что лежало в плейлисте на момент подключения, — история. Захват начинается с первого сегмента, появившегося после подключения; так же он ведёт себя и на реконнекте.
Глубины старта в настройках нет намеренно: иначе на каждом обрыве связи стрим вместо вещания разгребал бы накопленное окно, вылитое в тракт за секунды.
Аутентификация¶
Два способа, их можно сочетать:
- заголовки —
headers, напримерAuthorization: Bearer <token>; уходят с плейлистом, вариантами, init-сегментами, сегментами и ключами того же origin и не уходят на чужой origin после редиректа; - учётные данные в самом URL — обычная Basic-аутентификация средствами HTTP-клиента.
Значения заголовков в логи не попадают: печатаются только имена. Адрес входа пишется в логи целиком и в ответах управляющего API отдаётся как есть — где именно в строке адреса секрет, знает только оператор, и маскировка наугад либо оставила бы токен в query, либо срезала бы то, по чему вход опознают в логе.
Лесенка и расхождение рендишенов¶
При variant_mode: ladder захватываются все варианты мастера. Опорным берётся старший по BANDWIDTH вариант, остальные сравниваются с ним посегментно — по номеру медиа-последовательности. Вердикт виден в статистике входа, поле hls_ladder:
synced— рендишены идут вместе, переключение между ними безопасно;skewed— расхождение превысило порог либо сломана структура: переключение даст рывок или рассинхрон;unmeasurable— сравнивать не с чем, нет пар сегментов с общим номером.
Расхождение меряется двумя способами сразу. Заявленное — разница EXT-X-PROGRAM-DATE-TIME в разметке, фактическое — разница медиавремени первых кадров. Разошлись они между собой — болен не поток, а разметка, и чинить это владельцу источника.
Note
Sapsan не приводит варианты лесенки к общей шкале и вообще не чинит плохой контент: тихая правка не делает лесенку переключаемой, она только прячет дефект. Поэтому расхождение показывается числом, а решение принимает оператор.
Политика fallback_to_single откатывает захват на один вариант при вердикте skewed — для части источников это единственный рабочий режим. Откат живёт до перезапуска входа.
Шифрование¶
Поддержаны AES-128 (сегмент целиком) и SAMPLE-AES (покадрово, для H.264 и AAC). Ключ забирается тем же клиентом и с теми же заголовками, что и остальное, и кэшируется по адресу, поэтому ротация ключа посреди плейлиста работает сама собой — у нового ключа другой адрес.
Молчащий сервер ключей — это ошибка входа с адресом ключа и кодом ответа; тело сегмента при этом не качается вовсе, расшифровать его всё равно нечем.
CENC/DRM (Widevine, PlayReady, FairPlay) не поддерживается и отклоняется по имени KEYFORMAT.
Что поддержано¶
| Возможность | Состояние |
|---|---|
Медиа-плейлист: EXT-X-MEDIA-SEQUENCE, EXTINF, EXT-X-ENDLIST |
есть |
Мастер-плейлист: выбор варианта, рендишены EXT-X-MEDIA |
есть |
Сегменты fMP4 (EXT-X-MAP) и MPEG-TS |
есть |
EXT-X-BYTERANGE |
есть |
EXT-X-DISCONTINUITY, пересоздание плейлиста, отставание от окна |
есть |
EXT-X-GAP |
есть, пропуск без ошибки |
EXT-X-PROGRAM-DATE-TIME — привязка медиавремени к UTC |
есть |
Шифрование AES-128 и SAMPLE-AES (H.264, AAC) |
есть |
| Лесенка: приём всех вариантов, измерение расхождения | есть |
LL-HLS: EXT-X-PART, blocking reload |
нет, части игнорируются |
| CENC/DRM | нет |
| «Упакованный звук»: голый ADTS или ID3+AAC вместо контейнера | нет, формат называется в ошибке |
| Субтитры TTML и WebVTT-сегменты | нет, дорожка распознаётся и пропускается |
| Захват MPEG-DASH | нет |
Применение изменений¶
Перезапуска входа требует только смена url, headers и HTTP-настроек (connect_timeout_ms, read_timeout_ms, max_body_bytes): у нового источника своя нумерация дорожек, свой init и своя позиция в плейлисте. Остальное — режим варианта, порог и политика расхождения, peer_timeout_ms — применяется на лету, без разрыва вещания.
Проверка¶
Откройте http://server/streaming/v/chan1/index.m3u8 в плеере или запросите скриншот http://server/streaming/live-preview-jpeg/chan1.
Смотреть стоит не только «кадры пошли», а три вещи:
- отставание выдачи от края плейлиста — без него жалоба «поток отстаёт» неотличима от «источник отстаёт», а лечатся они в разных местах;
- пропуски сегментов и их разбивку: вымыло из окна, отменён на лету, объявлен
EXT-X-GAP. Последнее — не ошибка источника, а его честное заявление; - стык соседних фрагментов — дефект тихий: сегменты скачаны все, ошибок разбора нет, а медиа внутри них не стыкуется, и в архиве останется провал.