[Sugar-devel] [PATCH Jukebox 8/8] Port gst 0.10 to Gst 1.0
Manuel Kaufmann
humitos at gmail.com
Thu Oct 4 13:29:28 EDT 2012
Useful guide:
* https://wiki.ubuntu.com/Novacut/GStreamer1.0
Signed-off-by: Manuel Kaufmann <humitos at gmail.com>
---
jukeboxactivity.py | 180 ++++++++++++++++++++++++++---------------------------
1 file changed, 90 insertions(+), 90 deletions(-)
diff --git a/jukeboxactivity.py b/jukeboxactivity.py
index 1ad5ff1..48a5aa8 100644
--- a/jukeboxactivity.py
+++ b/jukeboxactivity.py
@@ -40,15 +40,23 @@ from sugar3.graphics.alert import ErrorAlert
import gi
gi.require_version('Gtk', '3.0')
+gi.require_version('Gst', '1.0')
from gi.repository import GObject
from gi.repository import Gdk
-
-import pygst
-pygst.require('0.10')
-import gst
-import gst.interfaces
from gi.repository import Gtk
+from gi.repository import Gst
+
+# 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)
import urllib
from ControlToolbar import Control, ViewToolbar
@@ -130,9 +138,6 @@ class JukeboxActivity(activity.Activity):
self.currentplaying = None
self.playflag = False
- self.p_position = gst.CLOCK_TIME_NONE
- self.p_duration = gst.CLOCK_TIME_NONE
-
# README: I changed this because I was getting an error when I
# tried to modify self.bin with something different than
# Gtk.Bin
@@ -169,6 +174,8 @@ class JukeboxActivity(activity.Activity):
self.player = GstPlayer(self.videowidget)
self.player.connect("eos", self._player_eos_cb)
self.player.connect("error", self._player_error_cb)
+ self.p_position = Gst.CLOCK_TIME_NONE
+ self.p_duration = Gst.CLOCK_TIME_NONE
def _notify_active_cb(self, widget, event):
"""Sugar notify us that the activity is becoming active or inactive.
@@ -524,7 +531,7 @@ class JukeboxActivity(activity.Activity):
real = long(scale.get_value() * self.p_duration / 100) # in ns
self.player.seek(real)
# allow for a preroll
- self.player.get_state(timeout=50 * gst.MSECOND) # 50 ms
+ self.player.get_state(timeout=50 * Gst.MSECOND) # 50 ms
def scale_button_release_cb(self, widget, event):
# see seek.cstop_seek
@@ -546,8 +553,13 @@ class JukeboxActivity(activity.Activity):
self.update_scale_cb)
def update_scale_cb(self):
- self.p_position, self.p_duration = self.player.query_position()
- if self.p_position != gst.CLOCK_TIME_NONE:
+ success, self.p_position, self.p_duration = \
+ self.player.query_position()
+
+ if not success:
+ return True
+
+ if self.p_position != Gst.CLOCK_TIME_NONE:
value = self.p_position * 100.0 / self.p_duration
self.control.adjustment.set_value(value)
@@ -558,7 +570,7 @@ class JukeboxActivity(activity.Activity):
# FIXME: this should be updated just once when the file starts
# the first time
- if self.p_duration != gst.CLOCK_TIME_NONE:
+ if self.p_duration != Gst.CLOCK_TIME_NONE:
seconds = self.p_duration * 10 ** -9
time = '%2d:%02d' % (int(seconds / 60), int(seconds % 60))
self.control.total_time_label.set_text(time)
@@ -592,7 +604,22 @@ class GstPlayer(GObject.GObject):
self.playing = False
self.error = False
- self.player = gst.element_factory_make("playbin2", "player")
+ # 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
@@ -600,12 +627,12 @@ class GstPlayer(GObject.GObject):
self.player.props.flags |= GST_PLAY_FLAG_VIS
self.player.props.flags |= GST_PLAY_FLAG_TEXT
- r = gst.registry_get_default()
- l = [x for x in r.get_feature_list(gst.ElementFactory)
- if (gst.ElementFactory.get_klass(x) == "Visualization")]
+ 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.element_factory_make(e.get_name())
+ vis_plug = Gst.ElementFactory.make(e.get_name(), e.get_name())
self.player.set_property('vis-plugin', vis_plug)
self.overlay = None
@@ -614,54 +641,48 @@ class GstPlayer(GObject.GObject):
self.videowidget_xid = videowidget.get_window().get_xid()
self._init_video_sink()
- bus = self.player.get_bus()
- bus.enable_sync_message_emission()
- bus.add_signal_watch()
- bus.connect('sync-message::element', self.on_sync_message)
- bus.connect('message', self.on_message)
+ 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.player.set_state(gst.STATE_PAUSED)
- self.player.set_state(gst.STATE_NULL)
+ self.pipeline.set_state(Gst.State.READY)
+ logging.debug('### Setting URI: %s', uri)
self.player.set_property('uri', uri)
- def on_sync_message(self, bus, message):
- if message.structure is None:
- return
- if message.structure.get_name() == 'prepare-xwindow-id':
- self.videowidget.set_sink(message.src, self.videowidget_xid)
- message.src.set_property('force-aspect-ratio', True)
-
- def on_message(self, bus, message):
- t = message.type
- if t == gst.MESSAGE_ERROR:
- err, debug = message.parse_error()
- logging.debug("Error: %s - %s" % (err, debug))
- self.error = True
- self.emit("eos")
- self.playing = False
- self.emit("error", str(err), str(debug))
- elif t == gst.MESSAGE_EOS:
- self.emit("eos")
- self.playing = False
-
def _init_video_sink(self):
- self.bin = gst.Bin()
- videoscale = gst.element_factory_make('videoscale')
+ self.bin = Gst.Bin()
+ videoscale = Gst.ElementFactory.make('videoscale', 'videoscale')
self.bin.add(videoscale)
- pad = videoscale.get_pad("sink")
- ghostpad = gst.GhostPad("sink", pad)
+ pad = videoscale.get_static_pad("sink")
+ ghostpad = Gst.GhostPad.new("sink", pad)
self.bin.add_pad(ghostpad)
videoscale.set_property("method", 0)
- textoverlay = gst.element_factory_make('textoverlay')
+ textoverlay = Gst.ElementFactory.make('textoverlay', 'textoverlay')
self.overlay = textoverlay
self.bin.add(textoverlay)
- conv = gst.element_factory_make("ffmpegcolorspace", "conv")
+ conv = Gst.ElementFactory.make("videoconvert", "conv")
self.bin.add(conv)
- videosink = gst.element_factory_make('autovideosink')
+ videosink = Gst.ElementFactory.make('autovideosink', 'autovideosink')
self.bin.add(videosink)
- gst.element_link_many(videoscale, textoverlay, conv, 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):
@@ -680,46 +701,37 @@ class GstPlayer(GObject.GObject):
def query_position(self):
"Returns a (position, duration) tuple"
- try:
- position, format = self.player.query_position(gst.FORMAT_TIME)
- except:
- position = gst.CLOCK_TIME_NONE
- try:
- duration, format = self.player.query_duration(gst.FORMAT_TIME)
- except:
- duration = gst.CLOCK_TIME_NONE
+ p_success, position = self.player.query_position(Gst.Format.TIME)
+ d_success, duration = self.player.query_duration(Gst.Format.TIME)
- return (position, duration)
+ return (p_success and d_success, position, duration)
def seek(self, location):
"""
@param location: time to seek to, in nanoseconds
"""
- event = gst.event_new_seek(1.0, gst.FORMAT_TIME,
- gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_ACCURATE,
- gst.SEEK_TYPE_SET, location,
- gst.SEEK_TYPE_NONE, 0)
-
- res = self.player.send_event(event)
- if res:
- self.player.set_new_stream_time(0L)
- else:
- logging.debug("seek to %r failed" % location)
+
+ 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.player.set_state(gst.STATE_PAUSED)
+ self.pipeline.set_state(Gst.State.PAUSED)
self.playing = False
def play(self):
logging.debug("playing player")
- self.player.set_state(gst.STATE_PLAYING)
+ self.pipeline.set_state(Gst.State.PLAYING)
self.playing = True
self.error = False
def stop(self):
- self.player.set_state(gst.STATE_NULL)
+ self.playing = False
+ self.pipeline.set_state(Gst.State.NULL)
logging.debug("stopped player")
def get_state(self, timeout=1):
@@ -733,26 +745,14 @@ class VideoWidget(Gtk.DrawingArea):
def __init__(self):
GObject.GObject.__init__(self)
self.set_events(Gdk.EventMask.POINTER_MOTION_MASK |
- Gdk.EventMask.POINTER_MOTION_HINT_MASK |
- Gdk.EventMask.EXPOSURE_MASK |
- Gdk.EventMask.KEY_PRESS_MASK |
- Gdk.EventMask.KEY_RELEASE_MASK)
- self.imagesink = None
+ Gdk.EventMask.POINTER_MOTION_HINT_MASK |
+ Gdk.EventMask.EXPOSURE_MASK |
+ Gdk.EventMask.KEY_PRESS_MASK |
+ Gdk.EventMask.KEY_RELEASE_MASK)
self.set_app_paintable(True)
self.set_double_buffered(False)
- def do_expose_event(self, event):
- if self.imagesink:
- self.imagesink.expose()
- return False
- else:
- return True
-
- def set_sink(self, sink, xid):
- self.imagesink = sink
- self.imagesink.set_xwindow_id(xid)
-
if __name__ == '__main__':
window = Gtk.Window()
--
1.7.11.4
More information about the Sugar-devel
mailing list