Fluxo de trabalho de legendas via CLI: gerar, converter e fixar
Forneça ao script abaixo um diretório de vídeos MP4 com fala em inglês. Ele produz legendas SRT com marcações de tempo, uma cópia em WebVTT, um MKV com uma faixa de legendas separada e um MP4 com as legendas gravadas na imagem dos quadros. O Whisper.cpp faz a transcrição na CPU; o FFmpeg faz a conversão das legendas e gera os vídeos de saída. Trate os resultados como rascunhos a revisar antes da publicação.
Escolha entre legendas selecionáveis e fixas
| Faixa de legendas selecionável | Legendas fixas | |
|---|---|---|
| O espectador pode desativar | Sim, em um reprodutor compatível com a faixa | Não |
| Alteração do texto | Substituir a faixa sem codificar o vídeo | Codificar um novo vídeo |
| Processamento do vídeo | Copiar o fluxo de vídeo existente | Renderizar o texto e codificar novos quadros |
| Requisito de reprodução | Compatibilidade com o contêiner e as legendas | Compatibilidade com o codec de vídeo de saída |
Use o MKV para ter uma faixa selecionável e o MP4 com legendas fixas quando o texto precisar ficar visível sem um controle para ativar ou desativar as legendas. Nenhuma das opções garante que todos os reprodutores sejam compatíveis com o vídeo resultante. O tamanho do arquivo depende da origem e das configurações de codificação; fixar o texto nem sempre aumenta esse tamanho.
Instale as ferramentas para transcrição na CPU
Use um sistema Linux com Bash e permissão para instalar pacotes. Estes comandos são voltados ao
Ubuntu 24.04 e usam a versão v1.9.4 do Whisper.cpp.
O modelo tiny.en, exclusivo para inglês, mantém este exemplo pequeno, mas não
garante precisão. Você precisa de acesso à internet para a instalação e o download do modelo;
depois, a transcrição é executada localmente.
Instale o FFmpeg, um compilador C++, o CMake e uma fonte para renderização:
(
sudo apt-get update &&
sudo apt-get install -y build-essential cmake curl ca-certificates tar ffmpeg fonts-liberation
)
Em um diretório de trabalho com permissão de escrita, crie subtitle-tools. O subshell
mantém seu diretório atual e as opções do shell inalterados. Se esse diretório já existir, a
configuração será interrompida antes de instalar ou gravar qualquer coisa nele; escolha outro
diretório de trabalho em vez de excluir o trabalho existente.
(
mkdir subtitle-tools &&
cd subtitle-tools &&
curl -fsSLo whisper.tar.gz https://github.com/ggml-org/whisper.cpp/archive/refs/tags/v1.9.4.tar.gz &&
mkdir source &&
tar -xzf whisper.tar.gz -C source --strip-components=1 &&
cmake -S source -B build -DCMAKE_BUILD_TYPE=Release \
-DGGML_CUDA=OFF -DGGML_VULKAN=OFF -DGGML_NATIVE=OFF \
-DBUILD_SHARED_LIBS=OFF -DWHISPER_BUILD_TESTS=OFF &&
cmake --build build --target whisper-cli -j2 &&
curl -fsSLo ggml-tiny.en.bin \
https://huggingface.co/ggerganov/whisper.cpp/resolve/5359861c739e955e79d9a303bcbc70fb988958b1/ggml-tiny.en.bin &&
printf '%s %s\n' \
921e4cf8686fdd993dcd081a5da5b6c365bfde1162e72b08d75ac75289920b1f \
ggml-tiny.en.bin | sha256sum -c -
)
Isso usa o fluxo de trabalho de compilação com CMake e preparação de áudio do Whisper.cpp. Sua CLI aceita o WAV PCM mono de 16 kHz e 16 bits que o script extrai de cada vídeo. Outras interfaces para o Whisper, incluindo o SubsAI, têm sua própria sintaxe de comandos; elas não são necessárias aqui.
Para testar o fluxo de trabalho sem fornecer uma gravação, crie um vídeo a partir da amostra de discurso de JFK incluída na versão. Execute o comando no mesmo diretório de trabalho usado na configuração:
(
mkdir demo-videos &&
ffmpeg -nostdin -n -v error -f lavfi -i color=c=black:s=640x360:r=24 \
-i ./subtitle-tools/source/samples/jfk.wav \
-map 0:v:0 -map 1:a:0 -c:v libx264 -threads 2 -c:a aac -shortest \
./demo-videos/example.mp4
)
Automatize tudo com um script de shell
Salve o conteúdo abaixo como subtitle-tools/subtitles.sh. Ele processa apenas arquivos regulares
diretamente no diretório informado, com o sufixo .mp4 em minúsculas, e ignora
links simbólicos. Cada arquivo de entrada recebe um novo diretório ao lado dele, como
example.mp4.subtitles. Há suporte a espaços, aspas e hífens iniciais nos nomes de arquivos;
caminhos com caracteres de quebra de linha são rejeitados. Execute um lote por vez.
Os nomes fixos dos arquivos em cada diretório de resultados mantêm o caminho original fora da sintaxe específica dos filtros do FFmpeg. O script mantém uma cópia da origem e um arquivo de áudio PCM, portanto reserve espaço em disco para eles e para os dois vídeos de saída.
#!/usr/bin/env bash
set -euo pipefail
export LC_ALL=C
fail() { printf '%s\n' "$*" >&2; exit 1; }
[[ $# -eq 1 ]] || fail 'Usage: bash subtitles.sh VIDEO_DIRECTORY'
[[ $PWD != *$'\n'* && $1 != *$'\n'* && ${BASH_SOURCE[0]} != *$'\n'* ]] ||
fail 'Newline characters in paths are not supported'
input_directory=$1
[[ $input_directory == /* ]] || input_directory=$PWD/$input_directory
case ${BASH_SOURCE[0]} in
*/*) tool_directory=${BASH_SOURCE[0]%/*} ;;
*) tool_directory=. ;;
esac
cd -P -- "$tool_directory"
tool_directory=$PWD
[[ $tool_directory != *$'\n'* ]] || fail 'Newline characters in paths are not supported'
whisper=$tool_directory/build/bin/whisper-cli
model=$tool_directory/ggml-tiny.en.bin
[[ -x $whisper && -s $model ]] || fail 'Complete the subtitle-tools setup first'
cd -P -- "$input_directory"
[[ $PWD != *$'\n'* ]] || fail 'Newline characters in paths are not supported'
shopt -s nullglob
process_video() (
local video=$1
local output=$video.subtitles
mkdir -- "$output"
cp -- "$video" "$output/source.mp4"
cd -- "$output"
ffmpeg -nostdin -n -v error -xerror -i source.mp4 -map 0:a:0 \
-ar 16000 -ac 1 -c:a pcm_s16le audio.wav
"$whisper" --no-gpu -t 2 -p 1 -l en -m "$model" -f audio.wav -osrt -of captions
[[ -s captions.srt ]] || fail 'No subtitle file was produced'
grep -q -- '-->' captions.srt || fail 'No timed captions were produced; check for speech'
ffmpeg -nostdin -n -v error -i captions.srt -map 0:s:0 -c:s webvtt captions.vtt
ffmpeg -nostdin -n -v error -i source.mp4 -i captions.srt \
-map 0:v:0 -map 0:a:0 -map 1:s:0 -c copy -metadata:s:s:0 language=eng soft.mkv
ffmpeg -nostdin -n -v error -xerror -i source.mp4 \
-vf "subtitles=captions.srt:force_style='Fontname=Liberation Sans,FontSize=24'" \
-map 0:v:0 -map 0:a:0 -c:v libx264 -threads 2 -crf 20 -preset veryfast \
-c:a copy burned.mp4
)
count=0
current_video='setup'
trap 'status=$?; if [[ $status -ne 0 ]]; then printf "Failed while processing: %s\n" "$current_video" >&2; fi' EXIT
for video in "$PWD"/*.mp4; do
[[ -f $video && ! -L $video ]] || continue
[[ $video != *$'\n'* ]] || fail 'Newline characters in paths are not supported'
current_video=$video
printf 'Processing: %s\n' "$video"
process_video "$video"
count=$((count + 1))
printf 'Created: %s.subtitles\n' "$video"
done
[[ $count -gt 0 ]] || fail 'No regular .mp4 files found'
Execute o script no diretório de trabalho em que você instalou as ferramentas, que é diferente do diretório de entrada:
bash ./subtitle-tools/subtitles.sh ./demo-videos
Substitua ./demo-videos pelo seu diretório de gravações. O script resolve esse
diretório antes de selecionar os arquivos; ele não processa os MP4 do diretório de onde você o
chamou. Você também pode passar caminhos absolutos tanto para o script salvo quanto para o diretório
de entrada.
Um diretório vazio retorna um erro. Um vídeo sem fluxo de áudio, um erro de decodificação ou uma falha em uma conversão posterior interrompe o lote e identifica o arquivo de entrada atual. Uma falha pode deixar arquivos parciais no diretório de resultados. Diretórios de resultados existentes causam um erro antes que o clipe seja copiado ou transcrito, então novas execuções preservam as saídas anteriores. Este script não retoma o processamento: para tentar novamente com um clipe corrigido, coloque-o em um diretório separado, sem resultados existentes.
Áudio silencioso não é o mesmo que ausência de um fluxo de áudio. O modelo pode não retornar nenhum
bloco de legenda ou retornar rótulos para trechos sem fala ou retornar texto inventado. Por exemplo,
tiny.en pode produzir um bloco de legenda [BLANK_AUDIO] que se
estende além da gravação. O script verifica se há blocos de legenda com marcações de tempo; ele não
valida as palavras nem os tempos. Ouça o áudio e confira o término dos blocos em relação à duração
do vídeo antes de publicar.
O FFmpeg também pode recuperar algumas mídias danificadas em vez de informar um erro. Comandos executados com sucesso não certificam a integridade da entrada nem a precisão da transcrição.
Confira as legendas SRT e WebVTT
Em demo-videos/example.mp4.subtitles, abra captions.srt e
captions.vtt. O SRT tem blocos numerados com milissegundos separados por vírgula;
o VTT começa com WEBVTT e usa pontos decimais nas marcações de tempo. Ambos
devem conter as frases “ask not what your country can do for you” e “ask what you can do for your
country” da amostra. Confira as palavras com o áudio, especialmente os nomes, a pontuação e o início
e o fim de cada bloco de legenda.
A conversão no script usa o codificador de legendas webvtt do FFmpeg. Você não
precisa do Subtitle Edit CLI para essa conversão de SRT para WebVTT. Um arquivo WebVTT separado está
pronto para ser usado como faixa de legendas em um reprodutor web compatível; gerar apenas o arquivo
não o integra a um site.
Confira a faixa de legendas selecionável
Abra soft.mkv em um reprodutor compatível com Matroska e legendas SRT e, em
seguida, selecione a faixa de legendas em inglês. O script mapeia explicitamente o primeiro fluxo de
vídeo, o primeiro fluxo de áudio e as legendas geradas. O
modo de cópia de fluxos -c copy do FFmpeg copia
esses fluxos para o MKV sem codificar o vídeo. Isso realiza a multiplexação sem uma instalação
adicional do MKVToolNix.
Fixe (incorpore permanentemente) legendas com FFmpeg
Abra burned.mp4 sem ativar um arquivo externo de legendas. O texto já deve
aparecer na imagem do vídeo. O filtro subtitles, baseado em libass
do FFmpeg renderiza o SRT, e libx264 codifica os quadros alterados. É por isso
que fixar legendas exige codificação de vídeo, enquanto adicionar uma faixa selecionável não exige.
O áudio é copiado nas duas saídas.
Para a amostra com fundo preto, você também pode decodificar um quadro na marca de dois segundos.
O novo preview.png deve conter texto branco visível perto da borda inferior;
nesse instante, o vídeo de origem é inteiramente preto:
(
cd ./demo-videos/example.mp4.subtitles || exit
[[ ! -e preview.png && ! -L preview.png ]] || { printf 'preview.png already exists\n' >&2; exit 1; }
ffmpeg -nostdin -n -v error -ss 2 -i burned.mp4 -frames:v 1 -threads 2 preview.png
)
A opção -n não permite substituir uma
prévia existente. Editar captions.srt depois não altera nenhum dos vídeos nem o VTT
que você já gerou. Mantenha as legendas corrigidas e renderize novas saídas antes de publicá-las.
Dicas de desempenho e escalabilidade
Meça a transcrição e a fixação das legendas separadamente nas suas próprias gravações. Este exemplo usa intencionalmente duas threads de CPU e nenhuma GPU; ele não prevê a velocidade de processamento em outros hardwares. Se o rascunho deixar de transcrever alguma fala, compare os resultados de um modelo maior para inglês em um clipe representativo e revise essa saída também. Um modelo maior não substitui a revisão da transcrição.
Guarde o SRT revisado junto com a gravação para poder mudar o formato de entrega sem transcrever novamente. O script em lote sempre faz a transcrição para novos diretórios de resultados; ele não reutiliza suas correções automaticamente.
Conclusão
Se você já tem legendas revisadas e quer um serviço gerenciado para fixar legendas, nosso
Robot 🤖 /video/subtitle aceita
legendas SRT ou WebVTT existentes. Ele não gera a transcrição. Faça o upload do vídeo no campo de
formulário input_video e do arquivo de legendas em input_srt;
estas Assembly Instructions mantêm essas entradas
separadas:
{
"steps": {
"subtitle_video": {
"robot": "/video/subtitle",
"use": {
"steps": [
{ "name": ":original", "fields": "input_video", "as": "video" },
{ "name": ":original", "fields": "input_srt", "as": "subtitles" }
]
},
"subtitles_type": "burned",
"font_size": 24,
"position": "bottom",
"font_color": "FFFFFF",
"border_style": "outline"
}
}
}
