Skip to content

Скрытые субтитры (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: перечисленный сервис получает дорожку с первого сегмента, не дожидаясь первой реплики. Это важно для плееров, которые читают список дорожек один раз при старте: сервис, заговоривший на десятой минуте, иначе появится в меню только после переоткрытия потока.

Ключ сервиса — CC1CC4 для CEA-608 и SERVICE1SERVICE63 для CEA-708.

Что получает плеер

Каждый говорящий сервис становится отдельной текстовой дорожкой с постоянным номером: CC1CC4t1t4, SERVICE1SERVICE63t5t67. Номер закреплён за сервисом и не меняется при перезапуске стрима, поэтому ссылки на дорожку остаются рабочими.

В 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 означает, что в потоке едет неизвестная нам обёртка кэпшнов; сообщите об этом в поддержку.