# This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 # USA # Copyright (C) 2013 Manuel Kaufmann import logging from gi.repository import Gst from gi.repository import GObject # Needed for window.get_xid(), xvimagesink.set_window_handle(), # respectively: from gi.repository import GdkX11, GstVideo # Avoid "Fatal Python error: GC object already tracked" # http://stackoverflow.com/questions/7496629/ # gstreamer-appsrc-causes-random-crashes # GObject.threads_init() # Initialize GStreamer Gst.init(None) class GstPlayer(GObject.GObject): __gsignals__ = { 'error': (GObject.SignalFlags.RUN_FIRST, None, [str, str]), 'eos': (GObject.SignalFlags.RUN_FIRST, None, []), 'play': (GObject.SignalFlags.RUN_FIRST, None, []), } def __init__(self): GObject.GObject.__init__(self) self.playing = False self.error = False # Create GStreamer pipeline self.pipeline = Gst.Pipeline() # Create bus to get events from GStreamer pipeline self.bus = self.pipeline.get_bus() self.bus.add_signal_watch() self.bus.connect('message::eos', self.__on_eos_message) self.bus.connect('message::error', self.__on_error_message) # This is needed to make the video output in our DrawingArea self.bus.enable_sync_message_emission() self.bus.connect('sync-message::element', self.__on_sync_message) # Create GStreamer elements self.player = Gst.ElementFactory.make('playbin', None) self.pipeline.add(self.player) def init_view_area(self, videowidget): videowidget.realize() self.videowidget = videowidget self.videowidget_xid = videowidget.get_window().get_xid() def __on_error_message(self, bus, msg): self.stop() self.playing = False self.error = True err, debug = msg.parse_error() self.emit('error', err, debug) def __on_eos_message(self, bus, msg): logging.debug('SIGNAL: eos') self.playing = False self.emit('eos') def __on_sync_message(self, bus, msg): if msg.get_structure().get_name() == 'prepare-window-handle': msg.src.set_window_handle(self.videowidget_xid) def set_uri(self, uri): self.pipeline.set_state(Gst.State.READY) # gstreamer needs the 'file://' prefix uri = 'file://' + uri logging.debug('URI: %s', uri) self.player.set_property('uri', uri) def query_position(self): "Returns a (position, duration) tuple" p_success, position = self.player.query_position(Gst.Format.TIME) d_success, duration = self.player.query_duration(Gst.Format.TIME) return (p_success and d_success, position, duration) def seek(self, location): """ @param location: time to seek to, in nanoseconds """ logging.debug('Seek: %s ns', location) self.pipeline.seek_simple(Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, location) def pause(self): logging.debug("pausing player") self.pipeline.set_state(Gst.State.PAUSED) self.playing = False def play(self): logging.debug("playing player") self.pipeline.set_state(Gst.State.PLAYING) self.playing = True self.error = False self.emit('play') def stop(self): self.playing = False self.pipeline.set_state(Gst.State.NULL) logging.debug("stopped player") def get_state(self, timeout=1): return self.player.get_state(timeout=timeout) def is_playing(self): return self.playing def playing_video(self): return self.player.props.n_video > 0