Monitoramento com câmeras de vigilância do homem probre

Um amigo me perguntou se “alguém conhece algum software…” que crie uma grade com várias fontes de vídeo ao estilo de sistemas de monitoramento de segurança com câmeras… well… EU conheço: ffmpeg!

O ffmpeg é bem versátil: Ele permite a leitura e escrita em diversos meios, não apenas arquivos. Podemos usar protocolos como async, bluray, cache, concat, crypto, data, file, ftp, gopher, hls, http, httpproxy, https, mmsh, mmst, pipe, rtp, sctp, srtp, subfile, tcp, tls, udp, udplite, unix, rtmp, rtmpe, rtmps, rtmpt, rtmpte, sftp.

Suponha que você tenha 4 câmeras Go Pro, disponibilizando streams através de UDP. Em seu /etc/hosts elas serão conhecidas sob os nomes camera1.local, camera2.local, camera3.local e camera4.local e você quer criar um stream de vídeo com os 4 streams das câmeras numa grade 2×2… Os streams das câmeras serão obtidos via URL udp://camera1.local/stream, por exemplo e o stream de saída será enviado para o ffserver via URL http://localhost:8090/stream.ffm.

Esses são os nomes dos “arquivos” de entrada e saída que usaremos no ffmpeg. Mas, para facilitar a minha vida neste texto, para que eu não tenha que explicar como configurar o ffserver (leia a man page!), usarei “arquivos” fictícios de entrada nomeados 1.mp4, 2.mp4, 3.mp4 e 4.mp4 e o arquivo de saída será output.mp4, codificado com o codec h264 e sem áudio.

Para criarmos a grade podemos usar um filtro complexo (múltiplas entradas e uma saída), usando os filtros scale, hstack e vstack. O grafo do filtro fica assim:

Grafo para o filtro complexo

Os filtros hstack “empilham” dois ou mais entradas horizontalmente, enquanto vstack faz o mesmo verticalmente. Do jeito que o filtro está montado as fontes 1 e 2 ficarão na primeira linha e as 3 e 4 na segunda. Os filtros scale estão ai para garantir que todos os vídeos aplicados aos filtros de empilhamento terão o mesmo tamanho (por exemplo, 320×200). Assim, a linha de comando com o filtro complexo fica assim:

$ ffmpeg -i 1.mp4 -i 2.mp4 -i 3.mp4 -i 4.mp4 -an -c:v libx264 \
-filter_complex '[v:0]scale=320:200[t1];\
[v:1]scale=320:200[t2];\
[v:2]scale=320:200[t3];\
[v:3]scale=320:200[t4];\
[t1][t2]hstack[v1];\
[t3][t4]hstack[v2];\
[v1][v2]vstack[vout]' -map '[vout]' \
output.mp4

Se os vídeos tiverem duração diferentes, use a opção -shortest para usar a duração do menor deles, por exemplo… Isso não é problema se estivermos lidanco com streams.

E se eu tiver um número ímpar de fontes de vídeo?

Existe uma fonte “especial” chamada nullsrc, onde você pode especificar, num grafo, "nullsrc=s=320x200[vnull]" e a fonte vnull será um vídeo de 320×200 vazio.

Mas… ficou muito chapado!

Os filtros vstack e hstack colocam os vídeos lado a lado, sem espaçamento… Isso não significa que, ao invés de usar esses filtros, você não possa usar o filtro overlay para os vídeos, como mostrei neste post aqui, só que para imagens.

Você pode usar uma imagem PNG como vídeo de fundo, por exemplo, com “painéis”, criados com o GIMP onde os streams serão colocados. Basta posicionar os streams, depois de “escalonados” nos locais corretos.

Mas… Eu quero fazer um programinha!

Você terá que lidar com as bibliotecas da família libav, como mostrei neste post. Mas, atenção, o código do artigo não funciona mais… existem muitas diferenças entre as versões atuais das bibliotecas da família libav do que as da época em que escrevi aquele artigo! Consulte a documentação da versão de suas libs.

Além do mais… o código final ficará muito grande e complicado (teremos que obter frames de fontes diferentes, via rede e “servir” o stream final, trabalhado… Os escalonamentos e posicionamentos podem ainda ser feitos por filtros e, suspeito, que as bibliotecas sejam agnósticas quanto às fontes dos streams… mas, isso será mesmo uma complicação. Deixo para o leitor os detalhes de implementação sabendo que será uma pequena dor-de-cabeça.

O ffserver:

O usuário vai querer ver o stream final, ao invés dos individuais. O meio mais “simples” de fazer isso é usar o ffserver, que receberá o stream gerado acima e permitirá que um usuário conecte ao servidor usando seu player favorito (VLC, por exemplo).

O ffserver precisa de um arquivo de configuração informando os dados da conexão que ele esperará receber, bem como o formato do stream de saída. Ao invés de enviar a saída do ffmpeg para um arquivo output.mp4, enviamos para, por exemplo, http://localhost:8090/stream.ffm. Onde a porta 8090 é a usada (por default) pelo ffserver e stream.ffm o path definido no arquivo de configuração. Do lado do cliente, podemos ver o stream via URL (por exemplo): rtsp://<meuhost>/stream. Claro, depende de como você configura o ffserver.

Anúncios