设置后如何更改视频叠加的窗口句柄?



背景

我正在寻找一种方法来更改正在渲染视频的窗口。这是必要的,因为在某些情况下,窗口可能会被破坏,例如,当我的应用程序切换到全屏模式时。

代码

当画布被实现时,视频源和接收器被连接。然后,当prepare-window-handle消息发出时,我存储对发送它的VideoOverlay元素的引用;切换画布";按钮调用该元素上的set_window_handle(new_handle),但视频继续在原始画布中渲染。

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()

问题/问题

第二次调用set_window_handle()似乎没有效果——视频继续渲染到原始窗口中。

在调用set_window_handle()之前,我曾尝试将管道设置为PAUSED、READY和NULL状态,但这没有帮助。

我也试着用一个新的自动视频接收器来代替它,但这也不起作用。

如何在不过多干扰播放的情况下更改窗口句柄?我必须完全重新创建管道吗?

查看源代码,至少VideoOverlay元素的基于GL的实现会更新expose事件的窗口id。

所以你可以试着打电话:

player._video_overlay.expose()

以在窗口句柄已经改变之后重新初始化GL场景。

如果这不起作用,您可以创建一个新的VideoOverlay元素,并在不停止图形的情况下动态添加它。

最新更新