[[Novacut|Novacut Wiki Home]] > '''GStreamer 1.0''' ''Port your Python app to PyGI, Gtk3, and GStreamer 1.0!'' GStreamer 1.0 packages are now available in [[http://packages.ubuntu.com/search?keywords=gstreamer1.0|Ubuntu Quantal]]. For Ubuntu Precise (12.04 LTS) users, there are back-ported packages available in the [[https://launchpad.net/~gstreamer-developers/+archive/ppa?field.series_filter=precise|GStreamer developers PPA]]. !GnonLin and GES packages are not yet available for GStreamer 1.0 (but hopefully soon). Although GStreamer 1.0 is ''not'' backward compatible with the GStreamer 0.10 series, it is parallel-installable. So you can safely install GStreamer 1.0 without effect on the many applications using GStreamer 0.10. The focus here is porting Python apps. Also see: * [[http://cgit.freedesktop.org/gstreamer/gstreamer/tree/docs/random/porting-to-1.0.txt|porting-to-1.0.txt (C Porting Guide)]] * [[https://live.gnome.org/GnomeGoals/PortToGstreamer1|GNOME Goal: Port your application to GStreamer 1.0]] <> == Adding PPA for Ubuntu Precise == To use the back-ported packages for Ubuntu 12.04 LTS, add the GStreamer developers PPA like this: {{{ sudo apt-add-repository ppa:gstreamer-developers/ppa sudo apt-get update }}} == Installing GStreamer 1.0 packages == This should give you all the packages you likely want: {{{ sudo apt-get install python-gi python3-gi \ gstreamer1.0-tools \ 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 }}} == Using GStreamer 1.0 from Python == You can use GStreamer 1.0 from Python (2 or 3) using PyGI like this: {{{ >>> import gi >>> gi.require_version('Gst', '1.0') >>> from gi.repository import Gst >>> Gst.version() (1, 0, 0, 0) }}} Although currently the default will be Gst 1.0 if available, it's still a good idea to `gi.require_version('Gst', '1.0')` so that the code will clearly fail if Gst 1.0 isn't available. The biggest change for Python users will be using the introspected API rather than the static python-gst bindings. However, there are also API changes in GStreamer itself, although these changes are generally more subtle. == Python Porting Guide == This is a guide to porting your PyGST app to GStreamer1.0 + PyGI. To whet your appetite, here's a simple (less than 100 lines) [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/video-player-0.10|PyGST video player]], and here's the [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/video-player-1.0|PyGI + GStreamer1.0 + Gtk3 port]] of that same simple video player. Also see [[http://cgit.freedesktop.org/gstreamer/gstreamer/tree/docs/random/porting-to-1.0.txt|porting-to-1.0.txt]]. Although from a C perspective, you can usually figure out the equivalent Python API fairly easily once you get the hang of PyGI. Some obscure features might still be broken or unavailable through PyGI, so if you end up digging into the C code, you'll want to read about [[https://live.gnome.org/GObjectIntrospection/Annotations|how the introspection annotations work]]. Note that although you can ''sort-of'' use GStreamer0.10 with PyGI, key functionality is fundamentally broken and wont ever be fixed in GStreamer0.10 (because it would require API breakage). It's unproductive to try and first port to PyGI, then to GStreamer1.0. You should do both in one move. If your PyGST app is also a PyGTK app (fairly likely), you ''must'' port the Gtk bits to PyGI + Gtk3 at the same time. You ''cannot'' use PyGI and any of the static python-gobject based bindings together in the same process. You just have to jump into the deep end and go for it! If you have extensive unit tests, pat yourself on the back, because you'll have a much easier time. On the upside, once you port to PyGI, it's generally very easy to port to [[Python/3|Python3]]. And you should, all the cool kids are doing it. === Examples === Some small reference examples of common GStreamer use-cases, with both a 0.10 and a 1.0 version: || Video Player: || [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/video-player-0.10|video-player-0.10]] || [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/video-player-1.0|video-player-1.0]] || || Webcam Capture: || [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/webcam-0.10|webcam-0.10]] || [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/webcam-1.0|webcam-1.0]] || || A/V Transcoder: || [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/transcoder-0.10|transcoder-0.10]] || [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/transcoder-1.0|transcoder-1.0]] || || Python Element: || [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/plugin-0.10|plugin-0.10]] || [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/plugin-1.0|plugin-1.0]] || Grab all the example code and the sample video like this: {{{ bzr branch lp:~jderose/+junk/gst-examples }}} === debian/control === You'll have to update your `debian/control`, of course. So replace something like this: {{{ Depends: python-gst0.10, gstreamer0.10-plugins-good, gstreamer0.10-plugins-ugly, gstreamer0.10-plugins-bad, gstreamer0.10-ffmpeg }}} With this: {{{ 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 }}} Then when you port to Python3, replace '''python-gi''' with '''python3-gi'''. === Imports === Replace: {{{ import gobject import gst }}} With: {{{ import gi gi.require_version('Gst', '1.0') from gi.repository import GObject, Gst }}} After that a bit of search and replace (used with care) can help. Basically: {{{ gobject.* => GObject.* gst.* => Gst.* }}} === Gst.init() === You should call `GObject.threads_init()` in your module-scope, right after your imports. Unlike the static bindings, you also need to call `Gst.init()`. So replace: {{{ gobject.threads_init() }}} With: {{{ GObject.threads_init() Gst.init(None) }}} === element_factory_make() === Replace: {{{ src = gst.element_factory_make('filesrc') }}} With: {{{ src = Gst.ElementFactory.make('filesrc', None) }}} Note that unlike `element_factory_make()`, `ElementFactory.make()` will return `None` rather than raising an exception when the element is ''not'' found. === element_link_many() === There is no equivalent to `gst.element_link_many()`, so replace: {{{ gst.element_link_many(one, two, three) }}} With: {{{ one.link(two) two.link(three) }}} === Pipeline.add(one, two) === `Bin.add()` can't be overloaded to add multiple elements at once, so replace: {{{ pipeline = gst.Pipeline() pipeline.add(one, two) }}} With: {{{ pipeline = Gst.Pipeline() pipeline.add(one) pipeline.add(two) }}} === one.link(two, mycaps) === Replace: {{{ one.link(two, mycaps) }}} With: {{{ one.link_filtered(two, mycaps) }}} === STATE_PLAYING === Replace: {{{ pipeline.set_state(gst.STATE_PLAYING) }}} With: {{{ pipeline.set_state(Gst.State.PLAYING) }}} And so on. Some search-and-replace is helpful here, basically: {{{ gst.STATE_* => Gst.State.* }}} === SEEK_FLAG_FLUSH === Replace something like this: {{{ pipeline.seek_simple( gst.FORMAT_TIME, gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT, nanoseconds ) }}} With this: {{{ pipeline.seek_simple( Gst.Format.TIME, Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, nanoseconds ) }}} Search and replace: {{{ gst.SEEK_FLAG_* => Gst.SeekFlags.* }}} === FORMAT_TIME === Search and replace: {{{ gst.FORMAT_* => Gst.Format.* }}} === prepare-xwindow-id === "prepare-xwindow-id" has been renamed "prepare-window-handle", and a few other details have changed (some on the PyGI + Gtk3 side). So replace something like this: {{{ xid = drawingarea.window.xid def on_sync_message(bus, msg): if msg.structure.get_name() == 'prepare-xwindow-id': msg.src.set_xwindow_id(xid) }}} With this: {{{ xid = drawingarea.get_property('window').get_xid() def on_sync_message(bus, msg): if msg.get_structure().get_name() == 'prepare-window-handle': msg.src.set_window_handle(xid) }}} Note that trying to get the drawingarea XID in your on_sync_message() handler will cause a segfault because of threading issues. Also note that for the above to work, you must have imported both `GdkX11` and `GstVideo`. So in your imports, make sure to include something like this: {{{ # Needed for window.get_xid(), xvimagesink.set_window_handle(), respectively: from gi.repository import GdkX11, GstVideo }}} See this [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/video-player-1.0|video-player-1.0]] script for a simple video player example. You might want to compare it to [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/video-player-0.10|video-player-0.10]] to see how the same thing was done with python-gst. === GhostPad === You've got to use `GhostPad.new()`. So replace: {{{ ghost = gst.GhostPad('sink', mysinkpad) }}} With: {{{ ghost = Gst.GhostPad.new('sink', mysinkpad) }}} === Pad.get_caps() === In callbacks for "pad-added" events and similar, it's common to use the string representation of the pad caps as a way to decide whether to link a pad and what to link the pad to. You need to use `Pad.query_caps()` instead of `Pad.get_caps()`, and the returned object is no longer array-like. So in general replace this pattern: {{{ def on_pad_added(element, pad): string = pad.get_caps()[0].get_name() if string.startswith('audio/'): }}} With this: {{{ def on_pad_added(element, pad): string = pad.query_caps(None).to_string() if string.startswith('audio/'): }}} === Element.get_pad() === `Element.get_pad()` has been renamed `Element.get_static_pad()`. So replace something like this: {{{ src = gst.element_factory_make('filesink') pad = src.get_pad('sink') }}} With this: {{{ src = Gst.ElementFactory.make('filesink', None) pad = src.get_static_pad('sink') }}} === Buffer.data === In a "preroll-handoff" or "handoff" callback, it's common to want access to the buffer data. A common use case is saving a JPEG thumbnail to a file, for example. Replace something like this: {{{ def on_preroll_handoff(element, buf, pad): data = buf.data }}} With this: {{{ def on_preroll_handoff(element, buf, pad): data = buf.extract_dup(0, buf.get_size()) }}} === Buffer.timestamp === `Buffer.timestamp` has been renamed to `Buffer.pts` (Presentation Time Stamp). So replace something like this: {{{ def on_handoff(element, buf, pad): timestamp = buf.timestamp }}} With this: {{{ def on_handoff(element, buf, pad): timestamp = buf.pts }}} There is also the new `Buffer.dts` (Decode Time Stamp). Often pts and dts will be the same, but B-frames are an example of when they'll be different. === Pad.get_negotiated_caps() === `Pad.get_negotiated_caps()` has been renamed `Pad.get_current_caps()`, so replace: {{{ caps = pad.get_negotiated_caps() }}} With: {{{ caps = pad.get_current_caps() }}} === caps[0] === Caps objects are not array-like from PyGI, so replace: {{{ cap = caps[0] }}} With: {{{ cap = caps.get_structure(0) }}} === caps[0]['foo'] === Individual capability structures are not dictionary-like objects from PyGI, so you need to use type-appropriate accessor methods instead. For example, replace this: {{{ framerate = caps[0]['framerate'] width = caps[0]['width'] height = caps[0]['height'] }}} With: {{{ (success, num, denom) = caps.get_structure(0).get_fraction('framerate') (success, width) = caps.get_structure(0).get_int('width') (success, height) = caps.get_structure(0).get_int('height') }}} === query_new_duration() === Replace: {{{ query = gst.query_new_duration(Gst.FORMAT_TIME) }}} With: {{{ query = Gst.Query.new_duration(Gst.Format.TIME) }}} === audio/x-raw, video/x-raw === "audio/x-raw-int" and "audio/x-raw-float" have been condensed into a unified "audio/x-raw" with a flexible format description. Likewise, "video/x-raw-yuv" and "video/x-raw-rgb" have been condensed into a unified "video/x-raw" with a flexible format description. This is much nicer because in GStreamer 0.10 certain element details were leaked that really shouldn't have been. For example, if you wanted filter caps to force a certain sample-rate, you previously needed to know whether, say, an audio encoder took audio/x-raw-int or audio/x-raw-float. So now you can replace say: {{{ caps = gst.caps_from_string('audio/x-raw-float, rate=(int)44100') }}} With: {{{ caps = Gst.caps_from_string('audio/x-raw, rate=(int)44100') }}} As you're only interested in specifying the rate anyway, it's much nicer to be truly abstracted from the format details. === element_register() === The `gst.element_register()` function had some magic to create an in-process plugin on the fly if needed (assuming Jason understood this detail correctly when talking with Edward Hervey about it). You can still register in-process elements with PyGI, but you need to use a bit more of the C API now and explicitly register your plugin, and then register your elements. So replace something like this: {{{ gobject.type_register(DmediaSrc) gst.element_register(DmediaSrc, 'dmediasrc') }}} With something like this: {{{ def plugin_init(plugin, userarg): DmediaSrcType = GObject.type_register(DmediaSrc) Gst.Element.register(plugin, 'dmediasrc', 0, DmediaSrcType) return True version = Gst.version() Gst.Plugin.register_static_full( version[0], # GST_VERSION_MAJOR version[1], # GST_VERSION_MINOR 'dmedia', 'dmedia src plugin', plugin_init, '12.06', 'LGPL', 'dmedia', 'dmedia', 'https://launchpad.net/dmedia', None, ) }}} See [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/plugin-0.10|plugin-0.10]] for an example python-gst plugin, and then [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/plugin-1.0|plugin-1.0]] for how to do the same with PyGI and GSTreamer 1.0. === PAD_SRC, PAD_ALWAYS === Search and replace: {{{ gst.PAD_SRC => Gst.PadDirection.SRC gst.PAD_SINK => Gst.PadDirection.SINK gst.PAD_ALWAYS => Gst.PadPresence.ALWAYS gst.PAD_REQUEST => Gst.PadPresence.REQUEST gst.PAD_SOMETIMES => Gst.PadPresence.SOMETIMES }}} === URIHandler === With PyGST, you can do something like this to implement the [[http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gstreamer/html/gstreamer-GstUriHandler.html|GstUriHandler interface]] in a custom element: {{{ class DmediaSrc(gst.Bin, gst.URIHandler): @classmethod def do_get_type_full(cls): return gst.URI_SRC @classmethod def do_get_protocols_full(cls): return ['dmedia'] def do_set_uri(self, uri): if not uri.startswith('dmedia://'): return False self.uri = uri return True def do_get_uri(self): return self.uri }}} (Note many details were left out above, see [[http://bazaar.launchpad.net/~jderose/+junk/gst-examples/view/head:/plugin-0.10|plugin-0.10]] for the full working example.) Currently it seems this isn't possible with PyGI + GStreamer 1.0. [[https://bugzilla.gnome.org/show_bug.cgi?id=679181|See bug 679181.]] === decodebin2 === The "decodebin2" element has been renamed to "decodebin", and the old "decodebin" element has been removed. === playbin2 === The "playbin2" element has been renamed to "playbin", and the old "playbin" element has been removed. === ffmpegcolorspace === The "ffmpegcolorspace" element has been replaced with the new "videoconvert" element. === ffmpeg => libav === All the major distros now ship the [[http://libav.org/|libav]] fork of ffmpeg (and current GStreamer 0.10 packages build against libav, not ffmpeg). GStreamer 1.0 was an opportunity to do some renaming to reflect this change. [[http://packages.ubuntu.com/search?keywords=gstreamer1.0-libav|gstreamer1.0-libav]] is the equivalent of the the [[http://packages.ubuntu.com/search?keywords=gstreamer0.10-ffmpeg|gstreamer0.10-ffmpeg]] Debian package. Additionally, the GStreamer elements have been renamed. For example, in 1.0 use '''avdec_h264''' instead of '''ffdec_h264'''. Basically some search and replace: {{{ ffvideoscale => avvideoscale ffdec_* => avdec_* ffenc_* => avenc_* ffmux_* => avmux_* ffdemux_* => avdemux_* }}} To see all the elements shipped in the `gstreamer1.0-libav` package, run: {{{ gst-inspect-1.0 libav }}} The equivalent command for `gstreamer0.10-ffmpeg` is: {{{ gst-inspect-0.10 ffmpeg }}} === gnlfilesource === The "gnlfilesource" element has been removed, so now use the "gnlurisource" element instead. Replace: {{{ src = gst.element_factory_make('gnlfilesource') src.set_property('location', '/my/cool/video') }}} With: {{{ src = Gst.ElementFactory.make('gnlurisource') src.set_property('uri', 'file:///my/cool/video') }}}