OTT сервис¶
Выдает потоки по протоколам на базе HTTP - HLS, MPEG-DASH (с версии 1.12) и MPEG-TS over HTTP. Поддерживается HTTPS (SSL). Выдача включается на вкладке OTT настроек Stream.
URL для подключения имеют формат:
http://host:port/http/stream/login/password - авторизация по логину и паролю
http://host:port/http/stream/login - авторизация по логину (токен)
http://host:port/http/stream/ - авторизация по IP
host и port - задаются в настройках http server.
stream - ID стрима. Не путать с порядковым номером в списке стримов. ID отображается вверху страницы статистики стрима и в колонке ID списка стримов, ID задается при создании стрима и никогда не меняется.
Аналогично для HLS и DASH:
На странице статистики стрима отображаются URL подключенных протоколов (в виде шаблона) и текущий статус их работы. Неавторизированный доступ запрещен, клиенты должны быть прописаны в Peers.
Для HLS и HLS в URL доступны дополнительные параметры (опционально):
[URL]?a=1&s=40&m=40&v=5
a: 1 - абсолютный путь в плейлисте (по умолчанию), 0 - относительный путь.
s: продолжительность динамического плейлиста (сек), по умолчанию 40 сек.
m: минимальная продолжительность динамического плейлиста (сек), по умолчанию 40 сек. Максимальный размер динамического плейлиста 60 сек. Если текущий размер буфера чанков менее минимального размера указанного в запросе, то будет выдана ошибка 404. Это сделано для того, чтобы HLS стартовал с заполненного буфера чанков на сервере.
v: версия HLS протокола, выдаваемого в плейлисте. По умолчанию 5. Смена версии может потребоваться для некоторых HLS клиентов.
Для совместимости с некоторыми HLS клиентами в URL может быть добавлено имя файла index.m3u8, например http://host:port/hls/stream/login/password/index.m3u8.
Имеется 2 режима работы HLS сервера - Peer mode и OTT mode.
Peer mode - режим с простой разбивкой сегментов (чанков). Рекомендуется для пиринга (дистрибьюции) потоками.
OTT mode - режим с для OTT вещания оптимизированной разбивкой сегментов для быстрого старта проигрывателей. В этом режиме нагрузка на CPU больше, рекомендуется для вещания.
Для HTTP сервера может быть включен SSL (HTTPS), это делается в настройках сервера.
Chunk Min Interval и Chunk Max Interval
В режиме OTT делается анализ потока на PAT/PMT/SPS/PPS/IFrame и чанки нарезаются по критерию быстрого старта проигрывателей. Анализ начинается с min interval и если по какой-то причине данные не найдены, то чанк принудительно нарезается по max interval.
HLS Adaptive Multistream
С версии 1.10 добавлена поддержка HLS Adaptive Multistream и с версии 1.12 DASH Adaptive Multistream
Для адаптивных потоков настраивается отдельный HLS плейлист. Для этого надо:
У стримов, которые будут включены в адаптивный плейлист, включить HLS с OTT Mode.
В главном меню появится раздел адаптивных потоков. В нем надо добавить поток, где прописать все потоки, которые должны быть добавлены в этот плейлист.
У потоков может быть задан параметр битрейт. По умочанию он 0, что означает что битрейт берется от измеренного значения. Иначе его можно задать явно.
Для адаптивных плейлистов будет другой URL:
У пиров (клиентов) может быть назначено ограничение доступа к адаптивным потокам, также как к обычным. Разрешение для адаптивного потока включает разрешение ко всем потокам, которые входят в него.
Модель кеширования OTT HLS и DASH.¶
Сервер формирует ответы трёх категорий, различающихся сроком жизни содержимого и пригодностью для кеширования промежуточными узлами (reverse proxy, CDN, клиентский кеш).
1. Модель кеширования¶
1.1. Ресурсы и HTTP-заголовки¶
Ресурс |
URL |
Content-Type |
Cache-Control |
|---|---|---|---|
TS-сегмент |
|
|
|
DASH MPD |
|
|
|
HLS master |
|
|
|
HLS media |
|
|
|
302 Redirect |
|
— |
|
Raw TS |
|
|
не задан; не кешируется |
1.2. Характеристики TS-сегментов¶
Идентификатор keyID формируется как CRC64(startTime || streamID) и
глобально уникален. URL сегмента адресует неизменное содержимое — при
повторных запросах того же URL возвращается идентичный байтовый поток
(пока сегмент остаётся в пределах скользящего окна).
Директива immutable подавляет условную ревалидацию клиентом
(If-None-Match, If-Modified-Since). Значение max-age=60
обеспечивает совместимость с типичным timeShiftBufferDepth=40s.
1.3. Характеристики манифестов¶
max-age=1 ограничивает верхнюю границу устаревания содержимого в кеше
одной секундой. При совместном использовании с proxy_cache_lock on
(nginx) всплески запросов к манифесту коалесцируются в один запрос на
origin в секунду.
1.4. Вариативность содержимого¶
При absPath=0 (значение по умолчанию, URL параметра «a» нет) манифесты HLS media и DASH MPD
не содержат идентификатора сессии в теле. Содержимое манифеста идентично
между сессиями, принадлежащими одной (stream, param)-комбинации. Это
позволяет reverse-proxy кешу переиспользовать запись между сессиями при
нормализации ключа кеша.
При absPath=1 (URL параметр «a=1») в теле манифеста присутствуют абсолютные URL, включающие
схему, хост и идентификатор сессии. Содержимое становится специфичным
для сессии, возможность кросс-сессионного переиспользования кеша
отсутствует.
2. Поведение клиентов¶
Клиент |
URL обновления манифеста |
Воздействие на количество сессий |
|---|---|---|
VLC 3.x HLS |
|
Одна сессия на сеанс воспроизведения |
VLC 3.x DASH |
|
Обрабатывается session reuse (см. 3.3) |
ffmpeg 5.x HLS |
|
Одна сессия на сеанс воспроизведения |
ffmpeg 5.x DASH |
|
Обрабатывается session reuse (см. 3.3) |
dash.js, hls.js |
|
Одна сессия на сеанс воспроизведения |
3. Специальные механизмы¶
3.1. HTTP 302 Redirect для DASH¶
Запрос вида /dash/<stream>/<login>/<pass>/index.mpd возвращает ответ
302 Found с заголовком Location: /h<sess>/index.mpd. Тело ответа
пустое. Авторизация и выделение сессии выполняются на этапе обработки
редиректа.
Клиенты, поддерживающие кеширование редиректа, обращаются к session URL напрямую в последующих запросах. Клиенты, не поддерживающие, повторяют запрос редиректа. Стоимость повторной обработки редиректа ограничена проверкой аутентификации и операциями session reuse.
3.2. Session reuse для DASH¶
При обработке запроса /dash/.../index.mpd, выполняемого под login-id
L для stream-id S и флага adaptive=A, при наличии в _ottClientList
существующей DASH-сессии с теми же (L, S, A) возвращается её sessID.
Новая сессия не создаётся, слот maxConn не расходуется.
Применяется только к DASH. Для HLS отдельный механизм reuse не требуется:
HLS-клиенты обновляют media playlist через session URL и не инициируют
applyNewOTTSess на каждом refresh.
3.3. Переиспользование сегментов между сессиями¶
Путь /h<sess>/<keyID>.ts не зависит от sess при разрешении keyID в
содержимое: keyID однозначно идентифицирует сегмент в рамках
зарегистрированных ChunkList (см. _ottStreamList). Nginx с
нормализованным cache key (обрезающим префикс /h<sess>/) обслуживает
все запросы одного keyID из единственной записи кеша.
4. Параметры запроса¶
Параметр |
Значение по умолчанию |
Влияние |
|---|---|---|
|
|
|
|
|
|
|
|
Минимальная длина окна для выдачи манифеста |
|
|
|
Изменение параметра через query string обновляет сохранённые в сессии
значения при следующем applyNewOTTSess-вызове.
5. Нагрузочные характеристики¶
Нагрузка на origin масштабируется с числом одновременно наблюдаемых различных стримов. Увеличение числа клиентов, наблюдающих один и тот же стрим, не увеличивает количество запросов к origin при наличии reverse-proxy кеша и нормализованного cache key.
Сценарий |
Частота origin-запросов (реф.) |
|---|---|
1 клиент на стрим X |
MPD: 0.4 req/s, segment: 0.2 req/s |
N клиентов на один стрим X (кеш включён) |
MPD: 1 req/s, segment: 0.2 req/s |
N ffmpeg-клиентов в режиме повтора на одном стриме |
MPD: 1 req/s (с |
N клиентов на N различных стримов |
MPD: 0.4·N req/s, segment: 0.2·N req/s |
6. Nginx как кеширующий reverse proxy¶
6.1. Базовая конфигурация¶
proxy_cache_path /var/cache/nginx/pss_segments
levels=1:2 keys_zone=pss_segments:100m
max_size=20g inactive=30m use_temp_path=off;
proxy_cache_path /var/cache/nginx/pss_manifests
levels=1:2 keys_zone=pss_manifests:10m
max_size=256m inactive=5m use_temp_path=off;
upstream pss_backend {
server 127.0.0.1:41972;
keepalive 64;
}
map $uri $pss_cache_key {
~^/h[0-9a-f]{16}(?<tail>/.+\.(ts|m3u8))$ "stream:$tail";
default $uri;
}
server {
listen 80;
server_name stream.example.com;
location ~* "^/h[0-9a-f]{16}(/[0-9]+)?/[0-9a-f]+\.ts$" {
proxy_cache pss_segments;
proxy_cache_key $pss_cache_key;
proxy_cache_valid 200 60s;
proxy_cache_valid 404 403 0s;
proxy_cache_lock on;
proxy_cache_use_stale updating error timeout;
proxy_cache_revalidate on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://pss_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering on;
}
location ~* "(^/h[0-9a-f]{16}(/[0-9]+)?/index\.(m3u8|mpd)$|^/(hls|dash)/.*\.(m3u8|mpd)$)" {
proxy_cache pss_manifests;
proxy_cache_key $pss_cache_key;
proxy_cache_valid 200 1s;
proxy_cache_valid 404 403 0s;
proxy_cache_lock on;
proxy_cache_lock_timeout 2s;
proxy_cache_use_stale updating;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://pss_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
location / {
proxy_pass http://pss_backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_buffering off;
proxy_read_timeout 3600s;
}
}
6.2. Назначение директив¶
Директива |
Назначение |
|---|---|
|
Сериализует выполнение upstream-запросов при одновременных cache miss по одному ключу |
|
Возвращает устаревшую копию параллельным запросам в период обновления кеша |
|
Использует |
|
Запрещает кеширование ошибок авторизации и 404 |
|
Поддерживает пул persistent-соединений к origin |
|
Для сегментов; включает буферизацию ответа в nginx |
|
Для раздела |
6.3. Расчёт max_size кеша сегментов¶
Ориентировочное значение:
bitrate × timeShiftBufferDepth × distinct_streams × 2
Пример: 10 стримов × 8 Mbps × 40s × 2 ≈ 800 MB. Рекомендуется задать запас 10x для учёта вариативности битрейта.
6.4. TLS-терминация¶
Сервер Perfect Streamer принимает соединения на портах HTTP и HTTPS.
При TLS-терминации на nginx upstream использует порт HTTP.
Пересылка заголовков X-Forwarded-Proto и X-Forwarded-Host обязательна для корректного формирования абсолютных URL при absPath=1.
server {
listen 443 ssl http2;
server_name stream.example.com;
ssl_certificate /etc/letsencrypt/live/stream.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/stream.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
location ... {
proxy_pass http://pss_backend;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header Host $host;
# + cache directives from 6.1
}
}
server {
listen 80;
server_name stream.example.com;
return 301 https://$host$request_uri;
}
При HTTPS между nginx и origin применяются директивы
proxy_ssl_verify, proxy_ssl_trusted_certificate. Для
loopback-соединений шифрование избыточно.
6.5. Мультихост¶
При обслуживании нескольких server_name из одного nginx-процесса
$host добавляется в cache key для изоляции контента:
map $uri $pss_cache_key {
~^/h[0-9a-f]{16}(?<tail>/.+\.(ts|m3u8))$ "$host:stream:$tail";
default "$host:$uri";
}
Размер keys_zone рассчитывается как 8000 ключей/MB. Для
мультихост-инсталляций с тысячами стримов рекомендуется
keys_zone=...:300m или выше.
7. Клиентское кеширование¶
Cache-Control: immutable обрабатывается браузерами
Chrome/Firefox/Safari. Клиентский кеш возвращает сегмент без условного
запроса при повторном обращении (в том числе при обратном seek в пределах
буфера плеера).
Service Workers могут применять стратегию cache-first на основании
содержимого Cache-Control. DASH-плееры (dash.js, Shaka) используют MSE
через SourceBuffer; сегмент, помещённый в буфер, остаётся доступен
без повторного HTTP-запроса до выхода за границу скольжения.
Для кросс-доменных запросов заголовок Access-Control-Allow-Origin: *
позволяет кеширование в shared caches без Vary: Origin. При смене
значения ACAO на конкретный Origin требуется Vary: Origin, что снижает
эффективность shared cache.
8. Размещение через CDN¶
Perfect Streamer совместим с CDN в режиме pull-from-origin (Cloudflare, Akamai, Fastly, BunnyCDN, Amazon CloudFront).
Origin shield. Рекомендуется размещение одного или нескольких shield-узлов между CDN edge и origin для снижения частоты запросов на origin при глобальном распределении клиентов.
Purge. Content-addressed сегменты не требуют purge. При изменении
метаданных стрима (кодек, разрешение) manifest’ы обновляются в течение
max-age=1 без явного purge.
Cache warming. При ожидаемом росте нагрузки на конкретный стрим допустим прогрев CDN с нескольких географических точек до начала трансляции.
Геораспределение. Сегменты (max-age=60) хорошо подходят для
географически распределённого кеширования. Манифесты (max-age=1)
допускают задержку доставки до одной секунды — приемлемо для
non-low-latency live.
9. Мониторинг¶
9.1. X-Cache-Status¶
Добавление add_header X-Cache-Status $upstream_cache_status; в каждый
location с кешированием. Значения:
Значение |
Описание |
|---|---|
|
Ответ из кеша |
|
В кеше отсутствовал, получен от origin и сохранён |
|
Просрочен, обновлён |
|
Отдана stale-копия параллельному запросу во время обновления |
|
|
|
Origin вернул 304 Not Modified |
|
Сработал |
9.2. Формат access-log¶
log_format pss_cache '$remote_addr $status $request_method "$request" '
'$body_bytes_sent rt=$request_time ut=$upstream_response_time '
'cache=$upstream_cache_status key=$pss_cache_key';
server {
access_log /var/log/nginx/pss.log pss_cache;
}
9.3. Метрики¶
Модуль nginx-vts экспортирует метрики per-zone в формате Prometheus:
GET /status/format/prometheus
Рекомендуемые пороги для алертов:
Метрика |
Порог |
Возможная причина |
|---|---|---|
Segment HIT rate |
< 90% за 5 минут |
Нарушена нормализация cache key; малый |
Manifest MISS rate |
> 50% за 1 минуту |
|
Upstream response time p95 |
> 500 мс за 1 минуту |
Перегрузка origin |
Cache zone fill |
> 90% за 10 минут |
Приближение к |
10. Диагностика¶
Симптом |
Вероятная причина |
Решение |
|---|---|---|
Segment HIT rate низкий |
|
Проверить заголовки и regex в директиве |
404 на сегментах после выхода из окна |
Закешированный 404 при сегменте, выпавшем из sliding window |
Добавить |
Задержка playback start 2-5 с |
|
Снизить до 1-2 с; включить |
Manifest не обновляется |
|
Явно указать |
Рост TIME_WAIT на upstream |
Отсутствует |
Добавить |
403 на |
Клиент резолвит относительные URL от pre-redirect URL |
Сервер эмитит |
11. Безопасность¶
11.1. Session URL¶
URL формата /h<sess>/... выполняет функцию сессионного токена — не
требует повторной аутентификации. Срок жизни ограничен idle timeout
(значение 30 с). При отсутствии активности сессия удаляется
cleaner-задачей.
Требования:
HTTPS для всех OTT-путей (
/hls/,/dash/,/h<sess>/) в productionSession ID в
Locationзаголовке 302 не кешируется (no-cache, no-store)
11.2. Rate limiting¶
limit_req_zone $binary_remote_addr zone=dash_top:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=hls_top:10m rate=5r/s;
server {
location /dash/ {
limit_req zone=dash_top burst=20 nodelay;
proxy_pass http://pss_backend;
}
location /hls/ {
limit_req zone=hls_top burst=20 nodelay;
proxy_pass http://pss_backend;
}
}
Session URL (/h<sess>/) не требует rate limiting — обработка дешёвая,
ответы кешируются.
11.3. Кеширование ответов с ошибками¶
proxy_cache_valid 200 60s;
proxy_cache_valid 301 302 0s;
proxy_cache_valid 404 403 0s;
proxy_cache_valid any 1s;
Запрещает кеширование редиректов (уникальный sess в Location) и ответов с ошибками авторизации или отсутствия ресурса.
11.4. Ограничение сетевого доступа к origin¶
Порт 41972 (41982 для HTTPS) должен быть закрыт для внешнего трафика. Допустимые конфигурации:
Bind Perfect Streamer на
127.0.0.1(при локальном nginx)Firewall-правило:
iptables -A INPUT -p tcp --dport 41972 ! -s 10.0.0.0/8 -j DROP
12. Интеграция с middleware¶
12.1. Модель prefix-login¶
Perfect Streamer поддерживает делегирование идентификации пользователя middleware/биллинг-системе через механизм prefix-login. Внешний коннектор к биллинг-системе не включён в текущий релиз.
Конфигурация embedded-пользователя:
{
"id": 9,
"login": "sub",
"password": "xxx",
"is-prefix": true,
"max-conn-http-hls": 1,
"accept-stream": [ ... ]
}
При is-prefix: true сервер принимает URL с логином вида
<prefix><billing_user_id>:
/dash/test1/sub42/xxx/index.mpd
/hls/test1/sub43/xxx/index.m3u8
12.2. Формат статистики¶
<clients>
<client login-id="-1974387287" login="sub" match-login="sub42"
sess-id="11331..." ott-type="dash" stream-id="10000" .../>
<client login-id="-2147031294" login="sub" match-login="sub43"
sess-id="11132..." ott-type="dash" stream-id="10000" .../>
</clients>
Поле login-id содержит хеш URL-логина. Поле login —
конфигурационное значение. Поле match-login — URL-логин,
использованный клиентом.
12.3. Ограничения prefix-login¶
Общий пароль. Все subscribers prefix-пула используют одно значение password. Компрометация пароля предоставляет доступ к любому
<prefix><string>.Гранулярность ACL.
accept-streamприменяется ко всему prefix-пулу. Per-subscriber ACL недоступен без внешнего биллинга.Ротация пароля. Изменение password отключает всех активных subscribers. Для постепенной замены требуется временное использование двух prefix-логинов.
13. WebVTT субтитры¶
Источник субтитров — DVB Teletext / DVB Subtitling из входного MPEG-TS.
13.1. URL VTT-сегментов¶
Схема |
URL |
Содержимое |
|---|---|---|
HLS master |
|
|
HLS subtitle playlist |
|
список |
HLS VTT-сегмент |
|
VTT с HLS-flavored X-TIMESTAMP-MAP |
DASH MPD AdaptationSet |
в |
|
DASH VTT-сегмент |
|
VTT с DASH-flavored X-TIMESTAMP-MAP |
<keyHex> — 16-символьный hex от CRC64(startTime, streamID, pid).
<seq> — десятичный номер чанка subtitle storage’а (separate counter
от video storage).