Background
I'm looking for a way to change the window that my video is being rendered into. This is necessary because there are some situations where the window can be destroyed, for example when my application switches into fullscreen mode.
Code
When the canvas is realized, the video source and sink are connected. Then when the prepare-window-handle
message is emitted, I store a reference to the VideoOverlay element that sent it. Clicking the "switch canvas" button calls set_window_handle(new_handle)
on this element, but the video continues to render in the original canvas.
import sys
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gst', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gtk, Gst, GstVideo
Gst.init(None)
if sys.platform == 'win32':
import ctypes
PyCapsule_GetPointer = ctypes.pythonapi.PyCapsule_GetPointer
PyCapsule_GetPointer.restype = ctypes.c_void_p
PyCapsule_GetPointer.argtypes = [ctypes.py_object]
gdkdll = ctypes.CDLL('libgdk-3-0.dll')
gdkdll.gdk_win32_window_get_handle.argtypes = [ctypes.c_void_p]
def get_window_handle(widget):
window = widget.get_window()
if not window.ensure_native():
raise Exception('video playback requires a native window')
window_gpointer = PyCapsule_GetPointer(window.__gpointer__, None)
handle = gdkdll.gdk_win32_window_get_handle(window_gpointer)
return handle
else:
from gi.repository import GdkX11
def get_window_handle(widget):
return widget.get_window().get_xid()
class VideoPlayer:
def __init__(self, canvas):
self._canvas = canvas
self._setup_pipeline()
def _setup_pipeline(self):
# The element with the set_window_handle function will be stored here
self._video_overlay = None
self._pipeline = Gst.ElementFactory.make('pipeline', 'pipeline')
src = Gst.ElementFactory.make('videotestsrc', 'src')
video_convert = Gst.ElementFactory.make('videoconvert', 'videoconvert')
auto_video_sink = Gst.ElementFactory.make('autovideosink', 'autovideosink')
self._pipeline.add(src)
self._pipeline.add(video_convert)
self._pipeline.add(auto_video_sink)
# The source will be linked later, once the canvas has been realized
video_convert.link(auto_video_sink)
self._video_source_pad = src.get_static_pad('src')
self._video_sink_pad = video_convert.get_static_pad('sink')
self._setup_signal_handlers()
def _setup_signal_handlers(self):
self._canvas.connect('realize', self._on_canvas_realize)
bus = self._pipeline.get_bus()
bus.enable_sync_message_emission()
bus.connect('sync-message::element', self._on_sync_element_message)
def _on_sync_element_message(self, bus, message):
if message.get_structure().get_name() == 'prepare-window-handle':
self._video_overlay = message.src
self._video_overlay.set_window_handle(self._canvas_window_handle)
def _on_canvas_realize(self, canvas):
self._canvas_window_handle = get_window_handle(canvas)
self._video_source_pad.link(self._video_sink_pad)
def start(self):
self._pipeline.set_state(Gst.State.PLAYING)
window = Gtk.Window()
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
window.add(vbox)
canvas_box = Gtk.Box()
vbox.add(canvas_box)
canvas1 = Gtk.DrawingArea()
canvas1.set_size_request(400, 400)
canvas_box.add(canvas1)
canvas2 = Gtk.DrawingArea()
canvas2.set_size_request(400, 400)
canvas_box.add(canvas2)
player = VideoPlayer(canvas1)
canvas1.connect('realize', lambda *_: player.start())
def switch_canvas(btn):
handle = get_window_handle(canvas2)
print('Setting handle:', handle)
player._video_overlay.set_window_handle(handle)
btn = Gtk.Button(label='switch canvas')
btn.connect('clicked', switch_canvas)
vbox.add(btn)
window.connect('destroy', Gtk.main_quit)
window.show_all()
Gtk.main()
Problem / Question
Calling set_window_handle()
a 2nd time seems to have no effect - the video continues to render into the original window.
I've tried setting the pipeline into PAUSED, READY, and NULL state before calling set_window_handle()
, but that didn't help.
I've also tried to replace the autovideosink with a new one as seen here, but that doesn't work either.
How can I change the window handle without disrupting the playback too much? Do I have to completely re-create the pipeline?
from How to change a VideoOverlay's window handle after it has already been set?
No comments:
Post a Comment