Mercurial > pidgin.yaz
changeset 26135:c334a9e17426
Wait to create the sinks until the frames are ready.
This should hopefully be the end of the BadWindow errors.
author | Mike Ruprecht <maiku@soc.pidgin.im> |
---|---|
date | Mon, 19 Jan 2009 10:44:07 +0000 |
parents | 997bbae7b80f |
children | d68924f1265f |
files | libpurple/media.c libpurple/media.h pidgin/gtkmedia.c |
diffstat | 3 files changed, 290 insertions(+), 50 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/media.c Sat Jan 17 04:36:40 2009 +0000 +++ b/libpurple/media.c Mon Jan 19 10:44:07 2009 +0000 @@ -39,6 +39,7 @@ #ifdef USE_VV #include <gst/interfaces/propertyprobe.h> +#include <gst/interfaces/xoverlay.h> #include <gst/farsight/fs-conference-iface.h> /** @copydoc _PurpleMediaSession */ @@ -57,6 +58,9 @@ gboolean codecs_ready; gboolean accepted; + + GstElement *sink; + gulong window_id; }; struct _PurpleMediaStream @@ -73,6 +77,8 @@ FsCandidate *local_candidate; FsCandidate *remote_candidate; + + gulong window_id; }; struct _PurpleMediaPrivate @@ -1365,7 +1371,7 @@ void purple_media_video_init_src(GstElement **sendbin) { - GstElement *src, *tee, *queue, *local_sink; + GstElement *src, *tee, *queue; GstPad *pad; GstPad *ghost; const gchar *video_plugin = purple_prefs_get_string( @@ -1395,14 +1401,6 @@ gst_object_unref(pad); gst_element_add_pad(*sendbin, ghost); - queue = gst_element_factory_make("queue", "purplelocalvideoqueue"); - gst_bin_add(GST_BIN(*sendbin), queue); - gst_element_link(tee, queue); - - local_sink = gst_element_factory_make("autovideosink", "purplelocalvideosink"); - gst_bin_add(GST_BIN(*sendbin), local_sink); - gst_element_link(queue, local_sink); - if (video_device != NULL && strcmp(video_device, "")) g_object_set(G_OBJECT(src), "device", video_device, NULL); } @@ -1436,8 +1434,8 @@ GstElement *sink; GstPad *pad, *ghost; - *recvbin = gst_bin_new("pidginrecvvideobin"); - sink = gst_element_factory_make("autovideosink", "purplevideosink"); + *recvbin = gst_bin_new("fakebin"); + sink = gst_element_factory_make("fakesink", NULL); gst_bin_add(GST_BIN(*recvbin), sink); pad = gst_element_get_pad(sink, "sink"); ghost = gst_ghost_pad_new("ghostsink", pad); @@ -1526,7 +1524,8 @@ PurpleMediaSessionType type = purple_media_from_fs(codec->media_type, FS_DIRECTION_RECV); GstPad *sinkpad = NULL; - stream->sink = purple_media_manager_get_element( + if (stream->sink == NULL) + stream->sink = purple_media_manager_get_element( purple_media_manager_get(), type); gst_bin_add(GST_BIN(purple_media_get_pipeline(stream->session->media)), @@ -1890,4 +1889,237 @@ } } +typedef struct +{ + PurpleMedia *media; + gchar *session_id; + gchar *participant; + gulong handler_id; +} PurpleMediaXOverlayData; + +static void +window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaXOverlayData *data) +{ + gchar *name; + gchar *cmpname; + gulong window_id; + gboolean set = FALSE; + + if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT || + !gst_structure_has_name(msg->structure, + "prepare-xwindow-id")) + return; + + name = gst_object_get_name(GST_MESSAGE_SRC(msg)); + + if (data->participant == NULL) { + cmpname = g_strdup_printf("session-sink_%s", + data->session_id); + if (!strncmp(name, cmpname, strlen(cmpname))) { + PurpleMediaSession *session = + purple_media_get_session( + data->media, data->session_id); + if (session != NULL) { + window_id = session->window_id; + set = TRUE; + } + } + } else { + cmpname = g_strdup_printf("stream-sink_%s_%s", + data->session_id, data->participant); + + if (!strncmp(name, cmpname, strlen(cmpname))) { + PurpleMediaStream *stream = + purple_media_get_stream( + data->media, data->session_id, + data->participant); + if (stream != NULL) { + window_id = stream->window_id; + set = TRUE; + } + } + } + g_free(cmpname); + g_free(name); + + if (set == TRUE) { + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE( + purple_media_get_pipeline(data->media))); + g_signal_handler_disconnect(bus, data->handler_id); + gst_object_unref(bus); + + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY( + GST_MESSAGE_SRC(msg)), window_id); + + g_free(data->session_id); + g_free(data->participant); + g_free(data); + } + + return; +} + +gboolean +purple_media_set_output_window(PurpleMedia *media, const gchar *session_id, + const gchar *participant, gulong window_id) +{ + + if (session_id != NULL && participant == NULL) { + + PurpleMediaSession *session; + session = purple_media_get_session(media, session_id); + + if (session == NULL) + return FALSE; + + session->window_id = window_id; + + if (session->sink == NULL) { + GstBus *bus; + GstElement *tee, *bin, *queue, *sink; + GstPad *request_pad, *sinkpad, *ghostpad; + gchar *name; + + /* Create callback for prepared-xwindow-id signal */ + PurpleMediaXOverlayData *data = + g_new0(PurpleMediaXOverlayData, 1); + data->media = media; + data->session_id = g_strdup(session_id); + data->participant = g_strdup(participant); + + bus = gst_pipeline_get_bus(GST_PIPELINE( + purple_media_get_pipeline(media))); + data->handler_id = g_signal_connect(bus, + "sync-message::element", + G_CALLBACK(window_id_cb), data); + gst_object_unref(bus); + + /* Create sink */ + tee = gst_bin_get_by_name(GST_BIN(session->src), + "purplevideosrctee"); + bin = gst_bin_new(NULL); + gst_bin_add(GST_BIN(GST_ELEMENT_PARENT(tee)), bin); + + queue = gst_element_factory_make("queue", NULL); + name = g_strdup_printf( + "session-sink_%s", session_id); + sink = gst_element_factory_make( + "autovideosink", name); + g_free(name); + + gst_bin_add_many(GST_BIN(bin), queue, sink, NULL); + gst_element_link(queue, sink); + + sinkpad = gst_element_get_static_pad(queue, "sink"); + ghostpad = gst_ghost_pad_new("ghostsink", sinkpad); + gst_object_unref(sinkpad); + gst_element_add_pad(bin, ghostpad); + + gst_element_set_state(bin, GST_STATE_PLAYING); + + request_pad = gst_element_get_request_pad( + tee, "src%d"); + gst_pad_link(request_pad, ghostpad); + gst_object_unref(request_pad); + + session->sink = bin; + return TRUE; + } else { + /* Changing the XOverlay output window */ + GstElement *xoverlay = gst_bin_get_by_interface( + GST_BIN(session->sink), + GST_TYPE_X_OVERLAY); + if (xoverlay != NULL) { + gst_x_overlay_set_xwindow_id( + GST_X_OVERLAY(xoverlay), + window_id); + } + return FALSE; + } + } else if (session_id != NULL && participant != NULL) { + PurpleMediaStream *stream = purple_media_get_stream( + media, session_id, participant); + GstBus *bus; + GstElement *bin, *sink; + GstPad *pad, *peer = NULL, *ghostpad; + PurpleMediaXOverlayData *data; + gchar *name; + + if (stream == NULL) + return FALSE; + + stream->window_id = window_id; + + if (stream->sink != NULL) { + gboolean is_fakebin; + name = gst_element_get_name(stream->sink); + is_fakebin = !strcmp(name, "fakebin"); + g_free(name); + + if (is_fakebin) { + pad = gst_element_get_static_pad( + stream->sink, "ghostsink"); + peer = gst_pad_get_peer(pad); + + gst_pad_unlink(peer, pad); + gst_object_unref(pad); + gst_element_set_state(stream->sink, + GST_STATE_NULL); + gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT( + stream->sink)), stream->sink); + } else { + /* Changing the XOverlay output window */ + GstElement *xoverlay = + gst_bin_get_by_interface( + GST_BIN(stream->sink), + GST_TYPE_X_OVERLAY); + if (xoverlay != NULL) { + gst_x_overlay_set_xwindow_id( + GST_X_OVERLAY(xoverlay), + window_id); + return TRUE; + } + return FALSE; + } + } + + data = g_new0(PurpleMediaXOverlayData, 1); + data->media = media; + data->session_id = g_strdup(session_id); + data->participant = g_strdup(participant); + + bus = gst_pipeline_get_bus(GST_PIPELINE( + purple_media_get_pipeline(media))); + data->handler_id = g_signal_connect(bus, + "sync-message::element", + G_CALLBACK(window_id_cb), data); + gst_object_unref(bus); + + bin = gst_bin_new(NULL); + + if (stream->sink != NULL) + gst_bin_add(GST_BIN(GST_ELEMENT_PARENT( + stream->sink)), bin); + + name = g_strdup_printf("stream-sink_%s_%s", + session_id, participant); + sink = gst_element_factory_make("autovideosink", name); + g_free(name); + + gst_bin_add(GST_BIN(bin), sink); + pad = gst_element_get_static_pad(sink, "sink"); + ghostpad = gst_ghost_pad_new("ghostsink", pad); + gst_element_add_pad(bin, ghostpad); + + if (stream->sink != NULL) { + gst_element_set_state(bin, GST_STATE_PLAYING); + gst_pad_link(peer, ghostpad); + } + + stream->sink = bin; + return TRUE; + } + return FALSE; +} + #endif /* USE_VV */
--- a/libpurple/media.h Sat Jan 17 04:36:40 2009 +0000 +++ b/libpurple/media.h Mon Jan 19 10:44:07 2009 +0000 @@ -624,6 +624,10 @@ void purple_media_set_output_volume(PurpleMedia *media, const gchar *session_id, const gchar *participant, double level); +gboolean purple_media_set_output_window(PurpleMedia *media, + const gchar *session_id, const gchar *participant, + gulong window_id); + #ifdef __cplusplus } #endif
--- a/pidgin/gtkmedia.c Sat Jan 17 04:36:40 2009 +0000 +++ b/pidgin/gtkmedia.c Mon Jan 19 10:44:07 2009 +0000 @@ -318,42 +318,37 @@ g_signal_emit(gtkmedia, pidgin_media_signals[MESSAGE], 0, msg); } -static gboolean -create_window (GstBus *bus, GstMessage *message, PidginMedia *gtkmedia) +typedef struct { - char *name; - - if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT) - return TRUE; - - if (!gst_structure_has_name(message->structure, "prepare-xwindow-id")) - return TRUE; - - name = gst_object_get_name(GST_MESSAGE_SRC (message)); - - /* The XOverlay's name is the sink's name with a suffix */ - if (!strncmp(name, "purplevideosink", strlen("purplevideosink"))) - gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC(message)), - GDK_WINDOW_XWINDOW(gtkmedia->priv->remote_video->window)); - else if (!strncmp(name, "purplelocalvideosink", strlen("purplelocalvideosink"))) - gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC(message)), - GDK_WINDOW_XWINDOW(gtkmedia->priv->local_video->window)); - g_free(name); - return TRUE; -} + PidginMedia *gtkmedia; + gchar *session_id; + gchar *participant; +} PidginMediaRealizeData; static gboolean -realize_cb_cb(GstElement *element) +realize_cb_cb(PidginMediaRealizeData *data) { - gst_element_set_locked_state(element, FALSE); - gst_element_set_state(element, GST_STATE_PLAYING); + PidginMediaPrivate *priv = data->gtkmedia->priv; + gulong window_id; + + if (data->participant == NULL) + window_id = GDK_WINDOW_XWINDOW(priv->local_video->window); + else + window_id = GDK_WINDOW_XWINDOW(priv->remote_video->window); + + purple_media_set_output_window(priv->media, data->session_id, + data->participant, window_id); + + g_free(data->session_id); + g_free(data->participant); + g_free(data); return FALSE; } static void -realize_cb(GtkWidget *widget, GstElement *element) +realize_cb(GtkWidget *widget, PidginMediaRealizeData *data) { - g_timeout_add(0, (GSourceFunc)realize_cb_cb, element); + g_timeout_add(0, (GSourceFunc)realize_cb_cb, data); } static void @@ -390,7 +385,6 @@ GstElement *videosendbin = NULL; gboolean audiorecvbool = FALSE; gboolean videorecvbool = FALSE; - GstBus *bus; gboolean is_initiator; PurpleMediaSessionType type = purple_media_get_session_type(media, sid); @@ -406,8 +400,8 @@ } else if (type & PURPLE_MEDIA_VIDEO) { if (!videosendbin && (type & PURPLE_MEDIA_SEND_VIDEO)) { purple_media_video_init_src(&videosendbin); - gst_element_set_locked_state(videosendbin, TRUE); purple_media_set_src(media, sid, videosendbin); + gst_element_set_state(videosendbin, GST_STATE_PLAYING); } if (!videorecvbool && (type & PURPLE_MEDIA_RECV_VIDEO)) { videorecvbool = TRUE; @@ -432,6 +426,7 @@ send_widget = gtkmedia->priv->send_widget; if (videorecvbool) { + PidginMediaRealizeData *data; GtkWidget *aspect; GtkWidget *remote_video; GtkWidget *plug; @@ -454,7 +449,14 @@ gtk_container_add(GTK_CONTAINER(aspect), socket); gtk_widget_show(socket); + data = g_new0(PidginMediaRealizeData, 1); + data->gtkmedia = gtkmedia; + data->session_id = g_strdup(sid); + data->participant = g_strdup(gtkmedia->priv->screenname); + remote_video = gtk_drawing_area_new(); + g_signal_connect(G_OBJECT(remote_video), "realize", + G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(plug), remote_video); gtk_widget_set_size_request (GTK_WIDGET(remote_video), 100, -1); gtk_widget_show(remote_video); @@ -463,6 +465,7 @@ gtkmedia->priv->remote_video = remote_video; } if (videosendbin) { + PidginMediaRealizeData *data; GtkWidget *aspect; GtkWidget *local_video; GtkWidget *plug; @@ -485,9 +488,14 @@ gtk_container_add(GTK_CONTAINER(aspect), socket); gtk_widget_show(socket); + data = g_new0(PidginMediaRealizeData, 1); + data->gtkmedia = gtkmedia; + data->session_id = g_strdup(sid); + data->participant = NULL; + local_video = gtk_drawing_area_new(); g_signal_connect(G_OBJECT(local_video), "realize", - G_CALLBACK(realize_cb), videosendbin); + G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(plug), local_video); gtk_widget_set_size_request (GTK_WIDGET(local_video), 100, -1); @@ -518,17 +526,13 @@ gtk_widget_show(gtkmedia->priv->mute); } - bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); - if (videorecvbool || videosendbin) - g_signal_connect(bus, "sync-message::element", - G_CALLBACK(create_window), gtkmedia); - - if (audiorecvbool || audiosendbin) + if (audiorecvbool || audiosendbin) { + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); g_signal_connect(G_OBJECT(bus), "message::element", G_CALLBACK(level_message_cb), gtkmedia); - - gst_object_unref(bus); + gst_object_unref(bus); + } if (send_widget != NULL) gtkmedia->priv->send_widget = send_widget;