Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/player.py
diff options
context:
space:
mode:
authorManuel Kaufmann <humitos@gmail.com>2013-01-14 15:42:35 (GMT)
committer Gonzalo Odiard <godiard@gmail.com>2013-01-25 12:38:40 (GMT)
commit0bf46d8bf132609bab93a93360e78e09f01d7b88 (patch)
tree45cf4f0892622a61b3a06caf8365920dadc7f0b1 /player.py
parentf4ac3ea1126a571bd0b8e60bcced451066853b9c (diff)
Modularization of classes
- Put each class in a different .py file - Rename jukeboxactivity.py to activity.py to make this more standard Signed-off-by: Manuel Kaufmann <humitos@gmail.com> Reviewed-by: Gonzalo Odiard <gonzalo@laptop.org>
Diffstat (limited to 'player.py')
-rw-r--r--player.py182
1 files changed, 182 insertions, 0 deletions
diff --git a/player.py b/player.py
new file mode 100644
index 0000000..b5b03da
--- /dev/null
+++ b/player.py
@@ -0,0 +1,182 @@
+# 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 <humitos@gmail.com>
+
+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, []),
+ }
+
+ def __init__(self, videowidget):
+ 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)
+
+ # Set the proper flags to render the vis-plugin
+ GST_PLAY_FLAG_VIS = 1 << 3
+ GST_PLAY_FLAG_TEXT = 1 << 2
+ self.player.props.flags |= GST_PLAY_FLAG_VIS
+ self.player.props.flags |= GST_PLAY_FLAG_TEXT
+
+ r = Gst.Registry.get()
+ l = [x for x in r.get_feature_list(Gst.ElementFactory)
+ if (x.get_metadata('klass') == "Visualization")]
+ if len(l):
+ e = l.pop() # take latest plugin in the list
+ vis_plug = Gst.ElementFactory.make(e.get_name(), e.get_name())
+ self.player.set_property('vis-plugin', vis_plug)
+
+ self.overlay = None
+ videowidget.realize()
+ self.videowidget = videowidget
+ self.videowidget_xid = videowidget.get_window().get_xid()
+ self._init_video_sink()
+
+ 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)
+ logging.debug('### Setting URI: %s', uri)
+ self.player.set_property('uri', uri)
+
+ def _init_video_sink(self):
+ self.bin = Gst.Bin()
+ videoscale = Gst.ElementFactory.make('videoscale', 'videoscale')
+ self.bin.add(videoscale)
+ pad = videoscale.get_static_pad("sink")
+ ghostpad = Gst.GhostPad.new("sink", pad)
+ self.bin.add_pad(ghostpad)
+ videoscale.set_property("method", 0)
+
+ textoverlay = Gst.ElementFactory.make('textoverlay', 'textoverlay')
+ self.overlay = textoverlay
+ self.bin.add(textoverlay)
+ conv = Gst.ElementFactory.make("videoconvert", "conv")
+ self.bin.add(conv)
+ videosink = Gst.ElementFactory.make('autovideosink', 'autovideosink')
+ self.bin.add(videosink)
+
+ videoscale.link(textoverlay)
+ textoverlay.link(conv)
+ conv.link(videosink)
+
+ self.player.set_property("video-sink", self.bin)
+
+ def set_overlay(self, title, artist, album):
+ text = "%s\n%s" % (title, artist)
+ if album and len(album):
+ text += "\n%s" % album
+ self.overlay.set_property("text", text)
+ self.overlay.set_property("font-desc", "sans bold 14")
+ self.overlay.set_property("halignment", "right")
+ self.overlay.set_property("valignment", "bottom")
+ try:
+ # Only in OLPC versions of gstreamer-plugins-base for now
+ self.overlay.set_property("line-align", "left")
+ except:
+ pass
+
+ 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
+
+ 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