Página principal

Criando Um Player de Áudio com GStreamer

From CInLUG

Imagem:Helmet.png
Este artigo é somente um esboço.
Mas estamos trabalhando duro!
"Release early, release often."

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:

  1. #!/usr/bin/python
  2.  
  3. import pygst
  4. pygst.require('0.10')
  5. import gst
  6. import gobject
  7.  
  8. paipe = gst.Pipeline('paipelaine')
  9.  
  10. source = gst.element_factory_make('filesrc', 'source')
  11. source.set_property('location', './linhadecomando.mp3')
  12.  
  13. decoder = gst.element_factory_make('mad', 'decoder')
  14. convert = gst.element_factory_make('audioconvert', 'convert')
  15. resample = gst.element_factory_make('audioresample', 'resample')
  16. sink = gst.element_factory_make('alsasink', 'sink')
  17.  
  18. paipe.add(source)
  19. paipe.add(decoder)
  20. paipe.add(convert)
  21. paipe.add(resample)
  22. paipe.add(sink)
  23.  
  24. gst.element_link_many(source, decoder, convert, resample, sink)
  25.  
  26. paipe.set_state(gst.STATE_PLAYING)
  27. loop = gobject.MainLoop()
  28. loop.run()
  29.  

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

Herança das classes mais relevantes para para este artigo. Veja herança completa em http://pygstdocs.berlios.de/class-hierarchy.html
Herança das classes mais relevantes para para este artigo. Veja herança completa em http://pygstdocs.berlios.de/class-hierarchy.html
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

Imagem:Cc-small.png : Imagem:Cc-by.png Imagem:Cc-sa.png
Atribuição-Compatilhamento pela mesma licença 2.5

Ferramentas pessoais
Vistas