Vídeo para Web
Com o amplo suporte ao HTML5 nos navegadores mais modernos todos tem a capacidade de entender a tag <video> que permite colocar o elemento na página, porém não é qualquer formato de vídeo que pode ser distribuído, já limitações de acordo com o navegador e sua versão. Então antes de colocar o vídeo na web precisamos convertê-lo para um formato suportado.
Formatos Suportados
O formato MP4 é o que hoje possui o suporte mais abrangente, mas uma das vantagens da tag <video> do HTML5 é permitir especificar diversos arquivos de vídeo, assim o navegador irá escolher automaticamente o arquivo com o formato suportado. De acordo com o site da W3Schools os formatos suportados para cada navegador são:
Navegador |
MP4 |
WebM |
Ogg |
Internet Explorer |
SIM |
NÃO |
NÃO |
Chrome |
SIM |
SIM |
SIM |
Firefox |
NÃO |
SIM |
SIM |
Safari |
SIM |
NÃO |
NÃO |
Opera |
NÃO |
SIM |
SIM |
Utilizaremos como exemplo este vídeo que está no formato Quicktime (.mov). Para converter pode-se utilizar a ferramenta FFmpeg, disponível para Linux, Windows e MAC.
Convertendo para MP4
Para converter o vídeo para o formato MP4 podemos utilizar a linha de comando abaixo:
ffmpeg -i Rain_Fire.mov -c:v libx264 -pix_fmt yuv420p -profile:v baseline -preset slower -crf 23 -vf "scale=trunc(in_w/2)*2:trunc(in_h/2)*2" -movflags +faststart rain.mp4
Alguns parâmetros importantes do comando acima:
- -c:v libx264 – Importante pois o codec x264 é o mais suportado
- -pix_fmt yuv420p – Pixel format a utilizar
- -profile:v baseline – Profile de vídeo. O baseline é para que o vídeo seja facilmente entendido em dispositivos Android
- – preset slower – Determina a relação qualidade de vídeo/tempo de codificação. Pode ser diversos valores (faster, fast, medium, slow)
- -crf 23 – Nível de qualidade do encoding 0 é sem perdas e 51 a pior qualidade possível. O valor de 23 é o padrão e pode ser omitido.
- -vf “scale=trunc(in_w/2)*2:trunc(in_h/2)*2” – Por alguns motivos os vídeos MP4 encodados nesta forma devem ter os valores de largura e altura com números pares. Este parâmetro ajusta o tamanho do vídeo
- -movflags +faststart – Faz com que os cabeçalhos de informação do vídeo sejam colocados no começo do arquivo, desta forma o navegador não precisa baixar o vídeo todo para saber qual o seu tamanho e formato. Este parâmetro é essencial para que possa ser feito o streaming do vídeo.
Para mais informações ver a documentação do FFmpeg sobre codificação de vídeos com H.264.
Convertendo para WebM
Outra possibilidade de formato de vídeo é o WebM, um formato recentemente adquirido pelo Google que o tornou de uso público. Para converter o vídeo para este formato podemos utilizar o comando abaixo:
ffmpeg -i Rain_Fire.mov -c:v libvpx -c:a libvorbis -pix_fmt yuv420p -b:v 2M -crf 5 rain.webm
De forma semelhante os parâmetros da conversão:
- -c:v libvpx – o codec a utilizar para o video
- -c:a libvorbis – o codec a utilizar para o áudio
- -pix_fmt – formato de pixel
- -b:v 2M – bitrate desejado. É importante definir este parâmetro pois o padrão não gera videos de boa qualidade
- -crf 5 – Nível de qualidade do vídeo. Pode ser de 4 – 63. Quanto menor mais qualidade
Mais informações na documentação do FFmpeg.
Servindo o arquivo
Com os arquivos no formato correto podemos então criar nossa página para exibi-lo ao usuário. Vamos montar uma estrutura de diretórios da seguinte forma:

Estrutura de diretórios
No diretório public ficarão os arquivos visíveis pelo servidor web e no diretório resource os arquivos de vídeo que iremos servir. No arquivo index.php está o HTML para a página e o arquivo stream.php será o responsável por ler os arquivos de vídeo e enviá-los ao navegador do usuário.
Streaming
O streaming é uma forma de distribuição de dados aonde o conteúdo é fornecido ao usuário conforme necessário, sem a necessidade de que ele receba todo o conteúdo para visualiza-lo. Esta é a melhor forma de servir vídeos pois permite a rápida visualização pelo usuário (ele não tem de esperar o download do vídeo todo) e também diminui a banda de rede utilizada pelo servidor. Os navegadores utilizam o cabeçalho HTTP Range para indicar qual parte do vídeo eles desejam que seja enviada. Assim se o usuário clicar na barra de navegação do vídeo o navegador envia uma nova requisição para o período respectivo.
Tratando o cabeçalho Range
Com o arquivo no formato correto podemos criar um script PHP para fazer a leitura do arquivo e envia-lo para o usuário. Porém precisamos considerar o cabeçalho Range para que sejam enviados somente os bytes que o navegador solicitou. De acordo com a RFC2616 que especifica o protocolo HTTP versão 1.1 o conteúdo do campo tem um dos seguintes formatos:
- Os primeiros 500 bytes: bytes=0-499
- O segundo lote de 500 bytes: bytes=500-999
- Os últimos 500 bytes: bytes=-500
- Todos os bytes à partir do byte 9500: bytes=9500-
O valor do campo vem na variável $_SERVER[‘HTTP_RANGE’] então podemos criar o código que irá ler o arquivo utilizando as funções de sistema de arquivos do PHP e colocar no arquivo stream.php:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
|
//Determina o caminho do arquivo de acordo com a extensão if (!isset($_GET['ext']) || $_GET['ext'] == 'mp4') { $path = dirname(__FILE__) . '/../resource/rain.mp4'; } else if ($_GET['ext'] == 'webm') { $path = dirname(__FILE__) . '/../resource/rain.webm'; } else { header('HTTP/1.1 400 Bad Request'); return; } // Determina o mimetype do arquivo $finfo = new finfo(FILEINFO_MIME); $mime = $finfo->file($path); // Define o tipo de conteúdo da resposta header('Content-type: ' . $mime); // Tamanho do arquivo $size = filesize($path); //Verifica se foi passado o cabeçalho Range if (isset($_SERVER['HTTP_RANGE'])) { // Parse do valor do campo list($specifier, $value) = explode('=', $_SERVER['HTTP_RANGE']); //Tratamos apenas o especificador de range "bytes" if ($specifier != 'bytes') { header('HTTP/1.1 400 Bad Request'); return; } // Determina os bytes de início/fim list($from, $to) = explode('-', $value); if (!$to) { $to = $size - 1; } // Cabeçalho da resposta header('HTTP/1.1 206 Partial Content'); header('Accept-Ranges: bytes'); // Tamanho da resposta header('Content-Length: ' . ($to - $from)); // Bytes enviados na resposta header("Content-Range: bytes {$from}-{$to}/{$size}"); // Abre o arquivo no modo bináro $fp = fopen($path, 'rb'); $chunkSize = 8192; // Tamanho dos blocos de leitura // Avança até o primeiro byte solicitado fseek($fp, $from); // Manda os dados while(true){ // Verifica se já chegou ao byte final if(ftell($fp) >= $to){ break; } // Envia o conteúdo echo fread($fp, $chunkSize); // Flush do buffer ob_flush(); flush(); } } else { // Se não possui o cabeçalho Range, envia todo o arquivo header('Content-Length: ' . $size); // Lê o arquivo readfile($path); } |
É essencial tratar o cabeçalho Range quando estamos servindo arquivos que serão consumidos via streaming pois se não o usuário não conseguirá avançar/retroceder no vídeo e terá de esperar o download completo do vídeo antes de começar a assisti-lo, o que é muito pouco prático para vídeos grandes.
A página web
Vamos agora criar o arquivo index.php que será acessado pelo usuário. Ele terá apenas o HTML necessário para a tag <video>:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
<?php // Para quando for executado pelo php -S redirecionar os arquivos if (php_sapi_name() === 'cli-server' && is_file(__DIR__ . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))) { return false; } ?> <!DOCTYPE html> <html> <head> <title>HTML5 Video + PHP</title> </head> <body> <video id="video" controls preload="auto" width="640" height="360" poster="poster.png"> <source src="stream.php?ext=webm" type='video/webm' /> <source src="stream.php?ext=mp4" type='video/mp4' /> </video> </body> </html> |
Com o arquivo index.php criado podemos agora acessá-lo e ver o vídeo. Caso não queira utilizar um servidor web para testar pode-se utilizar o servidor web embutido do PHP executando:
php -S localhost:8000 index.php
E então acessar no navegador o endereço http://localhost:8000
A tag <video>
A tag <video> é utilizada para inserir o vídeo na página. Dentro dela pode haver qualquer número de elementos <source> cada um deles indicando um formato de vídeo diferente ou até mesmo um arquivo de áudio. Então pode-se, por exemplo, colocar um arquivo de vídeo e um de áudio e o navegador vai “juntar” os dois na reprodução. No nosso caso especificamos dois elementos source, um com o vídeo no formato MP4 e outro com o vídeo no formato WebM. Desta forma o navegador vai escolher o que for suportado. Os atributos da tag são:
- controls – Se devem ser exibidos os controles (avanço, retrocesso, volume, etc)
- preload – Se o vídeo deve ser carregado juntamente com a página. Pode ser auto para carregar junto com página, metadata para o navegador carregar apenas os dados básico ou none para não carregar junto com página. O Internet Explorer não respeita este parâmetro.
- width e height – Largura e altura, respectivamente
- poster – URL para uma imagem que será exibida no lugar do vídeo enquanto o mesmo não é carregado. Veja abaixo como gerar uma utilizando o FFmpeg.
- autoplay – se especificado este parâmetro o vídeo irá começar a tocar assim que possível
Além destes diversos outros parâmetros estão disponíveis que permitem controlar o comportamento do vídeo.
Criando o poster
Como mostrado acima é possível especificar uma imagem que irá aparecer no lugar do vídeo enquanto este não for carregado. Podemos gerar esta imagem com o FFmpeg utilizando o comando abaixo:
ffmpeg -i Rain_Fire.mov -r 1 -vframes 1 -ss 0:05 poster.png
O parâmetro mais importante acima é o -ss 0:05 que especifica de qual momento será criada a imagem
Utilizando o video.js
A página acima funciona em qualquer navegador moderno que suporte HTML5. Mas e os navegadores mais antigos como Internet Explorer 7 e 8 que ainda são bastante utilizados? Para estes casos podemos utilizar a biblioteca video.js que faz automaticamente o fallback para um player em Flash caso o navegador não tenha suporte a HTML5. A utilização é bastante simples e há diversos exemplos no site. Segue a versão do nosso index.php utilizando a biblioteca:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
<?php // Para quando for executado pelo php -S redirecionar os arquivos if (php_sapi_name() === 'cli-server' && is_file(__DIR__ . parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH))) { return false; } ?> <!DOCTYPE html> <html> <head> <title>HTML5 Video + PHP</title> <link href="//vjs.zencdn.net/4.6/video-js.css" rel="stylesheet"> <script src="//vjs.zencdn.net/4.6/video.js"></script> </head> <body> <video id="video" class="video-js vjs-default-skin" controls preload="auto" width="640" height="360" poster="poster.png"> <source src="stream.php?ext=webm" type='video/webm' /> <source src="stream.php?ext=mp4" type='video/mp4' /> <p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p> </video> </body> </html> |
Conclusão
A tag <video> do HTML5 permite adicionar facilmente vídeos à paginas web, porém é necessário tomar cuidado com os formatos de vídeos suportados e fazer as conversões necessárias. É essencial que os arquivos sejam servidos via streaming para diminuir o tráfego de rede e melhorar a experiência do usuário e para tanto é essencial que o cabeçalho Range seja tratado de forma adequada, algo que pode ser feito facilmente pelo PHP. O código aqui mostrado tem apenas as funções básicas e pode com certeza ser muito melhorado.
O código de exemplo está disponível no Github: https://github.com/weckx/wcx-video-stream