【问题标题】:Playing video in Gtk in a window with a menubar在带有菜单栏的窗口中播放 Gtk 中的视频
【发布时间】:2016-10-17 04:56:23
【问题描述】:

我在 Gtk3 中使用 Python3 中的 Gstreamer 创建了一个视频播放器。除非我添加 GtkMenuBar(位置 2),否则它可以工作。然后它将显示黑屏,或因异常而失败。异常引用了我正在调用的XInitThreads(位置 1)(我从 pitivi 项目中获取了这个),但这似乎没有什么不同。

问题:我该如何进行这项工作?

我想知道的其他事情:

  1. 为什么菜单栏会破坏这个?
  2. 这显然会在 X 之外的任何东西上中断,是否有一些预构建的组件抽象了这个逻辑并且是我缺少的跨平台?

系统:

  • python3
  • Gtk3
  • Ubuntu 16.04

例外:

[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python3: ../../src/xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.

代码(以尽可能小的形式展示概念):

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, xlib
from gi.repository import Gst, Gdk, GdkX11, GstVideo
Gst.init(None)
Gst.init_check(None)

# Place 1
from ctypes import cdll
x11 = cdll.LoadLibrary('libX11.so')
x11.XInitThreads()

# [xcb] Unknown request in queue while dequeuing
# [xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
# [xcb] Aborting, sorry about that.
# python3: ../../src/xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.

# (foo.py:31933): Gdk-WARNING **: foo.py: Fatal IO error 11 (Resource temporarily unavailable) on X server :1.

class PipelineManager(object):
    def __init__(self, window, pipeline):
        self.window = window
        if isinstance(pipeline, str):
            pipeline = Gst.parse_launch(pipeline)

        self.pipeline = pipeline

        bus = pipeline.get_bus()
        bus.set_sync_handler(self.bus_callback)
        pipeline.set_state(Gst.State.PLAYING)

    def bus_callback(self, bus, message):
        if message.type is Gst.MessageType.ELEMENT:
            if GstVideo.is_video_overlay_prepare_window_handle_message(message):
                Gdk.threads_enter()
                Gdk.Display.get_default().sync()
                win = self.window.get_property('window')

                if isinstance(win, GdkX11.X11Window):
                    message.src.set_window_handle(win.get_xid())
                else:
                    print('Nope')

                Gdk.threads_leave()
        return Gst.BusSyncReply.PASS


pipeline = Gst.parse_launch('videotestsrc ! xvimagesink sync=false')

window = Gtk.ApplicationWindow()

header_bar = Gtk.HeaderBar()
header_bar.set_show_close_button(True)
# window.set_titlebar(header_bar)  # Place 2

drawing_area = Gtk.DrawingArea()
drawing_area.connect('realize', lambda widget: PipelineManager(widget, pipeline))
window.add(drawing_area)

window.show_all()

def on_destroy(win):
    try:
        Gtk.main_quit()
    except KeyboardInterrupt:
        pass

window.connect('destroy', on_destroy)

Gtk.main()

【问题讨论】:

  • 我无法回答这个问题,但这是一个极好的问题。切中要害,一个具体的问题,而不是简单的文档,提供一个完美的 MCVE,显示错误信息、系统信息、代码用途等。这应该是一个如何提问的例子。

标签: python gstreamer gtk3 xlib


【解决方案1】:

在搜索有关单独问题的文档时,我发现了对 gtksink 小部件的引用。这似乎是将视频放在 gtk 窗口中的正确方法,但不幸的是,没有一个教程使用它。

使用gtksink 小部件可以解决所有问题并大大降低代码复杂性。

修改后的代码:

from pprint import pprint

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
Gst.init(None)
Gst.init_check(None)


class GstWidget(Gtk.Box):
    def __init__(self, pipeline):
        super().__init__()
        self.connect('realize', self._on_realize)
        self._bin = Gst.parse_bin_from_description('videotestsrc', True)

    def _on_realize(self, widget):
        pipeline = Gst.Pipeline()
        factory = pipeline.get_factory()
        gtksink = factory.make('gtksink')
        pipeline.add(gtksink)
        pipeline.add(self._bin)
        self._bin.link(gtksink)
        self.pack_start(gtksink.props.widget, True, True, 0)
        gtksink.props.widget.show()
        pipeline.set_state(Gst.State.PLAYING)


window = Gtk.ApplicationWindow()

header_bar = Gtk.HeaderBar()
header_bar.set_show_close_button(True)
window.set_titlebar(header_bar)  # Place 2

widget = GstWidget('videotestsrc')
widget.set_size_request(200, 200)

window.add(widget)

window.show_all()

def on_destroy(win):
    try:
        Gtk.main_quit()
    except KeyboardInterrupt:
        pass

window.connect('destroy', on_destroy)

Gtk.main()

【讨论】:

  • 我可能选择了与您完全相同的路径,只是我不必写这篇文章!为我省去了很多麻烦。我绝对同意 gtksink 似乎更合适 - 在这种情况下 - 风格和功能明智。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-01
  • 1970-01-01
  • 2014-09-30
  • 1970-01-01
  • 2016-02-02
相关资源
最近更新 更多