Vídeo online com Wowza e Amazon Elastic Transcoder

Publicados: 2022-03-11

O sucesso e a adoção de qualquer aplicativo da Web hoje dependem muito de seu desempenho, flexibilidade e facilidade de uso.

Especialmente no mundo de TDAH de hoje, os usuários perderão rapidamente a paciência com um aplicativo se o carregamento da página demorar muito. Para aplicativos da Web que precisam dar suporte ao processamento de vídeo – que é inerentemente intensivo em computação e E/S – esse desafio é particularmente agudo. Mesmo assim, os usuários estão se tornando cada vez mais exigentes, querendo que seus vídeos sejam de alta qualidade e carreguem rapidamente, mesmo rodando em um smartphone ou tablet.

Os usuários também estão perdendo a tolerância para aplicativos da Web que não funcionam em seu navegador ou dispositivo preferido ou que não suportam o formato de dados que precisam carregar ou exportar. A diversidade de formatos de vídeo que precisam ser suportados, portanto, também torna a incorporação de suporte de vídeo em um aplicativo da web especialmente desafiadora.

Esta postagem descreve como aproveitei efetivamente tecnologias de código aberto e serviços baseados em nuvem para incorporar recursos de vídeo em um aplicativo da Web baseado em PHP.

Processamento de vídeo online em PHP com Wowza e Amazon Elastic Transcoder

Caso de uso

Eu fazia parte de uma equipe que precisava desenvolver um site semelhante ao YouTube, onde usuários cadastrados pudessem fazer upload e compartilhar seus vídeos.

O sistema precisava permitir que usuários registrados enviassem seus vídeos em uma variedade de formatos suportados que seriam então convertidos para um formato comum (MP4). Também precisávamos gerar um conjunto de miniaturas e uma colagem de imagens a serem usadas no player de vídeo para mostrar os quadros em uma barra de progresso do vídeo.

As coisas ficaram ainda mais complicadas pelo fato de que os requisitos do cliente nos impediram de usar qualquer CDN disponível ou APIs de transcodificação, então precisávamos desenvolver nossa solução do zero.

Envio de vídeo

Como o processo de upload em si não precisava ser específico de vídeo (só precisávamos de um recurso de upload de arquivo fácil de usar), fazia sentido usar uma solução de código aberto existente em vez de implementar a nossa. Selecionamos jQuery-File-Upload, principalmente porque ele suportava dois recursos que eram essenciais em nosso caso; ou seja, uma barra de progresso de upload e uploads em partes.

O upload em pedaços nos permitiu permitir que um usuário carregasse um arquivo de vídeo de praticamente qualquer tamanho (especialmente importante para suportar arquivos de vídeo em resolução HD). Com essa abordagem, o arquivo é dividido em vários “pedaços” no front-end que invoca a ação de upload com cada trecho de dados (junto com os metadados de cada trecho, como número do trecho e tamanho total do arquivo). O arquivo de vídeo completo é então remontado no back-end. A propósito, incluir o número do bloco nos metadados provou ser particularmente importante, pois alguns navegadores (como o Mobile Safari) tendem a transmitir os blocos em ordem aleatória.

Processamento de vídeo on-line

O processamento de vídeo pode ser tão simples quanto capturar quadros como imagens estáticas ou pode envolver operações mais complexas, como aprimoramento de imagem, estabilização do fluxo de vídeo e assim por diante. No nosso caso, os únicos requisitos de processamento de vídeo eram (a) extrair codecs de vídeo e outros metadados importantes e (b) gerar um conjunto de miniaturas e uma colagem de imagens (a serem usadas no player de vídeo para mostrar os quadros em um vídeo em andamento Barra).

O FFmpeg – uma biblioteca de código aberto amplamente usada e distribuída gratuitamente – foi extremamente útil para atender a esses requisitos. O FFmpeg fornece uma solução completa e multiplataforma para gravação, conversão e streaming de arquivos de áudio e vídeo. Também pode ser usado para converter vídeos e fazer edições simples (por exemplo, aparar, recortar, adicionar uma marca d'água, etc.).

Para nossos propósitos, pudemos usar o FFmpeg para dividir o vídeo em dez seções e, em seguida, capturar uma miniatura para cada seção para fornecer a funcionalidade necessária.

Infelizmente, porém, não há ligações de linguagem PHP para a biblioteca FFmpeg. Como resultado, a única maneira de aproveitar o FFmpeg do PHP é invocar o binário da linha de comando usando comandos do sistema. Existem basicamente duas maneiras de usar o FFmpeg no PHP:

  • libav. Libav é um projeto de software livre, bifurcado do FFmpeg em 2011, que produz bibliotecas e programas para manipulação de dados multimídia. No Ubuntu, por exemplo, isso pode ser instalado com o comando sudo apt-get install libav-tools . Os comandos libav são compatíveis com FFmpeg e avconv. O PHP precisa ter acesso de linha de comando ao ffmpeg/avconv para usar isso programaticamente.
  • PHP-FFMpeg. PHP-FFMpeg é um driver PHP orientado a objetos para o binário FFMpeg. Ele pode ser acessado simplesmente executando composer update "php-ffmpeg/php-ffmpeg" .

Usamos PHP-FFMpeg, pois fornece acesso fácil à funcionalidade FFmpeg que nos interessa. Por exemplo, a classe FFProbe deste pacote permite que você receba informações sobre codecs ou o comprimento de um arquivo de vídeo específico da seguinte forma:

 $ffprobe = FFMpeg\FFProbe::create(); $ffprobe ->format('/path/to/video/mp4') // extracts file informations ->get('duration');

O FFmpeg também facilita salvar qualquer quadro de vídeo:

 $ffmpeg = FFMpeg\FFMpeg::create(); $video = $ffmpeg->open('video.mpg'); $video ->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(10)) ->save('frame.jpg');

Um código de amostra mais detalhado está disponível aqui.

Uma nota de cautela: devido a algumas leis de patentes, nem todos os codecs podem ser processados ​​pelo FFmpeg e alguns formatos não são devidamente (ou totalmente) suportados. Lembro-me de lutar alguns anos atrás, por exemplo, com o formato .3gp , quando o suporte a telefones comuns era obrigatório.

Fila

Depois de obter os codecs de um vídeo e outros metadados, enviamos o vídeo para uma fila de conversão FIFO (first in first out). A fila foi implementada usando um script cron simples que seleciona um determinado número de vídeos não processados ​​cada vez que é executado e os passa para um utilitário de conversão (exemplo de código-fonte disponível aqui).

O utilitário de conversão invoca o FFMpeg para realizar a conversão e marca cada vídeo como tendo sido processado.

Também desenvolvemos um mecanismo simples de estimativa de tempo de espera, que calcula o tempo médio para converter 1 minuto de vídeo. Usando essa média, podemos calcular e exibir para o usuário o tempo de processamento restante estimado após a conclusão do upload de um vídeo, com base em quantos minutos de vídeo ainda precisam ser processados.

Conversão de formato de vídeo

Certos formatos universalmente reconhecidos (como JPEG e GIF) surgiram para imagens estáticas que são essencialmente suportados por todos os dispositivos e softwares de processamento de imagens. Embora alguns formatos de vídeo sejam mais comuns do que outros, nenhum formato com suporte universal surgiu ainda para vídeos.

No nosso caso, além de precisar converter de uma variedade de formatos para um único formato comum (MPEG-4), precisávamos que os vídeos convertidos fossem otimizados para streaming em dispositivos móveis.

Para conversão de formato de vídeo (pelo menos para nossas necessidades de curto prazo), usar o Amazon Elastic Transcoder baseado em nuvem foi a melhor opção disponível. Além de sua facilidade de uso geral, o serviço de transcodificador cuida da otimização e de todas as configurações de codificação. Felizmente, um AWS SDK for PHP está disponível, o que simplifica a chamada do serviço de nosso código PHP.

Observação: usar um serviço baseado em nuvem como o Amazon Elastic Transcoder é ótimo se você quiser começar a trabalhar rapidamente. No entanto, lembre-se de que essa opção pode se tornar cara para o seu cliente, especialmente se o modelo de negócios dele exigir o uso extensivo de vídeos grandes. Outro fator a ser considerado é que você não deve necessariamente presumir que os vídeos ou o modelo de negócios de seu cliente serão compatíveis com os Termos de Serviço.

A Amazon usa seus elementos básicos de armazenamento e computação, S3 (Simple Storage Service) e EC2 (Elastic Compute Cloud) – combinados com Auto Scaling e SNS (Simple Notification Service) – para fornecer a capacidade de aumentar e diminuir praticamente instantaneamente.

A instalação do aws-sdk é simples, pois a Amazon mantém uma versão do pacote instalável pelo Composer. Basta adicionar ”aws/aws-sdk-php": "2.*" ao seu arquivo composer.json e fazer uma composer update .

Obviamente, acessar o Amazon Elastic Transcoder requer uma conta da Amazon, portanto, você também precisará configurá-la se você (ou seu cliente) ainda não tiver uma.

Nosso uso do serviço Amazon Elastic Transcoder envolveu primeiro o upload de arquivos de vídeo para um bucket apropriado no S3. Em seguida, tornamos o trabalho do transcodificador responsável por decodificar e gerar uma miniatura que, ao ser concluída, envia uma solicitação HTTP para o endereço especificado. Isso requer alguma configuração no painel da AWS, mas é bastante simples e a Amazon fornece uma boa documentação sobre como fazer isso.

Sinta-se à vontade para usar nosso pacote de transcodificador, que ajuda a simplificar a integração do Symfony 2. Ele inclui uma descrição de uso e oferece um controlador para implementação rápida de um serviço de aviso enviado pela Amazon para coletar informações sobre o vídeo processado. Um exemplo de uso está disponível aqui.

Além disso, um controlador de exemplo que lida com notificações da Amazon está disponível aqui, que também implementa a confirmação de um endereço de assinatura. O serviço publicará primeiro o URL a ser visitado para confirmar que este é um receptor de notificação válido. Tudo o que é realmente necessário é marcar o vídeo como processado. A partir daí, podemos usar o vídeo transcodificado que fica armazenado na nuvem.

Transmissão

O streaming de vídeo é um recurso que exige alto desempenho: as expectativas do usuário para streaming ininterrupto são altas e a tolerância à latência é extremamente baixa. Esse desafio geralmente é exacerbado pela necessidade de transmitir vídeo para vários clientes simultaneamente em tempo real.

No nosso caso, precisávamos dar suporte para que cada usuário pudesse fazer seu próprio canal de vídeo e começar a transmitir. Nossa solução consistia em três componentes:

  • Painel. Aplicativo que serve como painel do streamer, oferecendo a possibilidade de veicular vídeo.
  • Visualizador. Cliente de vídeo que consome e exibe um fluxo de vídeo.
  • Motor de streaming. Serviço de streaming de vídeo baseado em nuvem.

Além do fato de que a tecnologia Video on Demand (VOD) ainda está evoluindo, outro problema que enfrentamos foi que o acesso à câmera não era bem suportado e oferecia apenas uma conexão P2P. Além disso, nosso objetivo era fornecer transmissão online para vários usuários simultâneos. Além disso, o suporte para a API getUserMedia/Stream (anteriormente concebida como o elemento <device> ) ainda não é consistente em navegadores modernos. Com base nesses fatores, decidi usar a tecnologia Flash, pois era realmente a única opção razoável. Ambos os aplicativos (Dashboard e Viewer) foram, portanto, implementados usando Flex e ActionScript .

Para o mecanismo de streaming, usamos o Wowza . Embora existam outras soluções não comerciais (como o Red5, que é comercializado essencialmente como um substituto do Wowza), no nosso caso o suporte comercial ao produto foi um fator importante. Além disso, pelo menos na época em que estávamos construindo o sistema, o Wowza oferecia uma documentação melhor, o que era uma vantagem adicional. (Observe que você pode obter uma versão de teste do Wowza gratuitamente por 30 dias e também existe a versão de teste do desenvolvedor que você pode usar por até 180 dias. Mas existem algumas limitações; o streaming só pode funcionar para dois clientes e há um limite no número máximo de conexões.)

Motor de streaming Wowza

Usamos o aplicativo LiveStream fornecido com o Wowza. Para configurá-lo, deixe applications/app_name vazio e em conf/app_name copie o arquivo Application.xml do catálogo conf . Edite o arquivo para configurar a seção <Streams> da seguinte forma:

 <Streams> <StreamType>live</StreamType> <StorageDir>${com.wowza.wms.context.VHostConfigHome}/content</StorageDir> <KeyDir>${com.wowza.wms.context.VHostConfigHome}/keys</KeyDir> <LiveStreamPacketizers></LiveStreamPacketizers> <Properties></Properties> </Streams>

O parâmetro chave é <StreamType>live</StreamType> que define que este será um fluxo de um feed de vídeo ao vivo (por exemplo, uma câmera). Observe que após editar e salvar este arquivo, você precisará reiniciar o Wowza.

Aplicativos Flash (Flex/ActionScript)

O Flash fornece um sistema totalmente integrado para conectar uma câmera e um microfone a um servidor de streaming Wowza. Isso é particularmente útil se sua experiência com o ActionScript for limitada.

Toda a aplicação é essencialmente baseada na interação entre os seguintes objetos:

  • NetConnection. A classe NetConnection cria uma conexão bidirecional entre um cliente e um servidor. O cliente pode ser um aplicativo Flash Player ou AIR. O servidor pode ser um servidor web, Flash Media Server, um servidor de aplicativos executando o Flash Remoting ou o serviço Adobe Stratus.
  • Câmera. A classe Camera é usada para capturar vídeo do sistema cliente ou da câmera do dispositivo.
  • Microfone. A classe Microphone é usada para monitorar ou capturar áudio de um microfone.
  • NetStream. A classe NetStream abre um canal de streaming unidirecional em um NetConnection.

Primeiro, nos conectamos ao servidor de streaming Wowza usando a instância NetConnection e, em seguida, anexamos o ouvinte de eventos que escutará as alterações no status da conexão de rede:

 nc = new NetConnection(); nc.connect(serverAddress:string); nc.addEventListener( NetStatusEvent.NET_STATUS, // event type eNetStatus, // listener function false, // use capture? 0, // priority true // use weak reference? );

Aqui está um exemplo minimalista de um ouvinte de eventos que conecta a câmera e o microfone ao servidor de streaming:

 private function eNetStatus(e:NetStatusEvent):void { switch (e.info.code) { case "NetConnection.Connect.Success": camera = Camera.getCamera(); mic = Microphone.getMicrophone(); ns = new NetStream(nc); ns.publish(streamName, "live"); ns.attachCamera(camera); ns.attachAudio(mic); break; case "NetConnection.Connect.Closed": // debug trace... display user message break; }

O código do cliente é muito semelhante, exceto que apenas exibimos a entrada de vídeo no lado do usuário. Isso é feito conectando o stream ao objeto Video , conforme mostrado neste exemplo simples:

 if(event.info.code == "NetConnection.Connect.Success") { ns = new NetStream(nc); ns.client = nsClient; ns.addEventListener(NetStatusEvent.NET_STATUS, nsClient.onNetStatus); ns.play(streamName); video = new Video(); addChild(video); // this will display video video.attachNetStream(ns); // connect NetStream to video }

Embrulhar

Espera-se que a transmissão ao vivo e o vídeo desempenhem um papel cada vez mais significativo em aplicativos móveis e da web. Portanto, é importante que os desenvolvedores da Web se familiarizem com a transcodificação, processamento e streaming de vídeo. Inúmeras ferramentas, bibliotecas e serviços existem hoje para incorporar esses recursos em aplicativos da web. Este artigo mostra como aproveitamos e integramos várias dessas tecnologias para criar com sucesso um site básico semelhante ao YouTube com relativa facilidade.