├── .gitignore ├── LICENSE ├── README.md ├── TODO.md ├── _config.yml └── imgs ├── qualities.png ├── rawfile.png └── testsrc2.gif /.gitignore: -------------------------------------------------------------------------------- 1 | *.mp4 2 | *.ts 3 | *.m3u8 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Demétrio de Castro Menezes Neto 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - [Meu primeiro stream](#meu-primeiro-stream) 2 | - [Ferramentas recomendadas](#ferramentas-recomendadas) 3 | - [Imagens docker necessárias](#imagens-docker-necessárias) 4 | - [Mãos a obra](#mãos-a-obra) 5 | - [Obtendo o vídeo base](#obtendo-o-vídeo-base) 6 | - [Importância do bitrate no streaming](#importância-do-bitrate-no-streaming) 7 | - [Adaptative bitrate para o resgate](#adaptative-bitrate-para-o-resgate) 8 | - [Gerando formatos de diversas qualidades a partir do vídeo base](#gerando-formatos-de-diversas-qualidades-a-partir-do-vídeo-base) 9 | - [Download Progressivo](#download-progressivo) 10 | - [Protocolos de streaming](#protocolos-de-streaming) 11 | - [Tocando o streaming](#tocando-o-streaming) 12 | 13 | # Meu primeiro stream 14 | 15 | Olá! Tudo bem? Espero que sim! :smiley: 16 | 17 | O objetivo dessa página é guiar você para entender um pouco de como funciona 18 | o mundo de entrega de vídeos. 19 | 20 | O foco é te fazer entender o fluxo básico necessário para fazer um streaming 21 | através da web. 22 | 23 | ## Ferramentas recomendadas 24 | 25 | * [VLC](https://www.videolan.org/vlc/index.pt-BR.html), FFPlay ou outro tocador de vídeos 26 | * [Docker](https://www.docker.com/get-started) 27 | * Google Chrome/Firefox ou outro navegador web de sua preferência 28 | 29 | ### Imagens docker necessárias 30 | Se você deseja seguir esse guia offline, baixe as imagens docker abaixo 31 | 32 | ```bash 33 | docker pull jrottenberg/ffmpeg:4.3-alpine 34 | docker pull webratio/nodejs-http-server:0.9.0 35 | ``` 36 | 37 | ## Mãos a obra 38 | 39 | ### Obtendo o vídeo base 40 | Antes de tudo, precisamos de um vídeo base para trabalhar. 41 | 42 | Geralmente o vídeo original vem de uma captura de câmera, ou softwares de 43 | edição ou renderização 3D. Ele é salvo em um formatos comumente chamados de 44 | **RAW**, ou arquivo bruto, que contém informações muito importantes pra quem 45 | vai editar e modificar, mas não interessam para o visualizador final na 46 | maioria das vezes. 47 | 48 | ![Imagem exemplificando a captura de vídeo sendo salva em um arquivo raw](imgs/rawfile.png) 49 | 50 | Nesse guia, iremos gerar um vídeo utilizando o **FFMPEG**, um software mais 51 | conhecido por ser o canivete suíco para se trabalhar com vídeos. Dessa 52 | maneira, você consegue seguir esse guia mesmo sem uma conexão com a internet. 53 | 54 | Caso queira utilizar arquivos de vídeo "de verdade", a Blender Foundation tem 55 | uma série de [vídeos abertos e gratuitos](https://www.blender.org/about/projects/) que possuem um licença que permite o uso livre deles. 56 | 57 | Para gerar um vídeo base utilizando o FFMPEG, basta executar o comando abaixo. 58 | 59 | ```bash 60 | docker run --rm -v $(pwd):/files "jrottenberg/ffmpeg:4.3-alpine"\ 61 | -hide_banner -y \ 62 | -re -f lavfi -i "testsrc2=size=1280x720:rate=30,format=yuv420p" \ 63 | -f lavfi -i "sine=frequency=1000:sample_rate=48000" \ 64 | -c:v libx264 -preset ultrafast -tune zerolatency -profile:v high \ 65 | -t 0:20 files/mps.mp4 66 | ``` 67 | 68 |
69 | Explicação do comando 70 | 71 | > * `-hide-banner`: Esconde o banner do ffmpeg 72 | > * `-re`: Lê a entrada obedecendo o framerate nativo 73 | > * `-f lavfi`: Define o formato para _lavfi_ (libavfilter). Através dele podemos usar um input virtual 74 | > * `-i "testsrc2=size=1280x720:rate=30,format=yuv420p"`: 75 | > * `testsrc2=size=1280x720`: Gerar o input virtual do tipo `testsrc2` com a resolução 1280x720 76 | > * `rate=30`: Frames por segundo (FPS) 77 | > * `format=yuv420p`: Padrão de cores utilizado 78 | > * `-i "sine=frequency=1000:sample_rate=48000"`: 79 | > * `sine=frequency=1000`: Define uma frequencia fixa de áudio no valor 1000 80 | > * `sample_rate=48000`: Define sample_rate de áudio para 48000 81 | > * `-c:v libx264`: Usa o encoder libx264 (versão opensource do H.264) 82 | > * `-preset ultrafast`: Utiliza um preset de configurações para realizar o encoding. 83 | > * `-tune zerolatency`: Realiza algumas modificações nas configurações de encoding 84 | > * `-profile:v high`: Profile do H.264, valor utilizado por padrão para dispositivos modernos 85 | > * `-t 0:20`: Duração do vídeo é igual a 20 segundos 86 | > * `files/mps.mp4`: Arquivo de saída 87 | > 88 | > Você pode ler mais sobre esses parâmetros na [documentação do FFMPEG](https://trac.ffmpeg.org/wiki/Encode/H.264) 89 |

90 | 91 | Se o comando foi executado corretamente, o resultado deve ser algo parecido 92 | com mostrado abaixo: 93 | 94 | ![Output de teste do ffmpeg](imgs/testsrc2.gif) 95 | 96 | ### Importância do bitrate no streaming 97 | 98 | O **bitrate de um vídeo** é basicamente quantos bits existem por segundo de 99 | vídeo exibido. A unidade de medida é bem parecida com a que vemos quando 100 | realizamos um download de arquivo (`[GMK]b/s`), geralmente acaba-se omitido o 101 | `/s`, ficando por exemplo `800k`. 102 | 103 | Quanto maior o bitrate de um vídeo, mais dados são trafegados/lidos por segundo, 104 | isso significa que quanto maior o bitrate, **geralmente**, maior a 105 | qualidade. _Geralmente_ porque o fato de mais dados estarem sendo transmitidos 106 | não significa necessariamente uma qualidade maior. 107 | 108 | Para ilustrar quão importante são os bitrates para o streaming, imagine que você 109 | quer assistir um vídeo cujo bitrate seja `3.5mbps`, mas sua capacidade de 110 | banda de rede é apenas `1mbps`. Você vai precisar esperar **3 segundos e meio** 111 | pra conseguir assistir a um segundo de vídeo. Imagine o tempo necessário para 112 | assistir um vídeo que possui horas de duração, como um filme. 113 | 114 | Uma maneira atual de contornar isso é gerar cópias do mesmo vídeo com 115 | diferentes bitrates, e entregar o mais adequado para a rede do espectador. 116 | Essa técnica é chamada de **adaptative bitrate** ou bitrate adaptativo. 117 | 118 | ### Adaptative bitrate para o resgate 119 | 120 | É das mais importantes features suportadas pelos protocolos de streaming 121 | hoje. Ela permite que o player consiga trocar entre diversas qualidades 122 | disponíveis de acordo com a banda de rede disponível. Melhorando a qualidade 123 | de experiência do usuário e o consumo de banda, entre outras coisas. 124 | 125 | Para o _adaptative bitrate_ funcionar, é necessário que sejam geradas cópias 126 | do vídeo inicial com bitrates adequados para a reprodução em diferentes 127 | dispositivos e redes. 128 | 129 | #### Gerando formatos de diversas qualidades a partir do vídeo base 130 | 131 | Vamos gerar 4 vídeos com bitrates e resoluções diferentes, a partir do nosso 132 | vídeo base. As informações estão na tabela abaixo: 133 | 134 | | Resolução |Bitrate de áudio | Bitrate de Vídeo | Bitrate total | 135 | |-----------|-----------------|------------------|---------------| 136 | | 240p | 96k | 700k | 796k | 137 | | 360p | 96k | 800k | 896k | 138 | | 480p | 128k | 1400k | 1528k | 139 | | 720p | 128k | 2800k | 3928k | 140 | 141 | ```bash 142 | docker run --rm -v $(pwd):/files "jrottenberg/ffmpeg:4.3-alpine" \ 143 | -hide_banner -y -i files/mps.mp4 \ 144 | -s 1280x720 -c:a aac -b:a 128k -c:v libx264 -b:v 2800k files/mps_720.mp4 \ 145 | -s 858x480 -c:a aac -b:a 128k -c:v libx264 -b:v 1400k files/mps_480.mp4 \ 146 | -s 640x360 -c:a aac -b:a 96k -c:v libx264 -b:v 800k files/mps_360.mp4 \ 147 | -s 352x240 -c:a aac -b:a 96k -c:v libx264 -b:v 700k files/mps_240.mp4 148 | ``` 149 | 150 |
151 | Explicação do comando 152 | 153 | > * `-i mps.mp4`: Arquivo de entrada 154 | > * `-s 1280x720`: Redimensiona o vídeo para a resolução 1280x720 155 | > * `-c:a aac`: Utiliza `aac` como codec de áudio 156 | > * `-b:a 128k`: Utiliza 128k como bitrate de áudio 157 | > * `-c:v libx264`: Utiliza `libx264` como codec de vídeo 158 | > * `-b:v 2800k`: Utiliza 2800k como bitrate de vídeo 159 |

160 | 161 | Parece que estamos com tudo preparado para fazer nosso streaming, certo? Mais 162 | ou menos. Com esses arquivos como estão, nós conseguimos realizar um 163 | **download progressivo**. 164 | 165 | #### Download Progressivo 166 | 167 | Apesar de um nome bonito, ou talvez assustador. O download progressivo 168 | acontece quando um player de vídeo realiza uma requisição HTTP para obter um 169 | arquivo de vídeo. O servidor divide esse arquivo em partes, entregando cada 170 | parte como **partial content** (status code 206). A medida que o player 171 | recebe esses pedaços de vídeo, a reprodução acontece. 172 | 173 | Para realizar um download progressivo iremos precisar de um servidor HTTP, 174 | aqui podemos aproveitar a praticidade do Docker e subir um *nginx* 175 | rapidamente, e de um tocador de vídeos. Inicialmente usaremos o *VLC*, além 176 | de ser aberto, ele já possui a maioria dos codecs embutidos para os mais 177 | variados formatos de vídeo. 178 | 179 | Subindo o servidor web: 180 | ```bash 181 | docker run --rm \ 182 | -v $(pwd):/opt/www \ 183 | -p 8080:8080 webratio/nodejs-http-server:0.9.0 \ 184 | http-server /opt/www -a :: -p 8080 --cors -c-1 185 | ``` 186 | 187 | Rodando um vídeo no VLC 188 | ```bash 189 | vlc http://localhost:8080/mps_240.mp4 190 | ``` 191 | 192 | Rodando um vídeo no ffplay 193 | ```bash 194 | ffplay http://localhost:8080/mps_240.mp4 195 | ``` 196 | 197 | Infelizmente, o download progressivo não nos oferece o recurso de ABR falado 198 | anteriormente. Isso significa que, se alguma alteração acontecer na rede, a 199 | reprodução pode ser prejudicada. Para termos suporte ao ABR, precisamos 200 | realizar a entrega utilizando um **protocolo de streaming**, além de realizar 201 | alguns comandos a mais com o FFMPEG. 202 | 203 | #### Protocolos de streaming 204 | 205 | Atualmente os três protocolos mais utilizados para streaming funcionam em 206 | cima do HTTP. Eles são: 207 | * HTTP Live-streaming (**HLS**), que, apesar do nome, também é utilizado para VODs; 208 | * Dynamic Adaptative Streaming over HTTP (**DASH** ou **MPEG-DASH**); e 209 | * Microsoft Smooth Streaming (**MSS**); 210 | 211 | **Nesse guia vamos focar no HLS**, por usar um arquivo de texto com um 212 | formato de fácil entendimento. A título de curiosidade, tanto o DASH como o 213 | MSS usam um padrão XML em seus arquivos. 214 | 215 | Uma coisa em comum em todos esses protocolos é que precisamos ter todos os 216 | _chunks_ ou pedaços de vídeo já divididos anteriormente. Existem ferramentas 217 | que já fazem esse trabalho _on the fly_ (o próprio FFMPEG é capaz de fazer 218 | isso), mas para fins de aprendizado, iremos executar um comando que irá gerar 219 | cada um desses _chunks_. 220 | 221 | Além dos _chunks_, os protocolos trabalham com um arquivo principal, chamado 222 | de _playlist_ ou _master playlist_. Eles servem como um índice para o player 223 | entender como montar as URLS para baixar cada pedaço de vídeo, a url base 224 | para cada qualidade disponível, entre outras informações. 225 | 226 | No caso do **HLS** a _master playlist_ indica qual a url para as 227 | playlists individuais de cada variante("qualidade") disponível, que são 228 | chamadas de "variant playlist" 229 | 230 | No comando abaixo iremos gerar tanto os pedaços/_chunks_ de vídeo, como a 231 | _master playlist_ e as playlists variantes. Isso porque é muito mais fácil 232 | gerar todos eles de uma vez, do que em comandos separados do FFMPEG. 233 | 234 | ```bash 235 | docker run --rm -v $(pwd):/files "jrottenberg/ffmpeg:4.3-alpine"\ 236 | -hide_banner -y -i files/mps_360.mp4 -i files/mps_480.mp4 -i files/mps_720.mp4 \ 237 | -map 0 -map 1 -map 2 \ 238 | -c:a:0 aac -ar 48000 -c:v:0 libx264 -x264opts keyint=30:min-keyint=30:scenecut=-1 -b:v:0 800k -b:a:0 96k \ 239 | -c:a:1 aac -ar 48000 -c:v:1 libx264 -x264opts keyint=30:min-keyint=30:scenecut=-1 -b:v:1 1400k -b:a:1 128k \ 240 | -c:a:2 aac -ar 48000 -c:v:2 libx264 -x264opts keyint=30:min-keyint=30:scenecut=-1 -b:v:2 2800k -b:a:2 128k \ 241 | -var_stream_map "v:0,a:0,name:360 v:1,a:1,name:480 v:2,a:2,name:720" \ 242 | -master_pl_name master.m3u8 \ 243 | -f hls -hls_time 2 -hls_list_size 0 \ 244 | -hls_segment_filename files/v%v_%03d.ts files/v%v.m3u8 245 | ``` 246 |
247 | Explicação do comando 248 | 249 | > * `-i _nome_.mp4`: Arquivo de entrada, pode ser repetido mais de uma vez para 250 | > mútiplos arquivos de entrada 251 | > * `-map X`: Indica que irá utilizar a entrada `X`, o numero do stream é 252 | > definido pela ordem de entrada na ordem dos arquivos. 0 para o primeiro, 253 | > 1 para o segundo e assim por diante 254 | > * `-s 1280x720`: Redimensiona o vídeo para a resolução 1280x720 255 | > * `-c:a:X aac`: Utiliza `aac` como codec de áudio do stream X 256 | > * `-b:a:X 128k`: Utiliza 128k como bitrate de áudio 257 | > * `-ar 48000`: Seta samplerate de áudio para 48000 258 | > * `-c:v:X libx264`: Utiliza `libx264` como codec de vídeo 259 | > * `-b:v:X 2800k`: Utiliza 2800k como bitrate de vídeo 260 | > * `-var_stream_map`: Indica como os streams de entrada devem ser utilizados 261 | para a saída. `"v:0,a:0"` significa que o stream de vídeo 0, deve ser utilizado 262 | com o stream de áudio 0. 263 | > * `-master_pl_name`: Nome da master playlist 264 | > * `-f hls`: Utiliza o formato HLS 265 | > * `-hls_time`: Define tamanho do chunk 266 | > * `-hls_list_size 0`: Tamanho da lista do hls. 0 = infinito 267 | > * `hls_segment_filename`: Padrão de nomes do arquivo dos chunks. Ex: `files/v360_001.ts` 268 | > * `files/v%v.m3u8`: Padrão de nomeclatura das playlists variantes. Ex: `files/v360.m3u8` 269 | > * `%v`: Nome do stream 270 | > * `-x264opts keyint=30:min-keyint=30:scenecut=-1`: Define o GOP para 30 e remove detecção de cenas 271 |

272 | 273 | ### Tocando o streaming 274 | 275 | Com todos os arquivos necessários disponíveis, agora podemos subir um servidor web e tocar em um player que suporte ABR. 276 | 277 | Para subir o servidor um web: 278 | ```bash 279 | docker run --rm \ 280 | -v $(pwd):/opt/www \ 281 | -p 8080:8080 webratio/nodejs-http-server:0.9.0 \ 282 | http-server /opt/www -a :: -p 8080 --cors -c-1 283 | ``` 284 | 285 | Com o servidor de pé, iremos utilizar a página de demonstração do [hls.js](https://github.com/video-dev/hls.js/). Um player feito em javascript para suportar o protocolo HLS. 286 | 287 | > [Clique aqui para acessar](https://hls-js.netlify.app/demo/?src=http%3A%2F%2Flocalhost%3A8080%2Fmaster.m3u8&demoConfig=eyJlbmFibGVTdHJlYW1pbmciOnRydWUsImF1dG9SZWNvdmVyRXJyb3IiOnRydWUsImR1bXBmTVA0IjpmYWxzZSwibGV2ZWxDYXBwaW5nIjotMSwibGltaXRNZXRyaWNzIjotMX0=) -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dneto/meu-primeiro-stream/3a80e3ac02f113bc8361468f160e40640014af9a/TODO.md -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: 'johno/pixyll' 2 | -------------------------------------------------------------------------------- /imgs/qualities.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dneto/meu-primeiro-stream/3a80e3ac02f113bc8361468f160e40640014af9a/imgs/qualities.png -------------------------------------------------------------------------------- /imgs/rawfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dneto/meu-primeiro-stream/3a80e3ac02f113bc8361468f160e40640014af9a/imgs/rawfile.png -------------------------------------------------------------------------------- /imgs/testsrc2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dneto/meu-primeiro-stream/3a80e3ac02f113bc8361468f160e40640014af9a/imgs/testsrc2.gif --------------------------------------------------------------------------------