Criando Um Player de Áudio com GStreamer
From CInLUG
Conteúdo |
Introdução
Neste artigo será criada e analisada uma pequena aplicação com interface gráfica para tocar arquivos de áudio em diversos formatos. O presente artigo é uma continuação de Apresentando o GStreamer, e caso você estranhe algum termo provavelmente ele foi explicado lá. Então se não leu ou não conhece a tecnologia do GStreamer, corra lá.
Player de Linha de Comando
O programa a seguir toca a instrutiva música Linha de Comando. Não há interface gráfica e ao terminar a música você precisa pressionar Ctrl+C para sair, além disso não toca nada além de mp3. Mas não vamos nos aperrear muito com isso, faremos uma aplicação melhorzinha mais adiante, esta aqui é só pra esquentar.
O pipeline que vamos montar neste programa python, como diagrama tem essa cara:
#!/usr/bin/python import pygst pygst.require('0.10') import gst import gobject paipe = gst.Pipeline('paipelaine') source = gst.element_factory_make('filesrc', 'source') source.set_property('location', './linhadecomando.mp3') decoder = gst.element_factory_make('mad', 'decoder') convert = gst.element_factory_make('audioconvert', 'convert') resample = gst.element_factory_make('audioresample', 'resample') sink = gst.element_factory_make('alsasink', 'sink') paipe.add(source) paipe.add(decoder) paipe.add(convert) paipe.add(resample) paipe.add(sink) gst.element_link_many(source, decoder, convert, resample, sink) paipe.set_state(gst.STATE_PLAYING) loop = gobject.MainLoop() loop.run()
Para testar, baixe o mp3 indicado ou mude o código para apontar para um outro arquivo em seu computador, e execute:
$ ./playa.py
Quando a música parar de tocar ou se você não quiser mais, basta pressionar Ctrl+C.
O código anterior equivale à linha de comando abaixo, usando o gst-launch.
$ gst-launch-0.10 filesrc location="./linhadecomando.mp3" ! mad ! audioconvert ! audioresample ! alsasink
Você deve ter percebido como é mais simples usar o gst-launch, em vez de se dar ao trabalho de escrever código em python. Mas é para fins didáticos! Serviu para dar uma visão de como montar um pipeline com alguns elementos, ligá-los e botar a mídia pra fluir. Vamos ver o código mais de perto (mas não exatamente na mesma ordem do código anterior).
Imports
import pygst
pygst.require('0.10')
import gst
import gobject
As três primeiras linhas importam o módulo GStreamer (gst) e exigem que seja a versão 0.10 (já disse antes, e repito, 0.8 é tosca... bem, foi uma fase). A última importa o módulo gobject, que contém a classe GObject, que serve de base para GTK+, mas pode ser usada independente de interface gráfica, como fizemos neste exemplo. As classes principais do módulo gst herdam de GObject, mas nosso interesse aqui é no MainLoop.
Fábrica de Elementos & Propriedades
source = gst.element_factory_make('filesrc', 'source')
source.set_property('location', './linhadecomando.mp3')
Na primeira linha usamos o método da "fábrica de elementos", com o primeiro parâmetro, "filesrc" indicando o tipo de elemento que desejamos criar, e o segundo é apenas um nome que escolhemos para o tal elemento. Usando a fábrica criamos todos os elementos do nosso pipeline. A segunda linha define o valor da propriedade "location" do elemento para "./linhadecomando.mp3". Lembre-se que o valor default de um elemento é gst.STATE_NULL, ou seja, o arquivo não foi sequer aberto.
Ligando Elementos
gst.element_link_many(source, decoder, convert, resample, sink)
Na linha acima ligamos os elementos criados pela fábrica na ordem em que aparecem nos parâmetros do método: source suja saída alimenta a entrada de decoder, que por sua vez alimenta convert e assim por diante. O método element_link_many coloca as "setinhas" do diagrama do início desta seção.
Pipeline & Estados
paipe = gst.Pipeline('MeuPipeline')
...
paipe.add(source)
...
paipe.set_state(gst.STATE_PLAYING)
Primeiro criamos o pipeline, pois é necessário que uma aplicação tenha pelo menos um pipeline englobando todos os outros elementos. Depois adicionamos os vários elementos ao pipeline com o método add. Definindo o estado do pipeline como gst.STATE_PLAYING, colocamos em movimento o fluxo de mídia por seus elementos, pois estes recebem automaticamente o estado do elemento que os contém (segundo a hierarquia de classes, o pipeline é um elemento). Outros estados possíveis são:
- gst.STATE_NULL: estado padrão, se trazido para este estado, todos os recursos mantidos pelo elemento serão desalocados.
- gst.STATE_READY: buffers são alocados e arquivos abertos, mas o fluxo está na espera.
- gst.STATE_PAUSED: o fluxo está aberto, mas seu "movimento" está congelado.
- gst.STATE_PLAYING: igual a Paused, mas os dados estão fluindo.
MainLoop
loop = gobject.MainLoop() loop.run()
Estas linhas criam um MainLoop e o coloram para rodar até que algo o interrompa, nesse caso o pressionamento de Ctrl+C. Poderíamos ter feito a aplicação de forma a receber um sinal do pipeline indicando o término da música, e vamos fazer isso! Na próxima aplicação...
PyPlaya
Agora vamos criar uma aplicação simples, porém mais funcional que a anterior e uma base para fazer um player "de verdade". Abaixo está um screenshot & explicação da interface do bicho.
O nome, muito original, da aplicação é PyPlaya, e a interface se chama FooPlaya. Essa distinção se deve a uma idéia minha e de Lauro de usarmos a mesma GUI para criar players em várias linguagens que tenham bindings pro GStreamer. Por enquanto temos esta e o RPlaya, em Ruby. Os códigos foram gentilmente hospedados no Google Hosting.
Mas antes de irmos pro código vamos falar da Bus e do Playbin.
Bus
Uma Bus é uma via de mensagens do pipeline (que executa numa thread própria) para o mundo exterior (a thread da sua aplicação). No seu código, você deve avisar à Bus as mensagens das quais quer ser informado e fornece como parâmetro um método callback de sua aplicação, que será chamado pela Bus quando uma mensagem precisar ser entregue. Observe que o evento que dispara a chamada e o callback estão em threads separadas, de forma que a comunicação Bus-Aplicação é assíncrona.
Sua aplicação pode chamar set_state para mudar o estado do pipeline para PLAYING ou PAUSED, e este método deve retornar o status de sucesso ou falha da mudança. O que caracteriza uma comunicação síncrona. Embora seja possível ignorar o valor de retorno e esperar a mensagem de mudança de estado adequada surgir na Bus.
Mensagens
As mensagens, representadas pela classe gst.Message, podem ser de vários tipos, mas para nossa aplicação usaremos apenas:
- gst.MESSAGE_ERROR: aquelas mensagens de erro que sempre acontecem quando não esperamos. ;)
- gst.MESSAGE_EOS: End-of-stream (EOS) acontece quando o fluxo que está passando pelo pipeline chega ao fim.
- gst.MESSAGE_TAG: normalmente acontece quando o fluxo é aberto e são detectadas tags de meta-informações sobre o fluxo, como autor ou título de uma música.
Playbin
O Playbin é um tipo especial de pipeline capaz de identificar o tipo de mídia do fluxo e alocar automaticamente os elementos necessário para tocá-la. Parece fácil demais e realmente é, além de ser tudo que é necessário para o uso que muitas aplicações fazem de multimídia. Contudo o esquema de montar um pipeline com diversos elementos dá um maior controle sobre componentes individuais do pipeline, que pode ser necessário por aplicações mais sofisticadas.
Conclusão
...
O site oficial é uma das melhores fontes de informação sobre o GStreamer, destaco os documentos:
- GStreamer Application Development Manual
- http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/index.html
- GStreamer Plugin Writer's Guide
- http://gstreamer.freedesktop.org/data/doc/gstreamer/head/pwg/html/
No próximo artigo, programação com Python e GStreamer.
| Autor: Marcelo Lira |






