Скрытые субтитры (closed captions)¶
Закрытые субтитры (closed captions, CC) едут не отдельной дорожкой, а внутри самого видео — в служебных данных кадров (SEI для H.264 и HEVC, user data для MPEG-2, метаданные OBU для AV1). Их понимают телевизоры и приставки, но браузерный плеер такой текст показать не может: HLS и DASH ждут субтитры отдельной дорожкой.
Sapsan умеет и то, и другое: пропускать кэпшны внутри видео как есть и декодировать их в субтитровую дорожку WebVTT.
Режимы обработки¶
Режим задаётся полем mode в секции closed_captions стрима и управляет только выдачей — декодирование и запись в архив идут всегда:
passthrough(по умолчанию) — кэпшны остаются внутри видео и объявляются в манифестах как closed captions. Субтитровой дорожки в манифестах нет.extract— субтитровая дорожка вместо встроенных кэпшнов: текст декодируется в WebVTT-дорожку, а из выходного видео кэпшны вырезаются и как closed captions не объявляются.both— и то, и другое: кэпшны едут внутри видео и объявляются, и рядом идёт субтитровая дорожка.
Конфигурация¶
streams:
- name: news
inputs:
- url: udp://239.0.0.1:1234
closed_captions:
mode: extract
services:
CC1:
language: eng
name: English
SERVICE3:
language: spa
name: Espanol
Секция services необязательна и решает две задачи:
- имя и язык пункта меню в плеере. Без неё имя выводится из потока: язык, объявленный сервисом, иначе — адрес сервиса (
CC1,SERVICE3); - предобъявление в режимах
extractиboth: перечисленный сервис получает дорожку с первого сегмента, не дожидаясь первой реплики. Это важно для плееров, которые читают список дорожек один раз при старте: сервис, заговоривший на десятой минуте, иначе появится в меню только после переоткрытия потока.
Ключ сервиса — CC1…CC4 для CEA-608 и SERVICE1…SERVICE63 для CEA-708.
Что получает плеер¶
Каждый говорящий сервис становится отдельной текстовой дорожкой с постоянным номером: CC1…CC4 → t1…t4, SERVICE1…SERVICE63 → t5…t67. Номер закреплён за сервисом и не меняется при перезапуске стрима, поэтому ссылки на дорожку остаются рабочими.
В HLS дорожка приезжает рендишеном EXT-X-MEDIA с TYPE=SUBTITLES, в DASH — адаптационным набором contentType="text" c mimeType="text/vtt". Сегменты дорожки лежат по адресу вида:
/streaming/v/news/subtitles/t1/1738245600000.vtt
Последний сегмент пути — начало окна в миллисекундах; конец окна определяет сервер по той же сетке сегментов, что и у видео. Пустое окно — это корректный пустой WebVTT-сегмент, а не ошибка: в паузе между репликами дорожка не должна рваться.
Субтитры отдаются в живом эфире, в отложенном просмотре (rewind) и в архиве. Декодированные реплики пишутся в архив всегда, независимо от режима, — поэтому включение extract действует и задним числом: субтитры появятся и на уже записанном архиве.
Ограничение режима extract на TS-выдачах¶
В режиме extract кэпшны вырезаются из выходного видео целиком — на всех выдачах сразу, включая MPEG-TS: push по udp/rtp/srt, .ts-сегменты HLS и tshttp. Текстовой дорожке в MPEG-TS ехать не в чем, поэтому потребитель TS-выдачи в этом режиме останется без субтитров вообще.
Если поток одновременно смотрят браузером и забирают по MPEG-TS, нужен режим both: он оставляет кэпшны внутри видео для TS-потребителя и добавляет дорожку для браузера.
Переход с Flussonic Media Server¶
В legacy Flussonic опция cc.extract не убирала кэпшны из видео — она включала их извлечение дополнительно к встроенным. Поэтому её прямое соответствие в Sapsan — это mode: both, а не mode: extract.
| Flussonic | Sapsan | Поведение |
|---|---|---|
| опция не задана | mode: passthrough |
кэпшны внутри видео, дорожки нет |
cc.extract |
mode: both |
кэпшны внутри видео и субтитровая дорожка |
| — | mode: extract |
новое строгое поведение: дорожка вместо встроенных кэпшнов |
mode: extract — новое поведение, аналога в legacy Flussonic у него нет. Выбирайте его, когда встроенные кэпшны не нужны никому из потребителей: он снимает двойной пункт меню в плеере (одни и те же субтитры и как closed captions, и как дорожка) и экономит место в видеокадрах.
Проверка¶
Убедиться, что дорожка появилась, можно по мастер-плейлисту:
curl -s http://localhost:8080/streaming/v/news/index.m3u8 | grep SUBTITLES
и по содержимому сегмента:
curl -s http://localhost:8080/streaming/v/news/subtitles/t1/1738245600000.vtt
Состав обнаруженных сервисов и номер дорожки каждого видны в статистике стрима (GET /streaming/api/v4/streams/stats/news, секция captions).
Работу тракта показывают метрики Prometheus с префиксом stream_captions_: stream_captions_cc_pairs_total — доходят ли кэпшны до сервера вообще, stream_captions_cues_decoded_total — сколько реплик распознано, stream_captions_cues_delivered_total — сколько уложено в дорожку. Растущий stream_captions_unrecognized_payloads_total означает, что в потоке едет неизвестная нам обёртка кэпшнов; сообщите об этом в поддержку.