# HG changeset patch # User Mike Ruprecht # Date 1235463084 0 # Node ID 6cf36f68033c992c89c6cd2b64f81adb0812ccd1 # Parent cb3c6ec9291e51b99b4ebbd61d40a1be3b814398 Only create output windows once there's data to be output. Also, allow multiple output windows per session/stream. diff -r cb3c6ec9291e -r 6cf36f68033c libpurple/media.c --- a/libpurple/media.c Sat Feb 21 05:15:28 2009 +0000 +++ b/libpurple/media.c Tue Feb 24 08:11:24 2009 +0000 @@ -39,7 +39,6 @@ #ifdef USE_VV #include -#include #include /** @copydoc _PurpleMediaSession */ @@ -1591,7 +1590,7 @@ *recvbin = gst_bin_new("pidginrecvaudiobin"); sink = gst_element_factory_make("alsasink", "asink"); - g_object_set(G_OBJECT(sink), "sync", FALSE, NULL); + g_object_set(G_OBJECT(sink), "async", FALSE, "sync", FALSE, NULL); volume = gst_element_factory_make("volume", "purpleaudiooutputvolume"); g_object_set(volume, "volume", output_volume, NULL); *recvlevel = gst_element_factory_make("level", "recvlevel"); @@ -1610,17 +1609,9 @@ void purple_media_video_init_recv(GstElement **recvbin) { - GstElement *sink; - GstPad *pad, *ghost; - g_return_if_fail(recvbin != NULL); - *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); - gst_element_add_pad(*recvbin, ghost); + *recvbin = gst_element_factory_make("autovideosink", NULL); } static void @@ -1736,6 +1727,12 @@ purple_media_connected_cb(PurpleMediaStream *stream) { g_return_val_if_fail(stream != NULL, FALSE); + + purple_media_manager_create_output_window( + stream->session->media->priv->manager, + stream->session->media, + stream->session->id, stream->participant); + g_signal_emit(stream->session->media, purple_media_signals[STATE_CHANGED], 0, PURPLE_MEDIA_STATE_CHANGED_CONNECTED, @@ -1884,6 +1881,11 @@ purple_media_manager_get_element( media->priv->manager, session_type)); gst_element_set_state(session->src, GST_STATE_PLAYING); + + purple_media_manager_create_output_window( + media->priv->manager, + session->media, + session->id, NULL); } if (!(participant = purple_media_add_participant(media, who))) { @@ -2303,293 +2305,14 @@ } } -typedef struct -{ - gchar *name; - gulong window_id; - gulong handler_id; -} PurpleMediaXOverlayData; - -static void -window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaXOverlayData *data) -{ - gchar *name; - - 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 (!strncmp(name, data->name, strlen(data->name))) { - g_signal_handler_disconnect(bus, data->handler_id); - - gst_x_overlay_set_xwindow_id(GST_X_OVERLAY( - GST_MESSAGE_SRC(msg)), data->window_id); - - g_free(data->name); - g_free(data); - } - - g_free(name); - - return; -} - -gboolean +gulong purple_media_set_output_window(PurpleMedia *media, const gchar *session_id, const gchar *participant, gulong window_id) { g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); - 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) { - PurpleMediaXOverlayData *data; - GstBus *bus; - GstElement *bin, *queue, *sink; - GstPad *request_pad, *sinkpad, *ghostpad; - gchar *name; - - /* Create sink */ - bin = gst_bin_new(NULL); - gst_bin_add(GST_BIN(GST_ELEMENT_PARENT( - session->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); - - 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); - - /* Connect callback for prepared-xwindow-id signal */ - data = g_new0(PurpleMediaXOverlayData, 1); - data->name = name; - data->window_id = window_id; - - bus = gst_pipeline_get_bus(GST_PIPELINE( - purple_media_manager_get_pipeline( - media->priv->manager))); - data->handler_id = g_signal_connect(bus, - "sync-message::element", - G_CALLBACK(window_id_cb), data); - gst_object_unref(bus); - - gst_element_set_state(bin, GST_STATE_PLAYING); - - request_pad = gst_element_get_request_pad( - session->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, *queue, *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; - } - } - - bin = gst_bin_new(NULL); - - name = g_strdup_printf("stream-sink_%s_%s", - session_id, participant); - queue = gst_element_factory_make("queue", NULL); - sink = gst_element_factory_make("autovideosink", name); - - gst_bin_add_many(GST_BIN(bin), queue, sink, NULL); - gst_element_link(queue, sink); - pad = gst_element_get_static_pad(queue, "sink"); - ghostpad = gst_ghost_pad_new("ghostsink", pad); - gst_element_add_pad(bin, ghostpad); - - /* Connect callback for prepared-xwindow-id signal */ - data = g_new0(PurpleMediaXOverlayData, 1); - data->name = name; - data->window_id = window_id; - - bus = gst_pipeline_get_bus(GST_PIPELINE( - purple_media_manager_get_pipeline( - media->priv->manager))); - data->handler_id = g_signal_connect(bus, - "sync-message::element", - G_CALLBACK(window_id_cb), data); - gst_object_unref(bus); - - if (stream->tee != NULL) { - gst_bin_add(GST_BIN(GST_ELEMENT_PARENT( - stream->tee)), bin); - gst_element_set_state(bin, GST_STATE_PLAYING); - gst_element_link(stream->tee, bin); - } - - stream->sink = bin; - return TRUE; - } - return FALSE; -} - -static void -dummy_block_cb(GstPad *pad, gboolean blocked, gpointer user_data) -{ -} - -gboolean -purple_media_remove_output_window(PurpleMedia *media, const gchar *session_id, - const gchar *participant) -{ - GstElement *parent, *fakesink, *sink; - GstPad *pad, *peer; - - g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); - - if (session_id != NULL && participant == NULL) { - PurpleMediaSession *session; - GstPad *pad, *peer; - - session = purple_media_get_session(media, session_id); - - if (session == NULL) - return FALSE; - - sink = session->sink; - - if (!GST_IS_ELEMENT(sink)) - return FALSE; - - pad = gst_element_get_static_pad(sink, "ghostsink"); - peer = gst_pad_get_peer(pad); - gst_object_unref(pad); - - gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer); - gst_object_unref(peer); - - gst_element_set_locked_state(sink, TRUE); - gst_element_set_state(sink, GST_STATE_NULL); - - gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(sink)), sink); - session->sink = NULL; - return TRUE; - } else if (session_id != NULL && participant != NULL) { - PurpleMediaStream *stream; - stream = purple_media_get_stream(media, - session_id, participant); - - if (stream == NULL) - return FALSE; - - sink = stream->sink; - } else - return FALSE; - - if (!GST_IS_ELEMENT(sink)) - return FALSE; - - /* Remove sink */ - parent = GST_ELEMENT(gst_element_get_parent(sink)); - - if (parent == NULL) { - /* It's not added and therefore not linked */ - gst_object_unref(sink); - return FALSE; - } - - pad = gst_element_get_static_pad(sink, "ghostsink"); - - if (pad == NULL) { - /* It's already a fakesink */ - gst_object_unref(parent); - return FALSE; - } - - peer = gst_pad_get_peer(pad); - gst_object_unref(pad); - gst_pad_set_blocked_async(peer, TRUE, dummy_block_cb, NULL); - gst_element_set_locked_state(sink, TRUE); - gst_element_set_state(sink, GST_STATE_NULL); - gst_bin_remove(GST_BIN(parent), sink); - - /* Add fakesink */ - fakesink = gst_element_factory_make("fakesink", NULL); - gst_bin_add(GST_BIN(parent), fakesink); - gst_element_sync_state_with_parent(fakesink); - gst_object_unref(parent); - pad = gst_element_get_static_pad(fakesink, "sink"); - gst_pad_link(peer, pad); - gst_object_unref(pad); - gst_pad_set_blocked_async(peer, FALSE, dummy_block_cb, NULL); - gst_object_unref(peer); - return TRUE; + return purple_media_manager_set_output_window(media->priv->manager, + media, session_id, participant, window_id); } void @@ -2598,15 +2321,37 @@ GList *iter = media->priv->streams; for (; iter; iter = g_list_next(iter)) { PurpleMediaStream *stream = iter->data; - purple_media_remove_output_window(media, + purple_media_manager_remove_output_windows( + media->priv->manager, media, stream->session->id, stream->participant); } iter = purple_media_get_session_names(media); for (; iter; iter = g_list_delete_link(iter, iter)) { gchar *session_name = iter->data; - purple_media_remove_output_window(media, session_name, NULL); + purple_media_manager_remove_output_windows( + media->priv->manager, media, + session_name, NULL); } } +GstElement * +purple_media_get_tee(PurpleMedia *media, + const gchar *session_id, const gchar *participant) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA(media), NULL); + + if (session_id != NULL && participant == NULL) { + PurpleMediaSession *session = + purple_media_get_session(media, session_id); + return (session != NULL) ? session->tee : NULL; + } else if (session_id != NULL && participant != NULL) { + PurpleMediaStream *stream = + purple_media_get_stream(media, + session_id, participant); + return (stream != NULL) ? stream->tee : NULL; + } + g_return_val_if_reached(NULL); +} + #endif /* USE_VV */ diff -r cb3c6ec9291e -r 6cf36f68033c libpurple/media.h --- a/libpurple/media.h Sat Feb 21 05:15:28 2009 +0000 +++ b/libpurple/media.h Tue Feb 24 08:11:24 2009 +0000 @@ -651,12 +651,14 @@ 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, +gulong purple_media_set_output_window(PurpleMedia *media, const gchar *session_id, const gchar *participant, gulong window_id); -gboolean purple_media_remove_output_window(PurpleMedia *media, + +void purple_media_remove_output_windows(PurpleMedia *media); + +GstElement *purple_media_get_tee(PurpleMedia *media, const gchar *session_id, const gchar *participant); -void purple_media_remove_output_windows(PurpleMedia *media); #ifdef __cplusplus } diff -r cb3c6ec9291e -r 6cf36f68033c libpurple/mediamanager.c --- a/libpurple/mediamanager.c Sat Feb 21 05:15:28 2009 +0000 +++ b/libpurple/mediamanager.c Tue Feb 24 08:11:24 2009 +0000 @@ -35,12 +35,27 @@ #ifdef USE_VV #include +#include + +typedef struct _PurpleMediaOutputWindow PurpleMediaOutputWindow; + +struct _PurpleMediaOutputWindow +{ + gulong id; + PurpleMedia *media; + gchar *session_id; + gchar *participant; + gulong window_id; + GstElement *sink; +}; struct _PurpleMediaManagerPrivate { GstElement *pipeline; GList *medias; GList *elements; + GList *output_windows; + gulong next_output_window_id; PurpleMediaElementInfo *video_src; PurpleMediaElementInfo *video_sink; @@ -120,6 +135,7 @@ { media->priv = PURPLE_MEDIA_MANAGER_GET_PRIVATE(media); media->priv->medias = NULL; + media->priv->next_output_window_id = 1; } static void @@ -412,4 +428,180 @@ return NULL; } +static void +window_id_cb(GstBus *bus, GstMessage *msg, PurpleMediaOutputWindow *ow) +{ + if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT || + !gst_structure_has_name(msg->structure, + "prepare-xwindow-id")) + return; + + if (GST_ELEMENT_PARENT(GST_MESSAGE_SRC(msg)) == ow->sink) { + g_signal_handlers_disconnect_matched(bus, G_SIGNAL_MATCH_FUNC + | G_SIGNAL_MATCH_DATA, 0, 0, NULL, + window_id_cb, ow); + + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY( + GST_MESSAGE_SRC(msg)), ow->window_id); + } +} + +gboolean +purple_media_manager_create_output_window(PurpleMediaManager *manager, + PurpleMedia *media, const gchar *session_id, + const gchar *participant) +{ + GList *iter; + + g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); + + iter = manager->priv->output_windows; + for(; iter; iter = g_list_next(iter)) { + PurpleMediaOutputWindow *ow = iter->data; + + if (ow->sink == NULL && ow->media == media && + ((participant != NULL && + ow->participant != NULL && + !strcmp(participant, ow->participant)) || + (participant == ow->participant)) && + !strcmp(session_id, ow->session_id)) { + GstBus *bus; + GstElement *queue; + GstElement *tee = purple_media_get_tee(media, + session_id, participant); + + if (tee == NULL) + continue; + + queue = gst_element_factory_make( + "queue", NULL); + ow->sink = purple_media_manager_get_element( + manager, PURPLE_MEDIA_RECV_VIDEO); + + if (participant == NULL) + /* aka this is a preview sink */ + g_object_set(G_OBJECT(ow->sink), "sync", FALSE, + "async", "FALSE", NULL); + + gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)), + queue, ow->sink, NULL); + + bus = gst_pipeline_get_bus(GST_PIPELINE( + manager->priv->pipeline)); + g_signal_connect(bus, "sync-message::element", + G_CALLBACK(window_id_cb), ow); + gst_object_unref(bus); + + gst_element_sync_state_with_parent(ow->sink); + gst_element_link(queue, ow->sink); + gst_element_sync_state_with_parent(queue); + gst_element_link(tee, queue); + } + } + return TRUE; +} + +gulong +purple_media_manager_set_output_window(PurpleMediaManager *manager, + PurpleMedia *media, const gchar *session_id, + const gchar *participant, gulong window_id) +{ + PurpleMediaOutputWindow *output_window; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + g_return_val_if_fail(PURPLE_IS_MEDIA(media), FALSE); + + output_window = g_new0(PurpleMediaOutputWindow, 1); + output_window->id = manager->priv->next_output_window_id++; + output_window->media = media; + output_window->session_id = g_strdup(session_id); + output_window->participant = g_strdup(participant); + output_window->window_id = window_id; + + manager->priv->output_windows = g_list_prepend( + manager->priv->output_windows, output_window); + + if (purple_media_get_tee(media, session_id, participant) != NULL) + purple_media_manager_create_output_window(manager, + media, session_id, participant); + + return output_window->id; +} + +gboolean +purple_media_manager_remove_output_window(PurpleMediaManager *manager, + gulong output_window_id) +{ + PurpleMediaOutputWindow *output_window = NULL; + GList *iter; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + + iter = manager->priv->output_windows; + for (; iter; iter = g_list_next(iter)) { + PurpleMediaOutputWindow *ow = iter->data; + if (ow->id == output_window_id) { + manager->priv->output_windows = g_list_delete_link( + manager->priv->output_windows, iter); + output_window = ow; + break; + } + } + + if (output_window == NULL) + return FALSE; + + if (output_window->sink != NULL) { + GstPad *pad = gst_element_get_static_pad( + output_window->sink, "sink"); + GstPad *peer = gst_pad_get_peer(pad); + GstElement *queue = GST_ELEMENT_PARENT(peer); + gst_object_unref(pad); + pad = gst_element_get_static_pad(queue, "sink"); + peer = gst_pad_get_peer(pad); + gst_object_unref(pad); + gst_element_release_request_pad(GST_ELEMENT_PARENT(peer), peer); + gst_element_set_locked_state(queue, TRUE); + gst_element_set_state(queue, GST_STATE_NULL); + gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(queue)), queue); + gst_element_set_locked_state(output_window->sink, TRUE); + gst_element_set_state(output_window->sink, GST_STATE_NULL); + gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(output_window->sink)), + output_window->sink); + } + + g_free(output_window->session_id); + g_free(output_window->participant); + g_free(output_window); + + return TRUE; +} + +void +purple_media_manager_remove_output_windows(PurpleMediaManager *manager, + PurpleMedia *media, const gchar *session_id, + const gchar *participant) +{ + GList *iter; + + g_return_if_fail(PURPLE_IS_MEDIA(media)); + + iter = manager->priv->output_windows; + + for (; iter;) { + PurpleMediaOutputWindow *ow = iter->data; + iter = g_list_next(iter); + + if (media == ow->media && + ((session_id != NULL && ow->session_id != NULL && + !strcmp(session_id, ow->session_id)) || + (session_id == ow->session_id)) && + ((participant != NULL && ow->participant != NULL && + !strcmp(participant, ow->participant)) || + (participant == ow->participant))) + purple_media_manager_remove_output_window( + manager, ow->id); + } +} + #endif /* USE_VV */ diff -r cb3c6ec9291e -r 6cf36f68033c libpurple/mediamanager.h --- a/libpurple/mediamanager.h Sat Feb 21 05:15:28 2009 +0000 +++ b/libpurple/mediamanager.h Tue Feb 24 08:11:24 2009 +0000 @@ -184,6 +184,20 @@ PurpleMediaElementInfo *info); PurpleMediaElementInfo *purple_media_manager_get_active_element( PurpleMediaManager *manager, PurpleMediaElementType type); +/** + * This shouldn't be called outside of mediamanager.c and media.c + */ +gboolean purple_media_manager_create_output_window( + PurpleMediaManager *manager, PurpleMedia *media, + const gchar *session_id, const gchar *participant); +gulong purple_media_manager_set_output_window(PurpleMediaManager *manager, + PurpleMedia *media, const gchar *session_id, + const gchar *participant, gulong window_id); +gboolean purple_media_manager_remove_output_window( + PurpleMediaManager *manager, gulong output_window_id); +void purple_media_manager_remove_output_windows( + PurpleMediaManager *manager, PurpleMedia *media, + const gchar *session_id, const gchar *participant); /*}@*/ #ifdef __cplusplus