Skip to content

Legendas (speech to text)

O Sapsan pode reconhecer fala num stream gravado e escrevê-la numa faixa de legendas separada. O reconhecimento roda no motor Whisper embutido (whisper.cpp) sobre o arquivo DVR: um worker em segundo plano lê o áudio gravado, transcreve-o e acrescenta de volta no arquivo quadros de legendas. A faixa de legendas é então servida em HLS e DASH como WebVTT.

Como o worker lê do arquivo e não toca no stream ao vivo, ele não pode quebrá-lo: se o reconhecimento não dá conta, as legendas simplesmente atrasam e depois se põem em dia. Por isso o stream precisa ter o DVR ligado.

Pré-requisitos

  1. Uma compilação com a feature whisper. O reconhecimento de fala enlaça uma pilha nativa whisper.cpp + ffmpeg, por isso vem desligado por padrão:

    bash make release FEATURES=whisper # ou: cargo build --release --features whisper

    No Apple Silicon adicione Metal para aceleração por GPU (bem mais rápido): --features whisper,metal.

  2. DVR ligado no stream (o worker lê e escreve o arquivo).

  3. Um arquivo de modelo. Os modelos são nomeados por um nome curto (tiny, base, small, medium, large-v3); o Sapsan resolve o nome num arquivo GGML ggml-<nome>.bin no diretório de modelos:

    • WHISPER_MODELS_DIR, se definido, senão ~/.cache/whisper-models.

    Baixe um modelo uma única vez, por exemplo 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

Configuração

Adicione um bloco speech2text a um stream que já tenha DVR:

streams:
  news:
    inputs:
    - rtsp:
        url: rtsp://admin:password@10.0.0.5/stream0
    dvr:
      root: /storage
    speech2text:
      model: medium        # tiny | base | small | medium (padrão) | large-v3
      language: ru         # ISO-639-1; omita para detecção automática
      audio_track: a1       # trilha de áudio de origem (padrão: a primeira trilha de áudio)
      max_window_secs: 30   # limite da janela de reconhecimento, s (padrão 30, máx. 120)

Campos:

Campo Significado Padrão
model Modelo por nome curto; resolvido em ggml-<nome>.bin medium
language Idioma do reconhecimento (ISO-639-1) auto
audio_track Identificador da trilha de áudio de origem a primeira trilha de áudio
max_window_secs Limite superior da janela de análise 30

Validação: um stream com speech2text, mas sem dvr, é rejeitado; max_window_secs precisa estar em 1..=120; language precisa ser um código de 2–3 letras.

As mudanças são aplicadas no reload (SIGHUP / central): o worker é reiniciado apenas se o bloco speech2text tiver mudado de fato.

Verificação offline (antes de ligar num stream)

O CLI subtitle (compilado com a feature whisper) roda exatamente o mesmo reconhecedor de produção num arquivo local, grava o resultado ao lado e, se o arquivo já tem uma faixa de legendas, calcula uma métrica de proximidade. É a forma mais rápida de escolher modelo e idioma e de avaliar a qualidade:

subtitle recognize movie.mkv --lang ru --model medium
# processar apenas um trecho (p. ex., 2 minutos a partir de 25:00) para uma verificação rápida:
subtitle recognize movie.mkv --lang ru --start-sec 1500 --limit-sec 120

Arquivos de saída, junto do arquivo de entrada:

  • movie.subtitles.json — quadros estruturados de legendas com procedência completa (janela de áudio, modelo, hash, prompt, diagnóstico por cue), o bastante para reproduzir uma execução;
  • movie.vtt — a projeção em WebVTT;
  • movie.diff.txt — um lado a lado do texto de referência contra o reconhecido (quando o arquivo tem uma faixa de legendas de referência).

Se o arquivo tem legendas de referência, o CLI imprime WER (word error rate) e CER (character error rate), globalmente e por janela. Atenção: contra uma faixa de legendas feita de forma independente, o WER mede a divergência de tradução, não o erro de ASR — leia-o como um sinal relativo entre execuções e olhe o diff.

Qualquer arquivo .mkv/.mp4 serve como entrada.

Verificação num stream

Depois de ligar o speech2text num stream com DVR:

  1. As informações de mídia do stream ganham uma faixa de legendas. Verifique a playlist mestra do HLS — ela agora contém um rendition EXT-X-MEDIA:TYPE=SUBTITLES, e as variantes o referenciam com SUBTITLES="subs":

    bash curl -s "http://server/streaming/<stream>/index.m3u8" | grep -i subtitle

    No DASH o manifesto ganha um AdaptationSet com contentType="text".

  2. Abra o arquivo num reprodutor com suporte a legendas (ou o URL de HLS/DASH) e ligue a faixa de legendas. As legendas aparecem com atraso, enquanto o worker alcança a borda ao vivo, e depois a acompanham.

  3. Acompanhe os contadores do reconhecimento no endpoint de métricas (segundos processados, tamanho da lacuna / atraso, erros).

Como o worker é stateless — o progresso é derivado da borda da faixa de legendas no arquivo —, depois de um restart ele continua da mesma lacuna, completa com legendas o arquivo já acumulado e nunca duplica um quadro.

Como funciona

A unidade de trabalho do worker é a lacuna entre a cobertura do áudio e a cobertura das legendas no arquivo. Ele lê o áudio da lacuna, decodifica-o em PCM mono de 16 kHz, roda o Whisper em janelas deslizantes (com um gate de silêncio contra alucinações e reancoragem em segmentos completos) e acrescenta quadros estruturados de legendas. O WebVTT entregue aos reprodutores é uma projeção desses quadros; os quadros armazenados guardam adicionalmente procedência e diagnóstico do Whisper para depuração.

Próximos passos