最近编写的 Firefox plugin 需要使用 GstXOverlay 接口在插件中创建的窗口里输出视频图像,使用 PluginContainer 方式运行总是出现如下错
###!!! ABORT: X_GetWindowAttributes: BadWindow (invalid Window parameter); |
而关闭了 IPC 直接在 Firefox 主进程地址空间中执行 Plugin 则是正常的,进一步调试发现是由于为 GstXOverlay 接口操作的对象设置的窗口时机不对导致的。
起初是在输出窗口 “realize” 的时候将其设置为输出窗口,这在独立进程的测试程序或关闭 IPC 的情况下都可以正常工作。代码如下
... static void drawing_area_realize_handler(GtkWidget *widget, gpointer user_data) { HevPluginPrivate *priv = (HevPluginPrivate*)user_data; XID xid = 0; g_debug("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__); xid = gdk_x11_drawable_get_xid(GDK_DRAWABLE(gtk_widget_get_window(widget))); gst_x_overlay_set_window_handle(GST_X_OVERLAY(priv->xvimagesink), xid); gst_element_set_state(priv->pipeline, GST_STATE_PLAYING); } NPError NPP_SetWindow(NPP instance, NPWindow *window) { HevPluginPrivate *priv = (HevPluginPrivate*)instance->pdata; GtkWidget *plug = NULL; GtkWidget *drawing_area = NULL; g_debug("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__); g_return_val_if_fail(instance, NPERR_INVALID_INSTANCE_ERROR); /* Check re-enter */ if(priv->window == window) return NPERR_NO_ERROR; priv->window = window; plug = gtk_plug_new((GdkNativeWindow)window->window); gtk_widget_show(plug); drawing_area = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(drawing_area), "realize", G_CALLBACK(drawing_area_realize_handler), priv); gtk_container_add(GTK_CONTAINER(plug), drawing_area); gtk_widget_show(drawing_area); return NPERR_NO_ERROR; } ... |
测试总结后发现主要原因是 realize 的窗口并不一定是 mapped,而 GstXOverlay 所需要的视频回放窗口必需是 mapped 的,在不使用 IPC 和独立进程模式的情况下,realized 和 mapped 时间间隔非常的短,所以不影响,而在使用多进程的通过 IPC 交互的模式下,map 过程会被滞后,从而导致问题。正确的设置回放窗口的时机是 map-event 信号触发的时候。 示例代码如下
... static gboolean drawing_area_map_event_handler(GtkWidget *widget, GdkEvent *event, gpointer user_data) { HevPluginPrivate *priv = (HevPluginPrivate*)user_data; XID xid = 0; g_debug("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__); xid = gdk_x11_drawable_get_xid(GDK_DRAWABLE(gtk_widget_get_window(widget))); gst_x_overlay_set_window_handle(GST_X_OVERLAY(priv->xvimagesink), xid); gst_element_set_state(priv->pipeline, GST_STATE_PLAYING); return FALSE; } NPError NPP_SetWindow(NPP instance, NPWindow *window) { HevPluginPrivate *priv = (HevPluginPrivate*)instance->pdata; GtkWidget *plug = NULL; GtkWidget *drawing_area = NULL; g_debug("%s:%d[%s]", __FILE__, __LINE__, __FUNCTION__); g_return_val_if_fail(instance, NPERR_INVALID_INSTANCE_ERROR); /* Check re-enter */ if(priv->window == window) return NPERR_NO_ERROR; priv->window = window; plug = gtk_plug_new((GdkNativeWindow)window->window); gtk_widget_show(plug); drawing_area = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(drawing_area), "map-event", G_CALLBACK(drawing_area_map_event_handler), priv); gtk_container_add(GTK_CONTAINER(plug), drawing_area); gtk_widget_show(drawing_area); return NPERR_NO_ERROR; } ... |
Over!
You can just wait for the xid != 0 and then set_xid in player thread.
As a plugin maybe it is a better way.
My project was writing a h/w acceleration player plugin for a webkit browser.
which was finished 2 months ago.
Welcome more exchanges.
Regards!
forest
My msn :forest0823@126.com