Reproduzindo Áudio via Browser – O Problema

Estou trabalhando em um cliente Web para controlar o meu robô Spykee em .NET. Este cliente é uma aplicação web baseada em HTML 5, multiplataforma (Windows/Linux) e focada nos browsers mais recentes (IE 9, Firefox 4, Chromium).

Antes de começar a trabalhar neste projeto eu estipulei vários requisitos sobre o funcionamento esperado deste cliente e até agora tenho conseguido manter todos, mesmo com as dificuldades do padrão HTML 5 que será tema de um outro post mais tarde. Mas estou começando a perceber que, pelo menos por enquanto, terei de abrir mão de alguns requisitos porque (PASMEM) o .NET Framework me deixou na mão. Isto porque uma das funcionalidades do meu robô é a possibilidade de envio e recebimento de áudio (VoIP) em tempo real, isto é, em streaming, o que eu descobri ser uma área bastante desleixada na tecnologia atual, conforme explicarei adiante.

Ainda pensando em como eu resolveria este problema de envio e reprodução de áudio, resolvi simplificar a minha vida e utilizar aquela velha técnica da engenharia de “dividir um problemão em probleminhas menores”, e foquei inicialmente na reprodução de áudio enviado do meu robô para o meu cliente Web. O que eu preciso nesse caso, tecnicamente falando, é enviar um buffer de áudio (array de bytes) em formato bruto (RAW) a 16KHz, 1 Canal, de 4000 em 4000 bytes, enviados a cada 0,125 segundos (4000 * 1 / 0,125 = 32000 bytes) para um dispositivo de áudio presente no computador cliente. Como o formato é bruto, não preciso de nenhum codec ou driver especial. Seria algo simples como, por exemplo, enviar esse buffer para o dispositivo /dev/audio* no Linux. É engraçado a esta altura imaginar o quanto isso deveria ser simples mas o quanto os engenheiros do Windows complicaram essa função no sistema operacional. Isso sem falar que eu precisaria reproduzir esse conteúdo via browser, o que adiciona mais um grau de complexidade no projeto.

Depois de muitas horas de pesquisa e diversos testes em diversas situações, creio que estas são TODAS as tecnologias atuais que te permitem reproduzir áudio no browser (não necessariamente são soluções multiplataforma):

 1 – A tag do HTML – Pela microsoft está “deprecated” pela utilização da tag <object>. Os outros browsers não interpretam corretamente a tag <object> (apesar de estar definido no padrão HTML). Alguns testes funcionaram bem, mas este método depende do player instalado na máquina e deixa a responsividade do browser muito ruim, tornando inviável utilizá-lo com outras funções (como andar com o robô). O IE não suporta a utilização de data URIs com este elemento e a única forma de fazê-lo funcionar é imprimir seguidamente tags via javascript no corpo da página. De vez em quando era possível ouvir algumas interrupções por conta do alto grau de atualização do conteúdo (1 vez a cada 0,125 segundos, ou 8 vezes por segundo). Utilização do processador muito alta por este método. 

2 – A tag <object> do HTML – Funcionamento parecido com a tag , só que não é suportado por outros browsers que não o IE. É possível especificar um player para reproduzir o áudio.

3 – A tag <bgsound> do IE – Funciona como as duas anteriores, só suporta arquivos WAVE e só é suportada pelo IE.

4 – Silverlight utilizando a classe System.Media.SoundPlayer – À primeira vista achei que todos os meus problemas estavam resolvidos. Uma classe que aceita um Stream com o conteúdo do áudio e possui um método Play(), 100% suportado pelo .NET Framework e, portanto, multiplataforma (Mono Moonlight)! Mas depois de montar toda a infra-estrutura do código notei que o som saía “picado”, isto é, era interrompido a cada 0,125 segundos. Depois de várias modificações na tentativa de melhoria da performance (inclusive instanciando mais de uma thread com mais de uma instância da classe SoundPlayer iniciando de forma intercalada), desisti. Pesquisei na internet e descobri que a terrível performance de execução dessa classe é algo conhecido e bastante dicutido nos fóruns de .NET, tornando essa solução que parecia tão promissora inviável.

5 – Silverlight utilizando o elemento MediaElement – Também parecia uma boa alternativa multiplataforma, mas só suporta reproduzir MP3 e WMA. Existe uma maneira de escrever um driver específico para fazê-lo tocar formato bruto RAW ou WAV (que são de fácil conversão entre um e outro), mas não sei qual é o tamanho da encrenca. Talvez vire um projeto maior do que o projeto de fato.

6 – Silverlight utilizando APIs do Windows – A partir deste ponto, a solução passa a deixar de ser multiplataforma. Parece que ninguém até hoje criou uma camada de abstração prestável para os dispositivos multimídia da máquina. No entanto, esta não é uma solução válida, pois o Silverlight é LIMITADÍSSIMO em suas classes .NET e, além disso, não consegue chamar APIs windows.

7 – WPF utilizando as APIs do Windows – Esta parece ser uma alternativa viável, mas ainda não fiz testes. Talvez o usuário tenha de instalar o WPF a partir de um pacote XBAP. As APIs do Windows são mais complicadas de mexer, mas a performance é a melhor possível. O cliente nativo do robô, feito em C++, importa as funções de WaveOut do arquivo winmm.dll, o que nos faz perceber que eles também utilizaram essas APIs para capturar/reproduzir o áudio.

8 – ActiveX utilizando as APIs do Windows – O velho ActiveX já me salvou algumas vezes, inclusive na comunicação com um scanner, e acho que me salvará mais essa. É necessário a instalação do ActiveX na máquina local, mas é o preço a se pagar por uma interatividade maior com o cliente. Não é multiplataforma e outras alternativas teriam de ser desenvolvidas para fazer funcionar no Linux.

Leave a comment