lunes, 18 de enero de 2016

Usando appsrc de Gstreamer-1.0 en python


Hola amigos, pues hace un par de días atrás estuve haciendo un proyecto donde necesitaba usar appsrc de gstreamer-1.0 en python y por más que busqué algún ejemplo para aprender como usarlo pues no lo encontré, así es que aquí lo publico por si alguien más lo necesita. Sin más que decir, pues aquí les escribo un pequeño ejemplo.

Les decribo el programa que está publicado. Como fuente vamos a utilizar un archivo wav, ya que su contenido es lo más parecido a un tipo raw, las características de archivo son 16LE, 2ch, 44.1 kHz. Lo que obtengamos de este archivo nos va a servir para alimentar nuetro pipeline de gstreamer. Después vamos ha convertir este archivo de 2 canales a 1 solo canal para posteriormente hacer un downsample y al final del proceso quede en un formato de 16LE, 1ch, 16 kHz y lo escucharemos por la salida de audio de nuestro PC.

Este es el archivo inicial:

$ soxi ~/Music/Toninho\ Horta\ _\ Jack\ Lee/Track\ 9.wav

Input File : '~/Music/Toninho Horta _ Jack Lee/Track 9.wav' 
Channels : 2 
Sample Rate : 44100 Precision : 16-bit
Duration : 00:05:00.84 = 13267044
samples = 22563 CDDA sectors
File Size : 53.1M
Bit Rate : 1.41M
Sample Encoding: 16-bit Signed Integer PCM

Este es el archivo python que vamos a usar:

# -*- coding: utf-8 -*-
import Queue
import wave
import time
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst, GstApp

Gst.init(None)

class appSrcEjemplo2():

def __init__(self):

"""Creo una cola o buffer"""
self.q = Queue.Queue()
self.i = 0

self.playing = False # Me indica si el pipeline esta reproduciendo o no

"""Configurando Gstreamer"""
"""Creo un elemento tipo sample para alimentar al appsrc"""
self.audioSignal = Gst.Sample.new()

"""Creo el pipeline"""
self.play = Gst.Pipeline()

"""Creo el source"""
src = GstApp.AppSrc(format=Gst.Format.TIME, emit_signals=True)
src.connect('need-data', self.need_data, self.samples())
self.play.add(src)

"""adding capsfilter"""
capsFilterOne = Gst.ElementFactory.make("capsfilter", "capsFilterOne")
capsFilterOne.props.caps = Gst.Caps("audio/x-raw, format=(string)S16LE, rate=(int)44100, channels=(int)2")
self.play.add(capsFilterOne)
src.link(capsFilterOne)

"""Creo un audioconvert"""
audioConvert = Gst.ElementFactory.make("audioconvert")
self.play.add(audioConvert)
capsFilterOne.link(audioConvert)

"""adding capsfilter"""
capsFilterTwo = Gst.ElementFactory.make("capsfilter", "capsFilterTwo")
capsFilterTwo.props.caps = Gst.Caps("audio/x-raw, format=S16LE, rate=44100, channels=1")
self.play.add(capsFilterTwo)
audioConvert.link(capsFilterTwo)

"""Creo un audioresample"""
audioResample = Gst.ElementFactory.make("audioresample")
self.play.add(audioResample)
capsFilterTwo.link(audioResample)

"""adding capsfilter"""
capsFilterThree = Gst.ElementFactory.make("capsfilter", "capsFilterThree")
capsFilterThree.props.caps = Gst.Caps("audio/x-raw, format=S16LE, rate=16000, channels=1")
self.play.add(capsFilterThree)
audioResample.link(capsFilterThree)

"""Creo un objeto sink -> fakesink, autoaudiosink"""
sink = Gst.ElementFactory.make("autoaudiosink")
self.play.add(sink)
capsFilterThree.link(sink)

self.play.set_state(Gst.State.PAUSED)

"""Dequeue all samples"""
def samples(self):
sample = self.q.get()
if (self.q.qsize() < 2):
print "samples: Queue es pequeño"

while sample:
yield sample
sample = self.q.get()

"""write samples with Gst.AppSrc"""
def need_data(self, appsrc, length, samples):
sample = next(samples)
appsrc.set_caps(sample.get_caps())
appsrc.push_buffer(sample.get_buffer())

"""Interfaz entre la RAW_DATA y el pipeline"""
def write_raw(self,data):
self.audioBuffer = Gst.Buffer.new_wrapped(data)
self.caps = Gst.Caps.from_string("audio/x-raw, format=(string)S16LE, layout=(string)interleaved, channels=(int)2, channel-mask=(bitmask)0x0000000000000003, rate=(int)44100")
self.audioSignal=Gst.Sample.new(self.audioBuffer, self.caps, None, None)
self.q.put(self.audioSignal)
print "write_raw: El tamano de q es: ", self.q.qsize()

def set_pipeline_state(self,estado):
self.playing = False
if (estado == Gst.State.PLAYING):
self.playing = True

self.play.set_state(estado)

if __name__ == "__main__":
cliente = appSrcEjemplo2()

AUDIO_FILE = "/home/usuario/Music/Toninho Horta _ Jack Lee/Track 9.wav" #este es el path hacia el archivo wav
audio=wave.open(AUDIO_FILE)

frames = audio.readframes(882)

while frames:
cliente.write_raw(frames)
time.sleep(0.0097)   # espero un momento para no saturar el buffer self.q
if (not cliente.playing):
cliente.set_pipeline_state(Gst.State.PLAYING)
frames = audio.readframes(441)

audio.close()


Como comentarios finales, les debo mencionar que sería mucho más eficiente haciéndolo en C/C++. También que lo importante es la clase appSrcEjemplo2, ya que el código del main es simplemente para generar la RAW_DATA.

Por último, no se olviden de comentar, por favor hagan sugerencias y/o correcciones (ya saben para mejorar) y no se olviden que no soy un experto, pero si en algo les puedo ayudar con gusto.


Slds

No hay comentarios:

Publicar un comentario