Created
November 12, 2019 19:31
-
-
Save jonasl/92c1ef32cfd87047e15f5ae24c6b510e to your computer and use it in GitHub Desktop.
Fullscreen GTK window with GStreamer glimagesink on Wayland
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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