GstXOverlay 使用 Firefox plugin 中的窗口输出视频

最近编写的 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!

One thought on “GstXOverlay 使用 Firefox plugin 中的窗口输出视频”

  1. 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

Leave a Reply

Your email address will not be published. Required fields are marked *