OTT сервис

Выдает потоки по протоколам на базе HTTP - HLS, MPEG-DASH (с версии 1.12) и MPEG-TS over HTTP. Поддерживается HTTPS (SSL). Выдача включается на вкладке OTT настроек Stream.

URL для подключения имеют формат:

host и port - задаются в настройках http server.

stream - ID стрима. Не путать с порядковым номером в списке стримов. ID отображается вверху страницы статистики стрима и в колонке ID списка стримов, ID задается при создании стрима и никогда не меняется.

Аналогично для HLS и DASH:

На странице статистики стрима отображаются URL подключенных протоколов (в виде шаблона) и текущий статус их работы. Неавторизированный доступ запрещен, клиенты должны быть прописаны в Peers.

Для HLS и DASH в URL доступны дополнительные параметры (опционально):

[URL]?a=1&s=40&m=40&v=5&h3=1

  • a: 1 — абсолютный путь в плейлисте, 0 — относительный путь (по умолчанию).

  • s: продолжительность динамического плейлиста (сек), по умолчанию 40 сек.

  • m: минимальная продолжительность динамического плейлиста (сек), по умолчанию 40 сек. Максимальный размер динамического плейлиста 60 сек. Если текущий размер буфера чанков менее минимального размера, указанного в запросе, то будет выдана ошибка 404. Это сделано для того, чтобы HLS стартовал с заполненного буфера чанков на сервере.

  • v: версия HLS протокола, выдаваемого в плейлисте. По умолчанию значение зависит от режима HLS (см. ниже): OTT mode6, Peer mode3. Явное значение в URL переопределяет дефолт. Смена версии может потребоваться для совместимости с конкретным HLS-клиентом.

  • h3: opt-in на HTTP/3 (QUIC) для этой OTT-сессии. Заставляет сервер выдать заголовок Alt-Svc в ответе, после чего совместимый плеер / браузер переключается на QUIC. См. раздел HTTP/3 (QUIC) ниже.

Для совместимости с некоторыми HLS клиентами в URL может быть добавлено имя файла index.m3u8, например http://host:port/hls/stream/login/password/index.m3u8.

Имеется 2 режима работы HLS сервера — Peer mode и OTT mode.

Peer mode — режим с простой разбивкой сегментов (чанков). Рекомендуется для пиринга (дистрибьюции) потоков. По умолчанию плейлист отдаётся как EXT-X-VERSION:3 для совместимости с пир-клиентами.

OTT mode — режим с разбивкой сегментов, оптимизированной для быстрого старта проигрывателей при OTT-вещании. В этом режиме нагрузка на CPU больше, рекомендуется для вещания. По умолчанию плейлист отдаётся как EXT-X-VERSION:6 с EXT-X-INDEPENDENT-SEGMENTS и атрибутом CHARACTERISTICS в EXT-X-MEDIA TYPE=SUBTITLES (Apple HLS, hls.js, Safari, dash.js/Shaka). Если конкретному клиенту нужно более раннее значение — задайте его через query-параметр ?v= (см. список параметров выше).

Для HTTP сервера может быть включен SSL (HTTPS), это делается в настройках сервера.

Chunk Min Interval и Chunk Max Interval

В режиме OTT делается анализ потока на PAT/PMT/SPS/PPS/IFrame и чанки нарезаются по критерию быстрого старта проигрывателей. Анализ начинается с min interval, и если по какой-то причине данные не найдены, то чанк принудительно нарезается по max interval.

GOP-aligned segments

С версии 1.13.1.438 в режиме OTT mode каждый TS-сегмент .ts гарантированно начинается с SPS / PPS / IDR (для HEVC — также VPS). Сегментер вырезает «хвост» предыдущего GOP — P / B-слайсы — из окна между лидирующим PAT и первым SPS чанка. Это:

  • устраняет начальный чёрный кадр на старте VOD-сессий (?t= / ?epg=) в hls.js, Safari и VLC: декодер MSE получает корректный заголовочный набор NAL-юнитов уже в первом сегменте и стартует немедленно;

  • приводит выдачу в соответствие HLS RFC 8216 §3 («Each Media Segment MUST contain a SPS and a PPS that decode its first Access Unit»);

  • делает корректным заявление EXT-X-INDEPENDENT-SEGMENTS в EXT-X-VERSION:6 плейлисте.

Управление — настройка стрима gop-aligned-segment (по умолчанию true). Применяется только при HLS = OTT mode: в Peer mode и для MPTS-стримов поведение не меняется. PSI (PAT / PMT) и аудио сохраняются в полном объёме; единственный побочный эффект — однокадровый разрыв continuity_counter на видео-PID на границе двух соседних чанков, который для HLS / DASH MSE является легитимной точкой decode boundary.

HLS Adaptive Multistream

С версии 1.10 добавлена поддержка HLS Adaptive Multistream и с версии 1.12 DASH Adaptive Multistream.

Для адаптивных потоков настраивается отдельный HLS плейлист. Для этого надо:

  • У стримов, которые будут включены в адаптивный плейлист, включить HLS с OTT Mode.

  • В главном меню появится раздел адаптивных потоков. В нем надо добавить поток, где прописать все потоки, которые должны быть добавлены в этот плейлист.

  • У потоков может быть задан параметр битрейт. По умолчанию он 0, что означает, что битрейт берется от измеренного значения. Иначе его можно задать явно.

Для адаптивных плейлистов будет другой URL:

У пиров (клиентов) может быть назначено ограничение доступа к адаптивным потокам, также как к обычным. Разрешение для адаптивного потока включает разрешение ко всем потокам, которые входят в него.

HTTP/3 (QUIC)

С версии 1.13.1.438 в Perfect Streamer реализован встроенный сервер HTTP/3 для OTT-доставки HLS и MPEG-DASH поверх QUIC (RFC 9000 + RFC 9114). Стек — ngtcp2 (QUIC v1) + nghttp3 (HTTP/3 frame layer); TLS-инфраструктура переиспользует тот же сертификат, что и HTTPS-listener.

QUIC обслуживает только OTT-маршруты:

  • / — root-redirect,

  • /hls/... и /dash/... — master playlists / MPD,

  • /h<sessID>/... — per-session URL (media playlists, сегменты, VTT),

  • /http/<stream>/... — raw MPEG-TS over HTTP.

Административные пути (/data, /config, /xmltv, /db/, /login, /logout, /restart) на QUIC отвечают 404 — admin-API остаётся на HTTPS/HTTP-TCP. Это сознательное ограничение для уменьшения поверхности атаки QUIC-listener’а.

Включение

Настройки QUIC живут в разделе Configuration / HTTP server (узел /config/http-server):

  • HTTP/3 Enable (http3-enable) — глобальный флаг включения QUIC-listener’а. По умолчанию off. Включение поднимает UDP-сокет на http3-port; выключение — закрывает его.

  • HTTP/3 Port (http3-port) — UDP-порт QUIC-listener’а. По умолчанию 43984. QUIC живёт на UDP, HTTPS — на TCP; порты могут совпадать или отличаться, конфликта между ними нет.

  • HTTP/3 0-RTT Enable (http3-zero-rtt-enable) — разрешение 0-RTT-handshake’а (RFC 9001 §4.6.1) при повторном соединении того же клиента. Снижает задержку старта; нужно учитывать replay-риск для не-идемпотентных запросов (для read-only OTT-нагрузки это безопасно). По умолчанию on.

Изменения настроек применяются «горячо», без рестарта сервиса.

Сертификат и ключ берутся из HTTPS-listener’а — отдельной настройки сертификата для HTTP/3 нет. Если HTTPS не сконфигурирован (ssl-enable=false), включение HTTP/3 ничего не отдаёт — handshake не пройдёт.

Параметр ?h3 — opt-in на сеанс

Браузер не подключается к HTTP/3 endpoint напрямую — он сначала идёт на HTTPS/TCP, получает в ответе заголовок Alt-Svc: h3=":<port>"; ma=86400 (RFC 7838), и только следующие запросы к этому origin переключает на QUIC.

В Perfect Streamer выдача Alt-Svcopt-in per OTT-сессия. Сервер не анонсирует QUIC всем клиентам подряд: клиент должен явно попросить про HTTP/3 query-параметром ?h3 на master HLS / DASH URL.

Значение в URL

Значение opt-in

Alt-Svc в ответе

параметр отсутствует

off (default)

нет

?h3 (без значения)

on

есть

?h3=1, ?h3=on, ?h3=yes, ?h3=true

on

есть

?h3=0, ?h3=off, ?h3=no, ?h3=false

explicit off

нет (как при отсутствии)

После того как клиент получил session-URL /h<sess>/..., признак wantH3 сохраняется в сессии — все последующие refresh’ы media playlist, GET’ы сегментов и VTT-чанков под этим session-URL также получают Alt-Svc, даже если ?h3 нет в самом запросе. Без opt-in на master — sticky-поведение не возникает.

Гейтинг Alt-Svc:

  1. Глобально включён QUIC-listener (http3-enable=true) — иначе анонс подавляется даже при ?h3.

  2. Запрос пришёл не по QUIC — на запросах, уже идущих по H3, Alt-Svc бессмысленен и не отдаётся.

  3. Per-session или per-URL wantH3=true (см. таблицу выше).

  4. Admin и EPG сервера Alt-Svc не отдают никогда — у них нет QUIC-listener’а.

Это поведение совместимо с RFC 7838 §3 — сервер сам решает, на каких ответах выдавать Alt-Svc.

Сценарий перехода на QUIC в браузере

Типичный поток (Chrome / Firefox / Safari):

  1. Плеер открывает master URL с явным opt-in:

    https://stream.example.com:41982/hls/test1/login/password/index.m3u8?h3=1
    
  2. Первый запрос идёт по HTTPS/TCP. В ответе сервер выдаёт Alt-Svc: h3=":43984"; ma=86400.

  3. Браузер запоминает сопоставление stream.example.com:41982 h3=":43984". В Chrome его можно увидеть на странице chrome://net-internals/#alt-svc; в Firefox — about:networking#http3.

  4. На следующих запросах к тому же origin (refresh playlist, GET-запросы сегментов, VTT) браузер открывает QUIC-соединение на udp/43984 и продолжает по HTTP/3. В DevTools → Network колонка Protocol на этих запросах становится h3.

Если QUIC-соединение не устанавливается (UDP-порт закрыт на firewall, сертификат не доверен в системе), браузер прозрачно остаётся на HTTPS/TCP — функционально стрим продолжает играть, без перерыва.

Учёт клиентов и мониторинг

Реальный IP-адрес QUIC-клиента в учёте активных пиров (/data/http-clients, лимиты конкурентных подключений, ACL по IP) — это настоящий peer-address QUIC-соединения, а не loopback внутреннего backend-моста.

Атрибут ott-type в /data/http-clients — составной, формат <PROTO>/<scheme>:

  • PROTO — OTT-протокол: HLS, DASH или HTTP.

  • scheme — фактический сетевой транспорт: http, https или quic.

Примеры: HLS/quic, DASH/https, HTTP/http. Админ-UI видит и OTT-протокол, и сетевой транспорт каждого клиента в одной колонке.

Таймаут OTT-сессии — 60 секунд независимо от транспорта. При переключении клиента между транспортами схема обновляется только «вверх» по приоритету: httphttpsquic. Параллельное возвращение к менее защищённому соединению не «затирает» текущий тип — в учёте остаётся максимально безопасный.

Совместимость плееров

HTTP/3 для OTT поддерживают:

  • iOS AVPlayer / Safari — нативно, через Alt-Svc, есть 0-RTT.

  • Chrome, Firefox, Edge, Brave — через Alt-Svc; HLS играется через hls.js, DASH — через dash.js / Shaka, трафик плеера наследует HTTP/3 от browser fetch API.

  • Android ExoPlayer — через Cronet QUIC engine (не используемый по умолчанию transport, требует настройки клиента).

VLC (libVLC) HTTP/3 не поддерживает — на этих клиентах поток продолжит работать через HTTPS/TCP без выигрыша QUIC.

Реальный выигрыш QUIC vs HTTPS/TCP заметен в мобильных сетях / Wi-Fi / сетях с потерями:

  • нет head-of-line blocking на TCP-уровне — потеря в одном HTTP-stream не задерживает остальные;

  • 0-RTT handshake при повторном соединении того же клиента;

  • стабильнее ABR-bitrate на потерях 1–5 %.

В управляемых сетях / корпоративном Wi-Fi выигрыш обычно скромный — 5–10 % по startup latency и почти нулевой по steady-state. CPU-нагрузка на сервере для QUIC выше, чем для kernel-TCP — это плата за user-space TLS+транспорт.

Модель кеширования OTT HLS и DASH

Сервер формирует ответы трёх категорий, различающихся сроком жизни содержимого и пригодностью для кеширования промежуточными узлами (reverse proxy, CDN, клиентский кеш).

1. Модель кеширования

1.1. Ресурсы и HTTP-заголовки

Ресурс

URL

Content-Type

Cache-Control

TS-сегмент

/h<sess>/<keyHex>.ts, /h<sess>/sub/<pid>/<keyHex>.vtt

video/mp2t

public, max-age=60, immutable

DASH MPD

/h<sess>/index.mpd

application/dash+xml; charset=utf-8

public, max-age=1

HLS master

/hls/<stream>/<login>/<pass>/index.m3u8

application/vnd.apple.mpegurl

public, max-age=1

HLS media

/h<sess>/index.m3u8, /h<sess>/sub/<pid>/index.m3u8

application/vnd.apple.mpegurl

public, max-age=1

302 Redirect

/dash/<stream>/<login>/<pass>/index.mpd

no-cache, no-store

Raw TS

/http/<stream>/<login>/<pass>

video/mp2t

не задан; не кешируется

1.2. Характеристики TS-сегментов

Шестнадцатеричный идентификатор сегмента в URL (<keyHex> в путях /h<sess>/<keyHex>.ts) формируется как CRC64 от стартового времени сегмента и ID стрима и глобально уникален. 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

/h<sess>/index.m3u8

Одна сессия на сеанс воспроизведения

VLC 3.x DASH

/dash/<stream>/.../index.mpd

Обрабатывается session reuse (см. 3.3)

ffmpeg 5.x HLS

/h<sess>/index.m3u8

Одна сессия на сеанс воспроизведения

ffmpeg 5.x DASH

/dash/<stream>/.../index.mpd (цикл повтора)

Обрабатывается session reuse (см. 3.3)

dash.js, hls.js

/h<sess>/... через <Location> / session URL

Одна сессия на сеанс воспроизведения

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 от того же логина к тому же стриму (с тем же признаком adaptive) сервер находит уже существующую DASH-сессию и возвращает её идентификатор повторно. Новая сессия не создаётся, слот лимита одновременных подключений не расходуется.

Применяется только к DASH. Для HLS отдельный механизм reuse не требуется: HLS-клиенты обновляют media playlist по session-URL и не создают новую сессию на каждом refresh.

3.3. Переиспользование сегментов между сессиями

Путь /h<sess>/<keyHex>.ts не зависит от <sess> при разрешении <keyHex> в содержимое: <keyHex> глобально однозначно идентифицирует TS-сегмент в рамках стрима. Nginx с нормализованным cache key (обрезающим префикс /h<sess>/) обслуживает все запросы одного и того же <keyHex> из единственной записи кеша, независимо от того, какие клиенты их выдали.

4. Параметры запроса

Параметр

Значение по умолчанию

Влияние

a

0

1 — абсолютные URL в манифестах; 0 — относительные

s

40

timeShiftBufferDepth в секундах

m

40

Минимальная длина окна для выдачи манифеста

v

6 для OTT mode, 3 для Peer mode

#EXT-X-VERSION в HLS (игнорируется DASH); явное значение в URL переопределяет дефолт

h3

отсутствует (off)

opt-in на HTTP/3 (QUIC) — заставляет сервер выдать Alt-Svc в ответе. Опознаваемые значения: presence ⇒ on, 1/on/yes/true ⇒ on, 0/off/no/false ⇒ explicit off. Sticky на session-URL /h<sess>/.... См. раздел HTTP/3 (QUIC).

Изменение параметра через query string обновляет сохранённые в сессии значения при её следующем переоткрытии.

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 (с proxy_cache_lock)

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. Назначение директив

Директива

Назначение

proxy_cache_lock on

Сериализует выполнение upstream-запросов при одновременных cache miss по одному ключу

proxy_cache_use_stale updating

Возвращает устаревшую копию параллельным запросам в период обновления кеша

proxy_cache_revalidate on

Использует If-Modified-Since при cache miss с сохранённой копией

proxy_cache_valid 404 403 0s

Запрещает кеширование ошибок авторизации и 404

keepalive 64 в upstream

Поддерживает пул persistent-соединений к origin

proxy_buffering on

Для сегментов; включает буферизацию ответа в nginx

proxy_buffering off

Для раздела /; отключает буферизацию (raw streaming)

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;
        # + caching 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 с кешированием. Значения:

Значение

Описание

HIT

Ответ из кеша

MISS

В кеше отсутствовал, получен от origin и сохранён

EXPIRED

Просрочен, обновлён

UPDATING

Отдана stale-копия параллельному запросу во время обновления

STALE

use_stale вернул просроченную копию (origin недоступен)

REVALIDATED

Origin вернул 304 Not Modified

BYPASS

Сработал proxy_cache_bypass

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; малый max_size

Manifest MISS rate

> 50% за 1 минуту

proxy_cache_lock не сериализует запросы

Upstream response time p95

> 500 мс за 1 минуту

Перегрузка origin

Cache zone fill

> 90% за 10 минут

Приближение к max_size, планируется LRU-eviction

10. Диагностика

Симптом

Вероятная причина

Решение

Segment HIT rate низкий

Vary: Origin с высокой вариативностью Origin; нарушение normalisation в map

Проверить заголовки и regex в директиве map

404 на сегментах после выхода из окна

Закешированный 404 при сегменте, выпавшем из sliding window

Добавить proxy_cache_valid 404 0s в location segments

Задержка playback start 2-5 с

proxy_cache_lock_timeout превышает target latency

Снизить до 1-2 с; включить proxy_cache_use_stale updating

Manifest не обновляется

proxy_cache_valid переопределяет max-age

Явно указать proxy_cache_valid 200 1s

Рост TIME_WAIT на upstream

Отсутствует keepalive в upstream-блоке

Добавить keepalive 64, proxy_http_version 1.1, proxy_set_header Connection ""

403 на /dash/.../<segment>.ts от ffmpeg

Клиент резолвит относительные URL от pre-redirect URL

Сервер эмитит <BaseURL>/h<sess>/</BaseURL> (absolute path); совместимо в текущем билде

Лаги, частые ребуферы у удалённых клиентов

Низкий эффективный throughput TCP из-за slow start и idle restart при больших RTT (300 мс и выше)

Тюнинг сетевого стека Linux на origin: см. 10.1

10.1. TCP-тюнинг origin для high-RTT клиентов

Проблема проявляется у клиентов с большим RTT до origin (например, 300 мс и выше) при битрейте стрима, близком к пропускной способности канала. Симптомы в плеере (VLC, ffmpeg, dash.js) — частые ребуферы, warnings вида ES_OUT_SET_PCR called too late (увеличение pts_delay), buffer deadlock prevented, разрывы потока. На сервере клиент при этом нормальный, ошибок нет, throughput по /data/stream/... соответствует входному потоку.

Причина:

  • TCP slow start. Каждое новое TCP-соединение начинает с congestion window около 14 КБ и наращивает его за несколько RTT. При RTT 300 мс выход на полное окно занимает 2-3 секунды. За это время HLS/DASH-сегмент длительностью 5 с (4-6 МБ) скачивается заметно медленнее реального времени.

  • TCP idle restart. Между запросами сегментов клиент по HLS pull-модели делает паузу 4-5 с. По умолчанию ядро Linux после такой паузы сбрасывает congestion window соединения обратно к initial cwnd (поведение net.ipv4.tcp_slow_start_after_idle=1). В результате при следующем GET keep-alive-соединение начинает передачу с медленного start заново — даже на уже разогретой сессии.

Дополнительное усугубление — congestion control CUBIC по умолчанию плохо переносит длинные RTT и пакетные потери на промежуточных участках сети.

Решение — два sysctl-параметра на origin:

# Keep congestion window across idle pauses inside keep-alive sessions.
sysctl -w net.ipv4.tcp_slow_start_after_idle=0

# Use BBR instead of CUBIC: better behaviour on long-RTT paths
# with mild packet loss; paces sending instead of bursting.
modprobe tcp_bbr
sysctl -w net.ipv4.tcp_congestion_control=bbr

Для постоянного применения:

cat > /etc/sysctl.d/99-pss-net.conf <<EOF
net.ipv4.tcp_slow_start_after_idle = 0
net.ipv4.tcp_congestion_control    = bbr
EOF
sysctl --system

Главный эффект даёт первый параметр (tcp_slow_start_after_idle=0). Он напрямую устраняет повторный slow start между сегментными запросами в одном keep-alive соединении. Второй (BBR) даёт дополнительную устойчивость и применяется ко всем новым соединениям.

Тюнинг не требует перезапуска Perfect Streamer и применяется ко всем новым TCP-соединениям сразу после sysctl -w. Существующие соединения сохраняют congestion control, с которым были установлены.

11. Безопасность

11.1. Session URL

URL формата /h<sess>/... выполняет функцию сессионного токена — не требует повторной аутентификации. Срок жизни ограничен idle timeout (значение 30 с). При отсутствии активности сессия удаляется cleaner-задачей.

Требования:

  • HTTPS для всех OTT-путей (/hls/, /dash/, /h<sess>/) в production

  • Session 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) должен быть закрыт для внешнего трафика. Допустимые конфигурации:

  1. Bind Perfect Streamer на 127.0.0.1 (при локальном nginx)

  2. 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

  • Общий пароль. Все подписчики prefix-пула используют одно значение пароля. Компрометация пароля предоставляет доступ к любому <prefix><string>.

  • Гранулярность ACL. accept-stream применяется ко всему prefix-пулу. Per-subscriber ACL недоступен без внешнего биллинга.

  • Ротация пароля. Изменение пароля отключает всех активных подписчиков. Для постепенной замены требуется временное использование двух prefix-логинов.

13. WebVTT субтитры

Источник субтитров — DVB Teletext / DVB Subtitling из входного MPEG-TS. В разделах Media Information или Original Media Information должны присутствовать дорожки Teletext subtitles. Также в разделе Analyzer можно убедиться, что пакеты соответствующих PID активны.

Для OTT HLS/DASH должен быть включён режим OTT (в Peer mode WebVTT субтитры недоступны). В разделе Output # OTT должен появиться ненулевой счётчик чанков OTT WebVTT buffer chunk count.

Для диагностики субтитров включить у стрима Analyze и Trace. При старте потока в логе стрима должно появиться:

Start Teletext subtitle decoder
[ttxsubdec] ttx: pid=331 magazine=8 page=0x88 lang=***

Далее в лог пишется декодированный текст субтитров.

13.1. URL VTT-сегментов

Схема

URL

Содержимое

HLS master

/hls/.../index.m3u8

#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="subs",...,URI="/h<sess>/sub/<pid>/index.m3u8"

HLS subtitle playlist

/h<sess>/sub/<pid>/index.m3u8

список <keyHex>.vtt с #EXTINF

HLS VTT-сегмент

/h<sess>/sub/<pid>/<keyHex>.vtt

VTT с HLS-flavored X-TIMESTAMP-MAP

DASH MPD AdaptationSet

в index.mpd

contentType="text" mimeType="text/vtt" + <SegmentTemplate media="$Number$.vtt">

DASH VTT-сегмент

/h<sess>/sub/<pid>/<seq>.vtt

VTT с DASH-flavored X-TIMESTAMP-MAP

<keyHex> — 16-символьный hex CRC64 от стартового времени сегмента, ID стрима и PID субтитровой дорожки. <seq> — десятичный порядковый номер чанка субтитрового потока (нумерация субтитров не связана с нумерацией TS-чанков).