Субтитры (распознавание речи)¶
Sapsan умеет распознавать речь в записанном потоке и писать её в отдельную субтитровую дорожку. Распознавание идёт на встроенном движке Whisper (whisper.cpp) поверх DVR-архива: фоновый воркер читает записанное аудио, распознаёт его и дописывает субтитровые кадры обратно в архив. Дальше субтитровая дорожка отдаётся в HLS и DASH как WebVTT.
Поскольку воркер читает из архива, а не тапает живой поток, сломать поток он не может: если распознавание не успевает, субтитры просто отстают и потом догоняют. Поэтому для стрима обязателен включённый DVR.
Предварительные требования¶
-
Сборка с фичей
whisper. Распознавание тянет нативную сборку whisper.cpp + ffmpeg, поэтому по умолчанию выключено:bash make release FEATURES=whisper # или: cargo build --release --features whisperНа Apple Silicon добавьте Metal для ускорения на GPU (заметно быстрее):
--features whisper,metal. -
Включённый DVR на стриме (воркер читает и пишет архив).
-
Файл модели. Модель задаётся коротким именем (
tiny,base,small,medium,large-v3); Sapsan резолвит имя в GGML-файлggml-<имя>.binв каталоге моделей:WHISPER_MODELS_DIR, если задан, иначе~/.cache/whisper-models.
Скачайте модель один раз, например
medium:bash mkdir -p ~/.cache/whisper-models curl -L -o ~/.cache/whisper-models/ggml-medium.bin \ https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin
Конфигурация¶
Добавьте блок speech2text к стриму с включённым DVR:
streams:
news:
inputs:
- rtsp:
url: rtsp://admin:password@10.0.0.5/stream0
dvr:
root: /storage
speech2text:
model: medium # tiny | base | small | medium (по умолчанию) | large-v3
language: ru # ISO-639-1; уберите для автоопределения
audio_track: a1 # исходная аудиодорожка (по умолчанию первая)
max_window_secs: 30 # предел окна распознавания, с (по умолчанию 30, макс. 120)
Поля:
| Поле | Смысл | По умолчанию |
|---|---|---|
model |
Модель коротким именем; резолвится в ggml-<имя>.bin |
medium |
language |
Язык распознавания (ISO-639-1) | авто |
audio_track |
Идентификатор исходной аудиодорожки | первая аудио |
max_window_secs |
Верхний предел окна анализа | 30 |
Валидация: стрим с speech2text, но без dvr, отклоняется; max_window_secs — в диапазоне 1..=120; language — код из 2–3 букв.
Изменения применяются на reload (SIGHUP / central): воркер перезапускается только при фактическом изменении блока speech2text.
Проверка офлайн (до включения на стриме)¶
CLI subtitle (собран с фичей whisper) гоняет ровно тот же продакшн-распознаватель на локальном файле, кладёт результат рядом и, если у файла уже есть субтитровая дорожка, считает метрику близости. Это самый быстрый способ подобрать модель и язык и оценить качество:
subtitle recognize movie.mkv --lang ru --model medium
# обработать только отрезок (например, 2 минуты с 25:00) для быстрой проверки:
subtitle recognize movie.mkv --lang ru --start-sec 1500 --limit-sec 120
Файлы рядом с исходным:
movie.subtitles.json— структурные кадры субтитров с полным провенансом (аудио-окно, модель, хеш, prompt, диагностика на реплику), достаточно, чтобы воспроизвести прогон;movie.vtt— проекция в WebVTT;movie.diff.txt— эталон против распознанного построчно (если у файла есть эталонная субтитровая дорожка).
Если у файла есть эталонные субтитры, CLI печатает WER (word error rate) и CER (character error rate) — глобально и по окнам. Важно: против независимо сделанной субтитровой дорожки WER меряет расхождение переводов, а не ошибку ASR — читайте его как относительный сигнал между прогонами и смотрите дифф глазами.
На вход годится любой .mkv/.mp4.
Проверка на стриме¶
После включения speech2text на DVR-стриме:
-
В медиаинфо стрима появляется субтитровая дорожка. Проверьте HLS-мастер — в нём теперь есть рендиция
EXT-X-MEDIA:TYPE=SUBTITLES, а варианты ссылаются на неё атрибутомSUBTITLES="subs":bash curl -s "http://server/streaming/<stream>/index.m3u8" | grep -i subtitleВ DASH в манифесте появляется
AdaptationSetсcontentType="text". -
Откройте архив в плеере с поддержкой субтитров (или HLS/DASH-URL) и включите субтитровую дорожку. Субтитры появляются с отставанием, пока воркер догоняет живой край, затем идут следом.
-
Следите за счётчиками распознавания в метриках (обработано секунд, размер щели / отставание, ошибки).
Воркер stateless — прогресс выводится из края субтитровой дорожки в архиве, — поэтому после рестарта он продолжает с той же щели, добивает субтитрами уже накопленный архив и никогда не дублирует кадр.
Как это работает¶
Единица работы воркера — «щель» между покрытием аудио и покрытием субтитров в архиве. Он читает аудио щели, декодирует его в моно PCM 16 кГц, гоняет Whisper по скользящим окнам (с гейтом тишины против галлюцинаций и ре-якорем по полным сегментам) и дописывает структурные кадры субтитров. Отдаваемый плеерам WebVTT — проекция этих кадров; сами кадры дополнительно хранят провенанс и диагностику Whisper для отладки.