Transmita mídia remota para o FFmpeg com cURL
Passe um download HTTP do cURL para o FFmpeg por pipe e extraia miniaturas e áudio sem manter uma cópia local do vídeo de entrada. O MP4 precisa permitir leitura sequencial. Este passo a passo cria um clipe de teste conhecido, serve esse clipe por HTTP e processa a resposta em JPEGs e um arquivo WAV.
Visão geral
Use Bash no Linux, cURL, Python 3 e um build do FFmpeg com os encoders libx264 e AAC. Os
comandos abaixo foram testados com o FFmpeg 6.1.1 e 9.0.1. O Python fornece o servidor HTTP local e
é desnecessário quando você já tem uma URL remota adequada.
O exemplo cria um MP4 “faststart”, com os metadados moov antes dos dados de mídia. A
documentação de MP4 do FFmpeg explica esse
layout. Um pipe não consegue voltar atrás na leitura (seek), então um MP4 comum com os metadados no
final pode falhar mesmo depois que o cURL tiver enviado o arquivo inteiro. Quando o layout for
desconhecido, use o exemplo com download prévio mais abaixo. Adicionar -f mp4 identifica o
formato; isso não permite saltos (seek) na entrada.
Execute cada bloco no Bash a partir do mesmo diretório pai. Os parênteses contêm mudanças de opções do shell em um subshell. Cada bloco de processamento exige um novo diretório de saída e se recusa a executar de novo em um diretório existente. Execuções com falha podem deixar arquivos parciais nesse novo diretório; inspecione esses arquivos antes de usá-los e escolha um novo diretório para outra tentativa.
Configurando o pipeline
Prepare um clipe e uma origem HTTP
Crie um clipe de 6,4 segundos: vermelho por dois segundos, verde-limão por dois segundos e depois azul. O áudio fica em silêncio até um tom curto de 880 Hz nos 0,2 segundos finais, o que torna audível uma extração de áudio incompleta.
(
set -euo pipefail
mkdir curl-media-demo
ffmpeg -nostdin -n -hide_banner -loglevel warning -filter_threads 1 \
-f lavfi -i "color=c=red:s=160x90:r=10:d=6.4" \
-f lavfi -i "aevalsrc='if(gte(t,6.2),0.25*sin(2*PI*880*t),0)':s=48000:d=6.4" \
-vf "drawbox=c=lime:t=fill:enable='gte(t,2)',drawbox=c=blue:t=fill:enable='gte(t,4)'" \
-map 0:v:0 -map 1:a:0 -c:v libx264 -pix_fmt yuv420p -threads 1 \
-c:a aac -movflags +faststart curl-media-demo/input.mp4
)
Neste terminal, sirva apenas o diretório do clipe de teste no endereço de loopback:
(
set -eu
test -f curl-media-demo/input.mp4
python3 -m http.server 8765 --bind 127.0.0.1 --directory curl-media-demo
)
Mantenha esse servidor em execução enquanto usa um segundo terminal para os próximos blocos. Se a porta 8765 estiver ocupada, escolha uma porta disponível e altere-a tanto no comando do servidor quanto nas URLs. Pare o servidor com Ctrl+C ao terminar.
Extraia miniaturas e áudio de um único download
No segundo terminal, volte ao mesmo diretório pai e execute:
(
set -euo pipefail
mkdir outputs
curl -fsSL "http://127.0.0.1:8765/input.mp4" | \
ffmpeg -nostdin -n -hide_banner -loglevel warning -filter_threads 1 -i pipe:0 \
-map 0:v:0 -vf "fps=1" -c:v mjpeg -q:v 2 -threads 1 -frame_pts 1 \
outputs/thumbnail_%03d.jpg \
-map 0:a:0 -c:a pcm_s16le outputs/audio.wav
)
Para este clipe de teste, espere seis JPEGs de 160×90, chamados de thumbnail_000.jpg a thumbnail_005.jpg.
Os dois primeiros são vermelhos, os dois seguintes verde-limão e os dois últimos azuis. Reproduza
outputs/audio.wav: ele deve ficar em silêncio até o tom perto do fim da gravação de 6,4 segundos. A
decodificação AAC pode adicionar uma pequena quantidade de preenchimento no final do WAV.
Explicação
pipe:0 lê a entrada padrão. Opções -map explícitas selecionam os primeiros streams de vídeo
e de áudio, com uma saída para cada um. Se o vídeo não tiver áudio, o mapeamento de áudio
obrigatório falha, em vez de o WAV ser omitido silenciosamente. A verificação de diretório novo
protege a sequência de imagens contra execuções anteriores. O -n do FFmpeg protege os
nomes de arquivo MP4 e WAV; uma sequência image2 pode substituir JPEGs individuais. Mantenha a
proteção de diretório e execute estes exemplos em sequência.
O filtro fps descarta ou duplica quadros para produzir
um quadro por segundo. O arredondamento padrão dele gera seis quadros para este clipe de teste,
então a fração final de segundo não ganha uma miniatura separada. -frame_pts 1 usa os timestamps
de apresentação da saída como números nos nomes de arquivo, na base de tempo da saída. Aqui essa
base é de um segundo, então os números representam tempos de saída de 0–5 segundos. Eles não são
números de quadro originais nem garantem qual quadro exato da origem foi escolhido. Veja as
opções do image2.
Com o -f do cURL, um erro HTTP como 404 faz a transferência falhar, enquanto -sS oculta
o medidor de progresso e mantém as mensagens de erro. O pipefail do Bash propaga um download com
falha mesmo que o FFmpeg termine com sucesso. Nenhuma das opções comprova que o servidor enviou
mídia íntegra.
Usos avançados
Selecione mudanças de cena e registre seus tempos
Para amostrar mudanças de cor em vez de usar uma taxa fixa, execute um download separado em um novo diretório:
(
set -euo pipefail
mkdir scenes
curl -fsSL "http://127.0.0.1:8765/input.mp4" | \
ffmpeg -nostdin -n -hide_banner -loglevel warning -filter_threads 1 -i pipe:0 \
-map 0:v:0 \
-vf "select='gt(scene,0.3)',metadata=print:key=lavfi.scene_score:file=scenes/time.txt" \
-fps_mode vfr -c:v mjpeg -q:v 2 -threads 1 scenes/scene_%03d.jpg
)
Para este clipe, espere que scene_001.jpg seja verde-limão e scene_002.jpg seja azul. As entradas
pts_time correspondentes em scenes/time.txt são 2 e 4 segundos. Esses são os timestamps dos quadros
selecionados na linha do tempo de entrada do FFmpeg, que começa em zero para este clipe de teste.
Eles não são horários reais de captura, e os números sequenciais dos JPEGs não são timestamps.
O filtro select compara uma pontuação
de cena com o limite. Uma pontuação acima de 0,3 é uma heurística para uma mudança visual, não um
limite semântico de cena. Um clipe pode não produzir nenhum quadro selecionado. -fps_mode vfr evita
preencher as lacunas entre os quadros selecionados com duplicatas.
Solução de problemas
Se o pipe relatar um arquivo parcial ou não encontrar pacotes utilizáveis, o MP4 pode precisar de seek. Baixe o arquivo em um diretório novo e depois processe o arquivo realmente baixado. Isso também separa falhas de rede de falhas de decodificação:
(
set -euo pipefail
mkdir download
curl -fsSL --retry 3 "http://127.0.0.1:8765/input.mp4" -o download/input.mp4
ffmpeg -nostdin -n -hide_banner -loglevel warning -filter_threads 1 -i download/input.mp4 \
-map 0:v:0 -vf "fps=1" -c:v mjpeg -q:v 2 -threads 1 -frame_pts 1 \
download/thumbnail_%03d.jpg \
-map 0:a:0 -c:a pcm_s16le download/audio.wav
)
Com a URL de teste, isso produz as mesmas miniaturas e o mesmo áudio do pipe. Use as novas
tentativas do cURL com -o para o download de um arquivo, e não com um pipe para um
decodificador. O cURL não consegue desfazer bytes já gravados em um pipe antes de tentar novamente;
a documentação de novas tentativas dele
descreve essa distinção. O bloco para antes da decodificação se o download falhar.
Uma resposta HTTP 200 e um status de saída zero ainda não comprovam que a mídia está completa. O FFmpeg pode relatar danos na decodificação e mesmo assim retornar zero, ou recuperar quadros e amostras danificados. Leia os avisos dele, compare a quantidade e as cores esperadas das miniaturas e verifique a duração do áudio e o tom final. Para sua própria mídia, use expectativas conhecidas de duração e conteúdo ou um checksum confiável fornecido pelo publicador; estes exemplos não fazem verificação de integridade. Baixar antes resolve o problema de seek, não a corrupção.
Considerações de desempenho
O pipe evita armazenar o MP4 de entrada, mas os JPEGs e o WAV sem compressão ainda ocupam espaço em disco. O FFmpeg processa os dados à medida que eles chegam; a velocidade da rede e a da decodificação determinam a rapidez com que isso acontece. Trata-se do download de um arquivo por HTTP, não de uma garantia de comportamento em tempo real ou de transmissão ao vivo. Reduzir a taxa de miniaturas diminui o número de imagens salvas, enquanto a detecção de cena ainda examina os quadros decodificados.
Para outro contêiner de entrada, consulte a documentação do protocolo pipe do FFmpeg e verifique se o demuxer consegue consumi-lo sequencialmente. Mantenha o caminho com download prévio disponível quando você não puder controlar o layout do MP4.
