Gstreamer + Python = tu propio reproductor multimedia en minutos

— [ edit | raw ] migrated from node/349
Es muy sencillo construirse un reproductor/codificador/decodificador multimedia usando Gstreamer y Python. Aquí, haremos un reproductor de Ogg Vorbis básico.

Ingredientes

  • python-gst0.10

La instalación, como ya sabrás, es muy compleja y tediosa, pero no hay mas remedio que tragar :-P :

 # apt-get install python-gst0.10

Introducción

Antes de empezar a escribir código, unas breves líneas de como rula Gstreamer. Gstreamer es básicamente un framework para la manipulación multimedia. Con él se puede hacer casi de todo, desde un simple reproductor de Ogg Vorbis (como aqui veremos) hasta complejas aplicaciones distribuidas de streaming multimedia. Todo depende de los modulos y plugins de que dispongamos.

Gstreamer funciona como un flujo de datos dentro de una tubería. Se selecciona una fuente de datos, un camino para estos (en el que se pueden modificar o no) y un destino. Podemos manipular los eventos que se produzcan en la tubería, controlar su estado e incluso inyectar más datos a nuestro antojo. Interiormente, en la tubería, los datos también pueden seguir diferentes caminos. Por ejemplo, el vídeo puede ir por un lado, mientras que el audio procesarse por otro.

En Gstreamer, todas las partes que usamos para construir la tubería (la fuente, el destino, los codificadores, etc.) se llaman “Elementos”, que tienen ‘enchufes’ (Pads). Entonces, cuando creamos la tubería conectamos unos enchufes con otros, para que el flujo siga su curso. Y estos Pads, tienen establecido un sentido para el flujo de datos: de salida (llamados Source) y de entrada (llamados Sink). Cada elemento de Gstreamer puede tener uno o varios pads, o ninguno (e incluso, es muy común que los pads se creen dinámicamente, segun sean necesarios).

Por ejemplo, podemos crear una tubería con una fuente que lea de un fichero de audio en Ogg Vorbis, más un demultiplexor para el envoltorio del Vorbis (es decir, un elemento que extraiga del Ogg los datos
codificados con Vorbis), más un decodificador de Vorbis, por supuesto. Además necesitamos un conversor de audio y por último, un destino, que podría ser por ejemplo ALSA. Simplemente con esto, nos construimos un reproductor de Ogg/Vorbis, que es lo que vamos a hacer.

Manos a la obra

Pues bien, como todo lo vamos a hacer en Python, lo primero que necesitamos es importar los modulos que vamos a usar. Es decir:

  • gst: evidentemente, y además en su version 0.10, con lo que también importaremos pygst para cargarlo.
  • sys: es muy común.
  • gobject: porque lo usa Gstreamer.

Con lo que tendríamos:

import pygst
pygst.require("0.10")
import sys, gst, gobject
gobject.threads_init()

Bien, ahora creamos una función que haga las veces de main, y comprobamos que tenemos los argumentos suficientes:

def main(args):

    if len(args) != 2:
        print "Uso: " + args[0] + " <archivo Ogg>"
        return -1

Hasta ahí nada nuevo. Ahora tenemos que crear la tubería. Para esto hay diversas maneras. Podemos crear cada elemento por separado, enlazar los Pads correspondientes y después meterlos todos en un contenedor. De esta forma, tenemos un mejor control sobre lo que hacemos, pero existe una herramienta con la que es mucho más sencillo todo. Se llama ‘parse_launch’. Acepta una cadena con la estructura final de nuestra tubería, y nos devuelve la tubería hecha. Así, para nuestra tubería, la cadena que tendríamos que formar es:

    pipestr = "filesrc location= %s ! oggdemux ! vorbisdec ! audioconvert ! alsasink" % args[1]

Esta cadena contiene cada elemento (con el nombre por el cual está definido) más algun atributo de ese elemento, separados con el símbolo de admiración (!). ‘filesrc’ es el elemento que se encarga de leer de un fichero del disco, y con la propiedad ‘location’ establecemos la ruta al fichero que se tiene que leer; ‘oggdemux’ es el demultiplexor de Ogg, etc.

Pues si le pasamos esta cadena a parse_launch, intentará crearnos la tubería correspondiente, y además completamente funcional.

    try:
        pipeline = gst.parse_launch(pipestr)
    except gobject.GError, e:
        print "No es posible crear la tuberia, " + str(e)
        return -1

Ahora sería útil añadir un pequeño manipulador de eventos, ya que si se produce algún mensaje dentro de la tubería, debemos gestionarlo nosotros. En esta ocasión, solo haremos caso de dos mensajes: EOF, que
nos indica que debemos terminar (End Of Stream, o fin de flujo) y ERROR, que también nos obliga a terminar, pero advirtiendo de que se produjo un error interno. El resto lo ignoramos. Para ello crearemos una mini función interna que lo gestione todo.

    def eventos(bus, msg):
        t = msg.type
        if t == gst.MESSAGE_EOS:
            loop.quit()

        elif t == gst.MESSAGE_ERROR:
            e, d = msg.parse_error()
            print "ERROR:",e
            loop.quit()

        return True

Y la añadimos al bus de comunicaciones que tiene la tubería.

    pipeline.get_bus().add_watch(eventos)

Ya tenemos la tubería completamente creada. Ahora sólo nos queda establecer el estado de la misma. El estado de la tubería especifica que es lo que pasa con el flujo dentro de ella. Disponemos básicamente de NULL, READY, PLAYING y PAUSED. El estado inicial de la tubería es NULL, y si queremos que empieze a funcionar, tendremos que cambiarlo
a PLAYING. Una vez ahí, el flujo de datos puede fluir por todos los elementos.

    pipeline.set_state(gst.STATE_PLAYING)

¿Qué pasa? ¿No debería sonar ya? Hombre, pues no exactamente. Todavía nos queda un pequeño detalle. Como antes dije, Gstreamer usa Gobject, y por tanto, todos sus elementos bailan al son de Gboject. Por ello, es necesario crear un MainLoop y ponerlo en marcha. También sería bueno poder salir de una manera adecuada cuando se pulsa Ctrl+C.

    loop = gobject.MainLoop()
    try:
        print "Reproduciendo..."
        loop.run()

    except KeyboardInterrupt: # Por si se pulsa Ctrl+C
        pass

    print "Parando... \n¡Adios!"

Como nota, he de decir que la función ‘run’ de gobject es un bucle de eventos, es decir, el programa se quedará en ese punto hasta que ‘run’ retorne. En nuestro caso, eso sucede cuando se pulsa Ctrl+C o cuando se produce un evento EOS o ERROR (ya que entonces se llama a ‘quit’ de Gobject).

Ya esta sonando todo… bien. Ahora solo nos queda decidir que hacer cuando termine el flujo o se pulse Ctrl+C. Lo lógico es poner la tubería en estado NULL y salir. Eso haremos.

    pipeline.set_state(gst.STATE_NULL)
    return 0

Bien, pues ya tenemos nuestra función principal. Yo añado un par de lineas para que se llame a esa función principal cuando el script se ejecuta como tal, y no cuando se importa como módulo, pero esto es a gusto del consumidor ;-)

if __name__ == "__main__":
    sys.exit(main(sys.argv))

¡Y ya está! Así de sencillo ;)

Comentarios

Como ves, es muy fácil. Casi todo el codigo necesario es para manipular eventos o parte del programa principal. La esencia de todo radica en la cadena de texto que se pasa a ‘parse_launch’. Si cambiamos los elementos, creamos tuberías diferentes. Por ejemplo, en vez de dirigir la salida a ALSA, la puedes dirigir a un fichero (con filesink). Esto lo dejo para tu imaginación ;-p . Gstreamer es muy potente, así que con poca cosa se pueden hacer cosas muy interesantes… ¡que te diviertas!

Todo junto

Para que no tengas que copiar y pegar muchas veces, aquí dejo todo el codigo del programa completo, todo juntito, ¡testeado, funcionando y todo!

import pygst
pygst.require("0.10")
import sys, gst, gobject
gobject.threads_init()

def main(args):

    if len(args) != 2:
        print "Uso:", args[0], " <archivo Ogg>"
        return -1

    pipestr = "filesrc location= %s  ! oggdemux ! vorbisdec ! audioconvert ! alsasink" % args[1]

    try:
        pipeline = gst.parse_launch(pipestr)
    except gobject.GError, e:
        print "No es posible crear la tubería,", str(e)
        return -1

    def eventos(bus, msg):
        t = msg.type
        if t == gst.MESSAGE_EOS:
            loop.quit()

        elif t == gst.MESSAGE_ERROR:
            e, d = msg.parse_error()
            print "ERROR:", e
            loop.quit()

        return True

    pipeline.get_bus().add_watch(eventos)

    pipeline.set_state(gst.STATE_PLAYING)

    loop = gobject.MainLoop()
    try:
        print "Reproduciendo..."
        loop.run()
    except KeyboardInterrupt: # Por si se pulsa Ctrl+C
         pass

    print "Parando... \n¡Adios!"

    pipeline.set_state(gst.STATE_NULL)
    return 0

if __name__ == "__main__":
    sys.exit(main(sys.argv))

Referencias

Más:

Introducción al Proceso Software Personal (Humphrey)

— [ edit | raw ] migrated from node/347
Desde hace bastante tiempo estoy tratando de iniciarme en el Proceso de Software Personal. Estuve leyendo un libro bastante pesadete sobre este tema y, aunque me motivó bastante, no conseguí llevarlo a cabo. Parece que he encontrado un nuevo libro que me ha facilitado el paso. Trataré de escribir aquí mis conclusiones y facilidades para seguirlo.

Iptables en FC5

— [ edit | raw ] migrated from node/343
En un servidor Fedora Core 5, que hace de Proxy, quiero darle tambien el servicio de Web, pero, me encontre con el inconveniente que tengo una regla que direcciono todo el port 80 al 8080, por lo tanto la web local del proxy no funcionaba hasta que en la misma regla incluí un -d ! proxy y todo de maravillas.

Usando gphoto2 desde consola

— [ edit | raw ] migrated from node/341

Esta receta recoge algunas features interesantes del frontend de consola de gphoto2

Introducción

gphoto2 es una librería que soporta un montón de modelos de cámaras fotográficas digitales. Además dispone de un pequeño frontend que permite usar la mayor parte de sus posibilidades desde consola, algo que puede resultar muy útil para automatizar tareas.

Ingredientes

  • gphoto2
  • una cámara digital soportada por gphoto2

Copiar todas las fotos con su fecha y hora

Situados en un directorio creado al efecto simplemente ejecuta:

 $ gphoto2 -P ==--filename "%Y-%m-%d_%H:%M_%n.%C"==

Y gphoto2 descargará y renombrará todas las fotos y vídeos que tengas en la cámara, sin borrarlos de ésta. Dependiendo del modelo de tu cámara quizá tengas que indicar algún parámetro adicional como el puerto o similar; mira la documentación.

Si no te gusta tener tooodas las fotos en el mismo directorio, puedes utilizar este pequeño script Python para clasificar los ficheros en directorios por fecha.

Echa un ojo a la receta de Sacando jugo a EXIF, en consola que tiene algunos trucos útiles para clasificar y organizar fotos.

Referencias

Compartir ficheros con Samba/CIFS

— [ edit | raw ] migrated from node/338

Esta receta explica cómo utilizar SAMBA para servir ficheros y cómo acceder a ellos desde otro ordenador. Supondremos que en ambos equipos tienes Debian o sucedáneos.

La UCLM llega a UBUNTU.

— [ edit | raw ] migrated from node/337
Cuando he abierto mi aptitude en UBUNTU para actualizarme y he visto los paquetes nuevos que han metido en EDGY me he llevado una grata sorpresa al descubrir que el maintainer tenía una dirección @uclm.es.

El formato de Word es bueno (pero no para ti)

— [ edit | raw ] migrated from node/335
Muy buenas... hartos ya como estamos de la guerra abierta entre formatos ofimáticos, en los que el ejército enemigo son los propios usuarios y su dejadez, voy a escribir este documentillo para demostrar de una vez por todas porqué el mejor formato ofimático de todos es el de Microsoft Word.

Afina tus discos con hdparm

— [ edit | raw ] migrated from node/334

hdparm es una pequeña herramienta que sirve para manipular la configuración de las unidades de disco. El objetivo es optimizar el tiempo de acceso o la velocidad de transferencia.