Skip to content

Instantly share code, notes, and snippets.

@jonasl
Created November 12, 2019 19:31
Show Gist options
  • Save jonasl/92c1ef32cfd87047e15f5ae24c6b510e to your computer and use it in GitHub Desktop.
Save jonasl/92c1ef32cfd87047e15f5ae24c6b510e to your computer and use it in GitHub Desktop.
Fullscreen GTK window with GStreamer glimagesink on Wayland
import gi
gi.require_version('Gdk', '3.0')
gi.require_version('GObject', '2.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstGL', '1.0')
gi.require_version('GstVideo', '1.0')
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk, GObject, Gst, GstGL, GstVideo, Gtk
Gst.init(None)
# There currently are no Python bindings for GTK/Wayland
# so instead use ctypes.
import ctypes
from ctypes.util import find_library
libgdk = ctypes.CDLL(find_library('libgdk-3'))
libgdk.gdk_wayland_window_get_wl_surface.restype = ctypes.c_void_p
libgdk.gdk_wayland_window_get_wl_surface.argtypes = [ctypes.c_void_p]
libgdk.gdk_wayland_display_get_wl_display.restype = ctypes.c_void_p
libgdk.gdk_wayland_display_get_wl_display.argtypes = [ctypes.c_void_p]
libgst = ctypes.CDLL(find_library('libgstreamer-1.0'))
libgst.gst_context_writable_structure.restype = ctypes.c_void_p
libgst.gst_context_writable_structure.argtypes = [ctypes.c_void_p]
libgst.gst_structure_set.restype = ctypes.c_void_p
libgst.gst_structure_set.argtypes = [ctypes.c_void_p, ctypes.c_char_p,
ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
def get_wayland_window_handle(widget):
return libgdk.gdk_wayland_window_get_wl_surface(hash(widget.get_window()))
def get_default_wayland_display_context():
wl_display = libgdk.gdk_wayland_display_get_wl_display(hash(Gdk.Display.get_default()))
context = Gst.Context.new('GstWaylandDisplayHandleContextType', True)
structure = libgst.gst_context_writable_structure(hash(context))
libgst.gst_structure_set(structure, ctypes.c_char_p('display'.encode()),
hash(GObject.TYPE_POINTER), wl_display, 0)
return context
def setup_window(pipeline):
glsink = pipeline.get_by_name('glsink')
# Needed to commit the wayland sub-surface.
def on_gl_draw(sink, context, sample, widget):
widget.queue_draw()
return False
# Needed to account for window chrome etc.
def on_widget_configure(widget, event, glsink):
allocation = widget.get_allocation()
glsink.set_render_rectangle(allocation.x, allocation.y,
allocation.width, allocation.height)
return False
window = Gtk.Window()
window.fullscreen()
drawing_area = Gtk.DrawingArea()
window.add(drawing_area)
drawing_area.realize()
glsink.connect('client-draw', on_gl_draw, drawing_area)
# Wayland window handle.
wl_handle = get_wayland_window_handle(drawing_area)
glsink.set_window_handle(wl_handle)
# Wayland display context wrapped as a GStreamer context.
wl_display = get_default_wayland_display_context()
glsink.set_context(wl_display)
drawing_area.connect('configure-event', on_widget_configure, glsink)
window.connect('delete-event', Gtk.main_quit)
window.show_all()
# Needed only in pipelines with multiple branches with GL enabled
# elements in each branch. In that case we must ensure that the same
# GL display and context is used in all branches.
# This isn't automatically handled by GStreamer when setting an
# external display handle.
# The sample pipeline in this example doesn't need this, but it's
# included for completeness.
def on_bus_message_sync(bus, message, glsink):
if message.type == Gst.MessageType.NEED_CONTEXT:
_, context_type = message.parse_context_type()
if context_type == GstGL.GL_DISPLAY_CONTEXT_TYPE:
sinkelement = glsink.get_by_interface(GstVideo.VideoOverlay)
gl_context = sinkelement.get_property('context')
if gl_context:
display_context = Gst.Context.new(GstGL.GL_DISPLAY_CONTEXT_TYPE, True)
GstGL.context_set_gl_display(display_context, gl_context.get_display())
message.src.set_context(display_context)
return Gst.BusSyncReply.PASS
bus = pipeline.get_bus()
bus.set_sync_handler(on_bus_message_sync, glsink)
def main():
pipeline = Gst.parse_launch('videotestsrc ! glimagesink name=glsink')
setup_window(pipeline)
try:
print('Setting pipeline to PLAYING')
pipeline.set_state(Gst.State.PLAYING)
Gtk.main()
except:
pass
print('Setting pipeline to READY')
pipeline.set_state(Gst.State.READY)
pipeline.get_state(Gst.CLOCK_TIME_NONE)
print('Setting pipeline to NULL')
pipeline.set_state(Gst.State.NULL)
if __name__== "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment