2016-10-17 4 views
5

Я создал видеопроигрыватель в Gtk3 с помощью Gstreamer в Python3. Он работает, за исключением случаев, когда я добавляю GtkMenuBar (место 2). Затем он либо покажет черный экран, либо сбой с ошибкой. Исключение ссылается на XInitThreads, который я называю (Место 1) (я взял это из проекта pitivi), но это, похоже, не делает различий.Воспроизведение видео в Gtk в окне с помощью строки меню

Вопрос: Как мне сделать эту работу?

Другие вещи, которые я хотел бы знать:

  1. Почему бы MenuBar ломают это?
  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. 

Код (в качестве небольшой форме, как поз sible продемонстрировать концепцию):

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

Я не могу ответить на этот вопрос, но это превосходный вопрос. По какой-то теме, конкретная проблема, а не что-то простое в документации, обеспечивает идеальный MCVE, показывает сообщение об ошибке, системную информацию, цель кода и т. Д. Это должен быть пример того, как задавать вопрос. – oldtechaa

ответ

2

При поиске по документации по отдельному вопросу, я наткнулся на ссылку на 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()