Se você chegou aqui de paraquedas e ainda não leu a parte 1 clique aqui e aproveite.
No artigo anterior, nós criamos um servidor para Live Streaming utilizando apenas o Nginx com o módulo RTMP. Esta configuração é muito básica e, por isso precisamos de mais alguns passos.
Primeiro, utilizaremos a ferramenta FFmpeg para entregar múltiplas resoluções. Assim garantimos um vídeo contínuo e com qualidade ajustável de acordo com a banda de internet do cliente.
Segundo, para alcançar o mundo inteiro, precisamos da ajuda de um CDN(Content Delivery Network) para entregar a Live.
O que é ffmpeg?
É uma ferramenta open-source composta por diversas bibliotecas para conversão, compactação e edição. Saiba mais clicando aqui.
Porque precisamos de um CDN?
O CDN tem inicialmente o trabalho de reduzir a distância entre seu público e o seu conteúdo. Ele replica seus arquivos em diversos servidores localizados em pontos estratégicos pelo mundo. Então, quando o usuário solicita seu vídeo, ele não acessa o seu servidor e o sim um dos servidores do CDN que está mais próximo a ele.
Veja mais sobre aqui.
Vamos começar!
Download do código fonte aqui.
Como root execute os próximos comandos.
# Instale a dependência do H.264/MPEG-4 AVC encoder
apt install -y libx264-dev
# Crie pastas para guardar os arquivos dos videos e scripts.
mkdir -p /var/nginx/www/hls/p360
mkdir -p /var/nginx/www/hls/p720
mkdir -p /var/nginx/scripts
# Baixe a biblioteca do codec H.264/MPEG 4
apt install -y libx264-dev
# Vá para a pasta tmp
cd /tmp
# Baixe o ffmpeg e o descompacte
wget http://ffmpeg.org/releases/ffmpeg-4.2.2.tar.gz
tar -zxvf ffmpeg-4.2.2.tar.gz
# Entre na pasta descompactada
cd ffmpeg-4.2.2
# Execute estes passos para instalar o ffmpeg
./configure --disable-x86asm --enable-gpl --enable-libx264
make && make install && make distclean
O módulo RTMP pode ser configurado para executar um shell script no momento em que a transmissão é iniciada. Com esse gatilho iniciaremos o FFmpeg para começar a transcodificar.
Vá até a pasta /var/nginx/scripts.
Agora crie o arquivo live.sh
#!/bin/bash
on_die () {
# Mata o processo do FFmpeg para liberar o
# recurso para uma nova transmissão futura.
pkill -KILL -P $$
# Executa um script adicional para apagar arquivos
# não mais necessários
sh $SH_PATH/free.sh $HLS_OUT_PATH/p360
sh $SH_PATH/free.sh $HLS_OUT_PATH/p720
}
# Cria um gatilho para executar o método on_die quando
# o script for encerrado
trap 'on_die' TERM
# Variáveis com o caminho das as pastas
# para ajuda a simplificar o script
SH_PATH='/var/nginx/scripts'
HLS_OUT_PATH='/var/nginx/www/hls'
FFMPEG_LOG_OUTPUT='var/nginx/logs/fflog.txt'
# O comando entrega duas resoluções. Uma 360p e outra 720p.
# O objetivo é entregar um vídeo contínuo e que atenda
# a banda de internet do seu publico.
ffmpeg -re -i rtmp://localhost:1935/$1/$2 -async 1 -vsync -1 \
-filter_complex "[v:0]split=2[360p][720p];[360p]scale=w=640:h=360[p360];[720p]scale=w=1280:h=720[p720]" \
-map [p360] -map 0:a -preset veryfast -c:v libx264 -x264opts keyint=48:no-scenecut -sws_flags bilinear -pix_fmt yuv420p -tune zerolatency -c:a aac \
-r 24 -crf 23 -profile:v main -b:v 800K -b:a 64k -ar 44100 -maxrate 864k \
-f hls -hls_time 4 -start_number 0 -hls_list_size 10 -hls_flags delete_segments \
-hls_segment_filename $HLS_OUT_PATH/p360/segment_%03d.ts $HLS_OUT_PATH/p360/master.m3u8 \
-map [p720] -map 0:a -preset veryfast -c:v libx264 -x264opts keyint=48:no-scenecut -sws_flags bilinear -pix_fmt yuv420p -tune zerolatency -c:a aac \
-r 24 -crf 23 -profile:v main -b:v 1500k -b:a 96k -ar 44100 -maxrate 1596k \
-f hls -hls_time 4 -start_number 0 -hls_list_size 10 -hls_flags delete_segments \
-hls_segment_filename $HLS_OUT_PATH/p720/segment_%03d.ts $HLS_OUT_PATH/p720/master.m3u8 2> $FFMPEG_LOG_OUTPUT &
wait
Crie também o free.sh
#!/bin/bash
# O FFmpeg está configurado para apagar automaticamente os arquivos de .ts não utilizados.
# Porém, mesmo com essa configuração ainda fica algum lixo.
cd $1
mkdir tmp
cat master.m3u8 | grep ts | xargs -I {} cp {} tmp/
rm -rf *.ts
cp tmp/* ./
rm -rf tmp
# Torne os scripts executáveis
chmod +x live.sh free.sh
Agora o passo mais importante! Precisamos alterar o nginx.conf na pasta /usr/local/nginx/conf/ para utilizar os scripts que criamos.
nginx.conf
user nginx nginx;
worker_processes auto;
worker_rlimit_nofile 100000;
error_log /var/nginx/logs/error.log;
events {
worker_connections 4000;
multi_accept on;
}
rtmp {
server {
listen 1935;
ping 30s;
chunk_size 4096;
application stream {
live on;
record all;
record_path /var/nginx/rec;
record_suffix -%d-%b-%y-%T.flv;
record_unique off;
# Executa o script live.sh quando um cliente conecta para transmitir
exec bash /var/nginx/scripts/live.sh $app $name;
# Quando a transmissão encerra o nginx envia o sinal de "terminate" para o script.
exec_kill_signal term;
}
}
}
http {
default_type application/octet-stream;
gzip on;
server {
listen 80 default_server;
root /var/nginx/www;
location /hls {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
add_header Cache-Control no-cache;
}
location / {
add_header Cache-Control no-cache;
index index.html;
}
}
}
Para finalizar a configuração crie mais dois arquivos na pasta /var/nginx/www.
O primeiro arquivo master.m3u8 contém o caminho para as duas resoluções que estamos disponibilizando.
master.m3u8
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
p360/master.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
p720/master.m3u8
O segundo arquivo é uma página HTML com o player Plyr.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HLS Player</title>
<link rel="stylesheet" href="https://cdn.plyr.io/3.6.2/plyr.css" />
</head>
<body>
<style>
body {
background-color: black;
padding: 0;
margin: 0;
}
.center {
margin: auto;
width: 50%;
}
</style>
<div class="center" >
<video preload="none" id="player" autoplay controls crossorigin data-plyr-config='{ "title": "Example Title", "settings": ["quality"] }'></video>
</div>
<script src="https://cdn.plyr.io/3.6.2/plyr.js"></script>
<script src="https://cdn.jsdelivr.net/hls.js/latest/hls.js"></script>
<script>
(function () {
var video = document.querySelector('#player');
const defaultOptions = {};
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource('http://super-live-demo.b-cdn.net/hls/master.m3u8');
hls.on(Hls.Events.MANIFEST_PARSED, function (event, data) {
const availableQualities = hls.levels.map((l) => l.height)
defaultOptions.quality = {
default: availableQualities[0],
options: availableQualities,
forced: true,
onChange: (e) => updateQuality(e),
}
const player = new Plyr(video, defaultOptions);
});
hls.attachMedia(video);
window.hls = hls;
} else {
const player = new Plyr(video, defaultOptions);
}
function updateQuality(newQuality) {
window.hls.levels.forEach((level, levelIndex) => {
if (level.height === newQuality) {
console.log("Found quality match with " + newQuality);
window.hls.currentLevel = levelIndex;
}
});
}
})();
</script>
</body>
</html>
Execute o comando chown nginx:nginx -R /var/nginx/* como root e entregue ao usuário do nginx o controle de todas as pastas e arquivos que criamos.
Finalize com o comando Execute "systemctl reload nginx.service" para reiniciar o nginx.
CDN
Para este artigo escolhi o BunnyCDN pela simplicidade de configuração, simplicidade na precificação e API. Você tem 14 dias gratuitos para testar.
TESTES!
Abra seu app de Streaming favorito e o configure com o endereço rtmp://YOUR_SERVER_ADDRESS:1935/stream/{SOME_KEY}. Nesta configuração somente uma pessoa pode transmitir por vez.
Caso você não tenha nenhum app favorito, deixo duas recomendações.
O OBS Studio e o Larix Broadcaster.
E para assistir, basta acessar a URL do seu servidor ou a URL do CDN no navegador (Ex: http://YOUR_SERVER_ADDRESS).
Como de costume, deixo aqui um vídeo com o teste.
RESULTADOS
Na imagem a seguir temos os arquivos .ts gerados pelo módulo RTMP.
Nesta imagem temos os arquivos gerados pelo FFmpeg na resolução 360p.
Agora também, temos os arquivos gerados pelo FFmpeg na resolução 720p.
Diante deste teste, entendo que comparando os novos arquivos produzidos pelo FFmpeg com os originais (do módulo RTMP), é notável a diferença de tamanho entre eles. Portanto, quando você entrega arquivos menores, você economiza dinheiro no tráfego de internet e seu público recebe uma Live mais estável, pois com um arquivo menor se usa menos banda larga. Além disso, com o uso do CDN a estabilidade aumenta porque seu usuário acessa o vídeo de servidores bem preparados para distribuição de arquivos aliviando esta carga do seu servidor.
Top comments (14)
Parabéns Paulo muito bom o artigo.
Sabe me dizer em média a quantidade de espectadores que essa configuração sugerida por você suportaria?
O CDN seria apenas para distribuir o conteúdo estático do site ou estou enganado, seria para distribuir os arquivos do stream?
Seria para distribuir os arquivos estáticos e os arquivos do stream.
Muito fera hein Paulo, parabéns mesmo ! Agora aquela pergunta capciosa:
Conhece um jeito de fazer tudo isso e gerar a saída em LL-HLS fazendo a entrega de streaming também neste NGINX opensource ? Pq normalmente ele só vai suportar o HLS default (latência é muito muito mais alta). Parece que NGINX+ suporta mas é caríssimo, preciso de algo opensource mesmo. Estou aqui varando a madruga buscando a solução...Se souber, compartilha com a galera pq vai ter um utilidade incrível.
Obrigado.
Da uma olhada no ffmpeg. Provavelmente nele vc consegue configurar esse formato na saída.
Eu nao consigo assistir... no seu index tem um super-live-demo.b-cdn.net/hls/mast... e eu substitui pro meu.... mas da erro
o master eu tenho que substituir pelo key ?
Desculpe a demora. Você conseguiu assistir direto no IP do seu servidor? Nesta configuração, a key é necessária para transmitir, porém, não para assistir.
Qualquer texto
Olá Amigo, parabens pelo arquivo. Bem detalhado.
é possivel criar uma interface web onde cria outros streaming e tambem envio para plataformas de rede social?
Plataformas de rede social como Facebook já tem esse serviço. Você precisa de apenas softwares como OBS Studio para fazer as lives.
Sobre a interface web você teria que criar.
To querendo validar uma ideia, mas comecei a pesquisar sobre isso agora. tenho como colocar uma autenticaçao para que impeça que alguém capture a url da transmissão? na real estou bem perdido
Você pode criar um token e validar ele no nginx.
ótimo artigo, está de parabéns Paulo Porto!
Paulo,
vc sabe informa qual seria o tamanho do arquivo gerado após 1h hora de live ?
Opa, boa noite. Isso varia com a resolução + qualidade de áudio.