#!/usr/bin/env python # -*- coding: utf-8 -*- # JAMediaReproductor.py por: # Flavio Danesse # CeibalJAM! - Uruguay # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # Se remplaza: # Depends: python-gst0.10, # gstreamer0.10-plugins-good, # gstreamer0.10-plugins-ugly, # gstreamer0.10-plugins-bad, # gstreamer0.10-ffmpeg # Con: # Depends: python-gi, # gir1.2-gstreamer-1.0, # gir1.2-gst-plugins-base-1.0, # gstreamer1.0-plugins-good, # gstreamer1.0-plugins-ugly, # gstreamer1.0-plugins-bad, # gstreamer1.0-libav # https://wiki.ubuntu.com/Novacut/GStreamer1.0#Using_GStreamer_1.0_from_Python # http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/GstBus.html#gst-bus-create-watch # http://www.roojs.org/seed/gir-1.1-gtk-2.0/Gst.MessageType.html#expand import os import gi gi.require_version('Gst', '1.0') from gi.repository import GObject from gi.repository import Gst from gi.repository import GstVideo GObject.threads_init() Gst.init(None) class JAMediaReproductor(GObject.GObject): """ Reproductor de Audio, Video y Streaming de Radio y Television. Implementado sobre: python 2.7.3 Gtk 3 Gstreamer 1.0 """ __gsignals__ = {"endfile":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, []), "estado":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_STRING,)), "newposicion":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_INT,)), "volumen":(GObject.SIGNAL_RUN_FIRST, GObject.TYPE_NONE, (GObject.TYPE_FLOAT,))} # Estados: playing, paused, None def __init__(self, ventana_id): """ Recibe el id de un DrawingArea para mostrar el video. """ GObject.GObject.__init__(self) self.name = "JAMediaReproductor" self.ventana_id = ventana_id self.pipeline = None self.estado = None self.duracion = 0 self.posicion = 0 self.actualizador = None self.player = None self.bus = None self.set_pipeline() def set_pipeline(self): """Crea el pipe de Gst. (playbin)""" if self.pipeline: del(self.pipeline) self.pipeline = Gst.Pipeline() self.bus = self.pipeline.get_bus() self.bus.add_signal_watch() self.bus.connect('message', self.on_mensaje) self.bus.enable_sync_message_emission() self.bus.connect('sync-message', self.sync_message) self.player = Gst.ElementFactory.make("playbin", "player") self.pipeline.add(self.player) def sync_message(self, bus, mensaje): """Captura los mensajes en el bus del pipe Gst.""" try: if mensaje.get_structure().get_name() == 'prepare-window-handle': mensaje.src.set_window_handle(self.ventana_id) return except: pass if mensaje.type == Gst.MessageType.STATE_CHANGED: old, new, pending = mensaje.parse_state_changed() if old == Gst.State.PAUSED and new == Gst.State.PLAYING: if self.estado != new: self.estado = new self.emit("estado", "playing") self.new_handle(True) return elif old == Gst.State.READY and new == Gst.State.PAUSED: if self.estado != new: self.estado = new self.emit("estado", "paused") self.new_handle(False) return elif old == Gst.State.READY and new == Gst.State.NULL: if self.estado != new: self.estado = new self.emit("estado", "None") self.new_handle(False) return elif old == Gst.State.PLAYING and new == Gst.State.PAUSED: if self.estado != new: self.estado = new self.emit("estado", "paused") self.new_handle(False) return elif old == Gst.State.NULL and new == Gst.State.READY: pass elif old == Gst.State.PAUSED and new == Gst.State.READY: pass else: #print ">>>", old, new, pending return elif mensaje.type == Gst.MessageType.ASYNC_DONE: #print mensaje.get_structure().get_name() return elif mensaje.type == Gst.MessageType.NEW_CLOCK: return elif mensaje.type == Gst.MessageType.STREAM_STATUS: #print mensaje.parse_stream_status() return elif mensaje.type == Gst.MessageType.TAG: return elif mensaje.type == Gst.MessageType.ERROR: err, debug = mensaje.parse_error() print "***", 'sync_message' print err, debug self.new_handle(False) #self.pipeline.set_state(Gst.State.NULL) #self.pipeline.set_state(Gst.State.READY) #return self.set_pipeline() return elif mensaje.type == Gst.MessageType.EOS: return else: try: nombre = mensaje.get_structure().get_name() if nombre == "playbin-stream-changed": #print "Nuevo src:", mensaje.get_structure().to_string() pass elif nombre == "have-window-handle": pass elif nombre == "prepare-window-handle": pass else: pass except: print "sync_message", mensaje.type return def on_mensaje(self, bus, mensaje): """Captura los mensajes en el bus del pipe Gst.""" #if mensaje.type == Gst.MessageType.ASYNC_DONE: # print mensaje.get_structure().get_name() #elif mensaje.type == Gst.MessageType.NEW_CLOCK: # pass if mensaje.type == Gst.MessageType.ELEMENT: nombre = mensaje.get_structure().get_name() # playbin-stream-changed , prepare-window-handle, have-window-handle #if nombre == 'prepare-window-handle': # mensaje.src.set_window_handle(self.ventana_id) #else: # print nombre ''' elif mensaje.type == Gst.MessageType.STATE_CHANGED: old, new, pending = mensaje.parse_state_changed() if old == Gst.State.PAUSED and new == Gst.State.PLAYING: if self.estado != new: self.estado = new #self.emit("estado", "playing") elif old == Gst.State.READY and new == Gst.State.PAUSED: if self.estado != new: self.estado = new #self.emit("estado", "paused") else: if self.F: print old, new, pending pass elif mensaje.type == Gst.MessageType.STREAM_STATUS: pass elif mensaje.type == Gst.MessageType.TAG: #print mensaje.get_structure().to_string() pass''' if mensaje.type == Gst.MessageType.EOS: #self.pipeline.seek_simple(Gst.Format.TIME, #Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, 0) self.new_handle(False) self.set_pipeline() self.emit("endfile") elif mensaje.type == Gst.MessageType.QOS: pass elif mensaje.type == Gst.MessageType.WARNING: print mensaje.get_structure().to_string() #self.new_handle(False) #self.pipeline.set_state(Gst.State.NULL) #self.set_pipeline() elif mensaje.type == Gst.MessageType.ERROR: err, debug = mensaje.parse_error() print "***", 'sync_message' print err, debug self.new_handle(False) #self.pipeline.set_state(Gst.State.NULL) #self.set_pipeline() self.pipeline.set_state(Gst.State.READY) elif mensaje.type == Gst.MessageType.LATENCY: #print mensaje.type pass else: #print "on_mensaje", mensaje.type pass def load(self, uri): """Carga un archivo o stream en el pipe de Gst.""" self.stop() if os.path.exists(uri): direccion = Gst.filename_to_uri(uri) self.player.set_property("uri", direccion) self.play() else: # FIXME: Funciona con la radio pero no con la Tv direccion = uri #Gst.uri_get_protocol(uri) if Gst.uri_is_valid(uri): self.player.set_property("uri", direccion) self.play() #print Gst.uri_protocol_is_supported(uri, Gst.uri_get_protocol(uri)) #print Gst.uri_protocol_is_valid(Gst.uri_get_protocol(uri)) #self.player.set_property("buffer-size", 1024) #self.player.set_property("force-aspect-ratio", False) def play(self): """Pone el pipe de Gst en Gst.State.PLAYING""" self.pipeline.set_state(Gst.State.PLAYING) def stop(self): """Pone el pipe de Gst en Gst.State.NULL""" self.pipeline.set_state(Gst.State.NULL) self.pipeline.set_state(Gst.State.READY) def pause(self): """Pone el pipe de Gst en Gst.State.PAUSED""" self.pipeline.set_state(Gst.State.PAUSED) def pause_play(self): """Llama a play() o pause() segun el estado actual del pipe de Gst.""" if self.estado == Gst.State.PAUSED \ or self.estado == Gst.State.NULL \ or self.estado == Gst.State.READY: self.play() elif self.estado == Gst.State.PLAYING: self.pause() else: print self.estado def new_handle(self, reset): """Elimina o reinicia la funcion que envia los datos de actualizacion para la barra de progreso del reproductor.""" if self.actualizador: GObject.source_remove(self.actualizador) self.actualizador = None if reset: self.actualizador = GObject.timeout_add(300, self.handle) def handle(self): """Funcion que envia los datos de actualizacion para la barra de progreso del reproductor.""" bool1, valor1 = self.pipeline.query_duration(Gst.Format.TIME) bool2, valor2 = self.pipeline.query_position(Gst.Format.TIME) duracion = float(valor1) posicion = float(valor2) pos = 0 try: pos = int(posicion * 100 / duracion) except: pass if pos < 0 or pos > self.duracion: return True if self.duracion != duracion: self.duracion = duracion if pos != self.posicion: self.posicion = pos self.emit("newposicion", self.posicion) # print "***", gst.video_convert_frame(self.player.get_property("frame")) return True def set_position(self, posicion): """Funcion que permite desplazarse por el archivo que se esta reproduciendo.""" if self.duracion < posicion: self.emit("newposicion", self.posicion) return posicion = self.duracion * posicion / 100 self.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH, posicion) def get_volumen(self): pass def set_volumen(self, valor): pass