Mercurial > pidgin
changeset 26085:d6741612b1d0
merge of '8abcc61528a93f4c18a6e1f200a7ba69f1694c28'
and '96b230451345d5543554f1d018ab450c76820d01'
author | Mike Ruprecht <maiku@soc.pidgin.im> |
---|---|
date | Thu, 05 Feb 2009 00:15:33 +0000 |
parents | 218f052b9bf5 (current diff) 0e8814c437b2 (diff) |
children | af42303654a5 |
files | |
diffstat | 12 files changed, 540 insertions(+), 46 deletions(-) [+] |
line wrap: on
line diff
--- a/libpurple/mediamanager.c Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/mediamanager.c Thu Feb 05 00:15:33 2009 +0000 @@ -39,6 +39,12 @@ struct _PurpleMediaManagerPrivate { GList *medias; + GList *elements; + + PurpleMediaElementInfo *video_src; + PurpleMediaElementInfo *video_sink; + PurpleMediaElementInfo *audio_src; + PurpleMediaElementInfo *audio_sink; }; #define PURPLE_MEDIA_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), PURPLE_TYPE_MEDIA_MANAGER, PurpleMediaManagerPrivate)) @@ -123,6 +129,8 @@ g_list_delete_link(priv->medias, priv->medias)) { g_object_unref(priv->medias->data); } + for (; priv->elements; priv->elements = + g_list_delete_link(priv->elements, priv->elements)); parent_class->finalize(media); } @@ -205,19 +213,22 @@ PurpleMediaSessionType type) { GstElement *ret = NULL; - GstElement *level = NULL; /* TODO: If src, retrieve current src */ /* TODO: Send a signal here to allow for overriding the source/sink */ - if (type & PURPLE_MEDIA_SEND_AUDIO) - purple_media_audio_init_src(&ret, &level); - else if (type & PURPLE_MEDIA_RECV_AUDIO) - purple_media_audio_init_recv(&ret, &level); - else if (type & PURPLE_MEDIA_SEND_VIDEO) - purple_media_video_init_src(&ret); - else if (type & PURPLE_MEDIA_RECV_VIDEO) - purple_media_video_init_recv(&ret); + if (type & PURPLE_MEDIA_SEND_AUDIO + && manager->priv->audio_src != NULL) + ret = manager->priv->audio_src->create(); + else if (type & PURPLE_MEDIA_RECV_AUDIO + && manager->priv->audio_sink != NULL) + ret = manager->priv->audio_sink->create(); + else if (type & PURPLE_MEDIA_SEND_VIDEO + && manager->priv->video_src != NULL) + ret = manager->priv->video_src->create(); + else if (type & PURPLE_MEDIA_RECV_VIDEO + && manager->priv->video_sink != NULL) + ret = manager->priv->video_sink->create(); if (ret == NULL) purple_debug_error("media", "Error creating source or sink\n"); @@ -225,4 +236,122 @@ return ret; } +PurpleMediaElementInfo * +purple_media_manager_get_element_info(PurpleMediaManager *manager, + const gchar *id) +{ + GList *iter; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + + iter = manager->priv->elements; + + for (; iter; iter = g_list_next(iter)) { + PurpleMediaElementInfo *info = iter->data; + if (!strcmp(info->id, id)) + return info; + } + + return NULL; +} + +gboolean +purple_media_manager_register_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + g_return_val_if_fail(info != NULL, FALSE); + + if (purple_media_manager_get_element_info(manager, info->id) != NULL) + return FALSE; + + manager->priv->elements = + g_list_prepend(manager->priv->elements, info); + return TRUE; +} + +gboolean +purple_media_manager_unregister_element(PurpleMediaManager *manager, + const gchar *id) +{ + PurpleMediaElementInfo *info; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + + info = purple_media_manager_get_element_info(manager, id); + + if (info == NULL) + return FALSE; + + if (manager->priv->audio_src == info) + manager->priv->audio_src = NULL; + if (manager->priv->audio_sink == info) + manager->priv->audio_sink = NULL; + if (manager->priv->video_src == info) + manager->priv->video_src = NULL; + if (manager->priv->video_sink == info) + manager->priv->video_sink = NULL; + + manager->priv->elements = g_list_remove( + manager->priv->elements, info); + return TRUE; +} + +gboolean +purple_media_manager_set_active_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info) +{ + gboolean ret = FALSE; + + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), FALSE); + g_return_val_if_fail(info != NULL, FALSE); + + if (purple_media_manager_get_element_info(manager, info->id) == NULL) + purple_media_manager_register_element(manager, info); + + if (info->type & PURPLE_MEDIA_ELEMENT_SRC) { + if (info->type & PURPLE_MEDIA_ELEMENT_AUDIO) { + manager->priv->audio_src = info; + ret = TRUE; + } + if (info->type & PURPLE_MEDIA_ELEMENT_VIDEO) { + manager->priv->video_src = info; + ret = TRUE; + } + } + if (info->type & PURPLE_MEDIA_ELEMENT_SINK) { + if (info->type & PURPLE_MEDIA_ELEMENT_AUDIO) { + manager->priv->audio_sink = info; + ret = TRUE; + } + if (info->type & PURPLE_MEDIA_ELEMENT_VIDEO) { + manager->priv->video_sink = info; + ret = TRUE; + } + } + + return ret; +} + +PurpleMediaElementInfo * +purple_media_manager_get_active_element(PurpleMediaManager *manager, + PurpleMediaElementType type) +{ + g_return_val_if_fail(PURPLE_IS_MEDIA_MANAGER(manager), NULL); + + if (type & PURPLE_MEDIA_ELEMENT_SRC) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) + return manager->priv->audio_src; + else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) + return manager->priv->video_src; + } else if (type & PURPLE_MEDIA_ELEMENT_SINK) { + if (type & PURPLE_MEDIA_ELEMENT_AUDIO) + return manager->priv->audio_sink; + else if (type & PURPLE_MEDIA_ELEMENT_VIDEO) + return manager->priv->video_sink; + } + + return NULL; +} + #endif /* USE_VV */
--- a/libpurple/mediamanager.h Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/mediamanager.h Thu Feb 05 00:15:33 2009 +0000 @@ -50,6 +50,8 @@ typedef struct _PurpleMediaManagerClass PurpleMediaManagerClass; /** @copydoc _PurpleMediaManagerPrivate */ typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate; +/** @copydoc _PurpleMediaElementInfo */ +typedef struct _PurpleMediaElementInfo PurpleMediaElementInfo; /** The media manager class. */ struct _PurpleMediaManagerClass @@ -64,6 +66,37 @@ PurpleMediaManagerPrivate *priv; /**< Private data for the manager. */ }; +typedef enum { + PURPLE_MEDIA_ELEMENT_AUDIO = 1, /** supports audio */ + PURPLE_MEDIA_ELEMENT_VIDEO = 1 << 1, /** supports video */ + PURPLE_MEDIA_ELEMENT_AUDIO_VIDEO = PURPLE_MEDIA_ELEMENT_AUDIO + | PURPLE_MEDIA_ELEMENT_VIDEO, /** supports audio and video */ + + PURPLE_MEDIA_ELEMENT_NO_SRCS = 0, /** has no src pads */ + PURPLE_MEDIA_ELEMENT_ONE_SRC = 1 << 2, /** has one src pad */ + PURPLE_MEDIA_ELEMENT_MULTI_SRC = 1 << 3, /** has multiple src pads */ + PURPLE_MEDIA_ELEMENT_REQUEST_SRC = 1 << 4, /** src pads must be requested */ + + PURPLE_MEDIA_ELEMENT_NO_SINKS = 0, /** has no sink pads */ + PURPLE_MEDIA_ELEMENT_ONE_SINK = 1 << 5, /** has one sink pad */ + PURPLE_MEDIA_ELEMENT_MULTI_SINK = 1 << 6, /** has multiple sink pads */ + PURPLE_MEDIA_ELEMENT_REQUEST_SINK = 1 << 7, /** sink pads must be requested */ + + PURPLE_MEDIA_ELEMENT_UNIQUE = 1 << 8, /** This element is unique and + only one instance of it should + be created at a time */ + + PURPLE_MEDIA_ELEMENT_SRC = 1 << 9, /** can be set as an active src */ + PURPLE_MEDIA_ELEMENT_SINK = 1 << 10, /** can be set as an active sink */ +} PurpleMediaElementType; + +struct _PurpleMediaElementInfo +{ + const gchar *id; + PurpleMediaElementType type; + GstElement *(*create)(void); +}; + #ifdef __cplusplus extern "C" { #endif @@ -131,6 +164,16 @@ GstElement *purple_media_manager_get_element(PurpleMediaManager *manager, PurpleMediaSessionType type); +PurpleMediaElementInfo *purple_media_manager_get_element_info( + PurpleMediaManager *manager, const gchar *name); +gboolean purple_media_manager_register_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info); +gboolean purple_media_manager_unregister_element(PurpleMediaManager *manager, + const gchar *name); +gboolean purple_media_manager_set_active_element(PurpleMediaManager *manager, + PurpleMediaElementInfo *info); +PurpleMediaElementInfo *purple_media_manager_get_active_element( + PurpleMediaManager *manager, PurpleMediaElementType type); /*}@*/ #ifdef __cplusplus
--- a/libpurple/protocols/jabber/disco.c Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/disco.c Thu Feb 05 00:15:33 2009 +0000 @@ -452,7 +452,12 @@ if (!strcmp(name, "Google Talk")) { purple_debug_info("jabber", "Google Talk!\n"); js->googletalk = TRUE; - } + + /* autodiscover stun and relays */ + jabber_google_send_jingle_info(js); + } else { + /* TODO: add external service discovery here... */ + } } for (child = xmlnode_get_child(query, "feature"); child;
--- a/libpurple/protocols/jabber/google.c Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/google.c Thu Feb 05 00:15:33 2009 +0000 @@ -23,6 +23,8 @@ #include "mediamanager.h" #include "util.h" #include "privacy.h" +#include "dnsquery.h" +#include "network.h" #include "buddy.h" #include "google.h" @@ -30,6 +32,8 @@ #include "presence.h" #include "iq.h" +#include "jingle/jingle.h" + #ifdef USE_VV typedef struct { @@ -124,7 +128,7 @@ sess = google_session_create_xmlnode(session, "candidates"); xmlnode_insert_child(iq->node, sess); xmlnode_set_attrib(iq->node, "to", session->remote_jid); - + for (;candidates;candidates = candidates->next) { char port[8]; char pref[8]; @@ -132,7 +136,7 @@ if (!strcmp(transport->ip, "127.0.0.1")) continue; - + candidate = xmlnode_new("candidate"); g_snprintf(port, sizeof(port), "%d", transport->port); @@ -162,7 +166,6 @@ xmlnode_set_attrib(candidate, "generation", "0"); xmlnode_set_attrib(candidate, "network", "0"); xmlnode_insert_child(sess, candidate); - } jabber_iq_send(iq); } @@ -246,6 +249,26 @@ } } +static GParameter * +jabber_google_session_get_params(JabberStream *js, guint *num) +{ + guint num_params; + GParameter *params = jingle_get_params(js, &num_params); + GParameter *new_params = g_new0(GParameter, num_params + 1); + + memcpy(new_params, params, sizeof(GParameter) * num_params); + + purple_debug_info("jabber", "setting Google jingle compatibility param\n"); + new_params[num_params].name = "compatibility-mode"; + g_value_init(&new_params[num_params].value, G_TYPE_UINT); + g_value_set_uint(&new_params[num_params].value, 1); /* NICE_COMPATIBILITY_GOOGLE */ + + g_free(params); + *num = num_params + 1; + return new_params; +} + + PurpleMedia* jabber_google_session_initiate(JabberStream *js, const gchar *who, PurpleMediaSessionType type) { @@ -253,7 +276,8 @@ JabberBuddy *jb; JabberBuddyResource *jbr; gchar *jid; - GParameter param; + GParameter *params; + guint num_params; /* construct JID to send to */ jb = jabber_buddy_find(js, who, FALSE); @@ -286,18 +310,15 @@ purple_media_manager_get(), js->gc, "fsrtpconference", session->remote_jid, TRUE); - /* GTalk requires the NICE_COMPATIBILITY_GOOGLE param */ - param.name = "compatibility-mode"; - memset(¶m.value, 0, sizeof(GValue)); - g_value_init(¶m.value, G_TYPE_UINT); - g_value_set_uint(¶m.value, 1); /* NICE_COMPATIBILITY_GOOGLE */ + params = jabber_google_session_get_params(js, &num_params); if (purple_media_add_stream(session->media, "google-voice", session->remote_jid, PURPLE_MEDIA_AUDIO, - "nice", 1, ¶m) == FALSE) { + "nice", num_params, params) == FALSE) { purple_media_error(session->media, "Error adding stream."); purple_media_hangup(session->media); google_session_destroy(session); + g_free(params); return NULL; } @@ -310,6 +331,7 @@ sessions = g_hash_table_new(google_session_id_hash, google_session_id_equal); g_hash_table_insert(sessions, &(session->id), session); + g_free(params); return session->media; } @@ -322,8 +344,9 @@ xmlnode *desc_element, *codec_element; PurpleMediaCodec *codec; const char *id, *encoding_name, *clock_rate; - GParameter param; - + GParameter *params; + guint num_params; + if (session->state != UNINIT) { purple_debug_error("jabber", "Received initiate for active session.\n"); return; @@ -332,22 +355,21 @@ session->media = purple_media_manager_create_media(purple_media_manager_get(), js->gc, "fsrtpconference", session->remote_jid, FALSE); - /* GTalk requires the NICE_COMPATIBILITY_GOOGLE param */ - param.name = "compatibility-mode"; - memset(¶m.value, 0, sizeof(GValue)); - g_value_init(¶m.value, G_TYPE_UINT); - g_value_set_uint(¶m.value, 1); /* NICE_COMPATIBILITY_GOOGLE */ + params = jabber_google_session_get_params(js, &num_params); if (purple_media_add_stream(session->media, "google-voice", session->remote_jid, - PURPLE_MEDIA_AUDIO, "nice", 1, ¶m) == FALSE) { + PURPLE_MEDIA_AUDIO, "nice", num_params, params) == FALSE) { purple_media_error(session->media, "Error adding stream."); purple_media_hangup(session->media); google_session_send_terminate(session); + g_free(params); return; } + g_free(params); + desc_element = xmlnode_get_child(sess, "description"); - + for (codec_element = xmlnode_get_child(desc_element, "payload-type"); codec_element; codec_element = xmlnode_get_next_twin(codec_element)) { @@ -368,7 +390,7 @@ G_CALLBACK(google_session_state_changed_cb), session); purple_media_codec_list_free(codecs); - + result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, xmlnode_get_attrib(packet, "id")); xmlnode_set_attrib(result->node, "to", session->remote_jid); @@ -1025,3 +1047,105 @@ const char *attr = purple_status_get_attr_string(tune, PURPLE_TUNE_TITLE); return attr ? g_strdup_printf("♫ %s", attr) : g_strdup(""); } + +static void +jabber_google_stun_lookup_cb(GSList *hosts, gpointer data, + const char *error_message) +{ + JabberStream *js = (JabberStream *) data; + + if (error_message) { + purple_debug_error("jabber", "Google STUN lookup failed: %s\n", + error_message); + g_slist_free(hosts); + return; + } + + if (hosts && g_slist_next(hosts)) { + struct sockaddr *addr = g_slist_next(hosts)->data; + char dst[INET6_ADDRSTRLEN]; + int port; + + if (addr->sa_family == AF_INET6) { + inet_ntop(addr->sa_family, &((struct sockaddr_in6 *) addr)->sin6_addr, + dst, sizeof(dst)); + port = ntohs(((struct sockaddr_in6 *) addr)->sin6_port); + } else { + inet_ntop(addr->sa_family, &((struct sockaddr_in *) addr)->sin_addr, + dst, sizeof(dst)); + port = ntohs(((struct sockaddr_in *) addr)->sin_port); + } + + if (js) { + if (js->stun_ip) { + g_free(js->stun_ip); + } + js->stun_ip = g_strdup(dst); + purple_debug_info("jabber", "set Google STUN IP address: %s\n", dst); + js->stun_port = port; + purple_debug_info("jabber", "set Google STUN port: %d\n", port); + purple_debug_info("jabber", "set Google STUN port: %d\n", port); + /* unmark ongoing query */ + js->stun_query = NULL; + } + } + + g_slist_free(hosts); +} + +static void +jabber_google_jingle_info_cb(JabberStream *js, xmlnode *result, + gpointer nullus) +{ + if (result) { + const xmlnode *query = + xmlnode_get_child_with_namespace(result, "query", + GOOGLE_JINGLE_INFO_NAMESPACE); + + if (query) { + const xmlnode *stun = xmlnode_get_child(query, "stun"); + + purple_debug_info("jabber", "got google:jingleinfo\n"); + + if (stun) { + xmlnode *server = xmlnode_get_child(stun, "server"); + + if (server) { + const gchar *host = xmlnode_get_attrib(server, "host"); + const gchar *udp = xmlnode_get_attrib(server, "udp"); + + if (host && udp) { + int port = atoi(udp); + /* if there, would already be an ongoing query, + cancel it */ + if (js->stun_query) + purple_dnsquery_destroy(js->stun_query); + + js->stun_query = purple_dnsquery_a(host, port, + jabber_google_stun_lookup_cb, js); + } + } + } + /* should perhaps handle relays later on, or maybe wait until + Google supports a common standard... */ + } + } +} + +void +jabber_google_handle_jingle_info(JabberStream *js, xmlnode *packet) +{ + jabber_google_jingle_info_cb(js, packet, NULL); +} + +void +jabber_google_send_jingle_info(JabberStream *js) +{ + JabberIq *jingle_info = + jabber_iq_new_query(js, JABBER_IQ_GET, GOOGLE_JINGLE_INFO_NAMESPACE); + + jabber_iq_set_callback(jingle_info, jabber_google_jingle_info_cb, + NULL); + purple_debug_info("jabber", "sending google:jingleinfo query\n"); + jabber_iq_send(jingle_info); +}
--- a/libpurple/protocols/jabber/google.h Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/google.h Thu Feb 05 00:15:33 2009 +0000 @@ -27,6 +27,8 @@ #include "jabber.h" #include "media.h" +#define GOOGLE_JINGLE_INFO_NAMESPACE "google:jingleinfo" + void jabber_gmail_init(JabberStream *js); void jabber_gmail_poke(JabberStream *js, xmlnode *node); @@ -49,5 +51,7 @@ PurpleMedia *jabber_google_session_initiate(JabberStream *js, const gchar *who, PurpleMediaSessionType type); void jabber_google_session_parse(JabberStream *js, xmlnode *node); +void jabber_google_handle_jingle_info(JabberStream *js, xmlnode *packet); +void jabber_google_send_jingle_info(JabberStream *js); #endif /* _PURPLE_GOOGLE_H_ */
--- a/libpurple/protocols/jabber/iq.c Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/iq.c Thu Feb 05 00:15:33 2009 +0000 @@ -448,6 +448,9 @@ #ifdef USE_VV jabber_iq_register_handler(JINGLE, jingle_parse); #endif + /* handle Google jingleinfo */ + jabber_iq_register_handler(GOOGLE_JINGLE_INFO_NAMESPACE, + jabber_google_handle_jingle_info); } void jabber_iq_uninit(void)
--- a/libpurple/protocols/jabber/jabber.c Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.c Thu Feb 05 00:15:33 2009 +0000 @@ -735,20 +735,24 @@ js->sessions = NULL; #endif + js->stun_ip = NULL; + js->stun_port = 0; + js->stun_query = NULL; + if(!js->user) { purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, _("Invalid XMPP ID")); return; } - + if (!js->user->domain || *(js->user->domain) == '\0') { purple_connection_error_reason (gc, PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, _("Invalid XMPP ID. Domain must be set.")); return; } - + if((my_jb = jabber_buddy_find(js, purple_account_get_username(account), TRUE))) my_jb->subscription |= JABBER_SUB_BOTH; @@ -1222,6 +1226,10 @@ server = connect_server[0] ? connect_server : js->user->domain; js->certificate_CN = g_strdup(server); + js->stun_ip = NULL; + js->stun_port = 0; + js->stun_query = NULL; + jabber_stream_set_state(js, JABBER_STREAM_CONNECTING); if(purple_account_get_bool(account, "old_ssl", FALSE)) { @@ -1425,6 +1433,15 @@ g_free(js->srv_rec); js->srv_rec = NULL; + g_free(js->stun_ip); + js->stun_ip = NULL; + + /* cancel DNS query for STUN, if one is ongoing */ + if (js->stun_query) { + purple_dnsquery_destroy(js->stun_query); + js->stun_query = NULL; + } + g_free(js); gc->proto_data = NULL;
--- a/libpurple/protocols/jabber/jabber.h Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/jabber.h Thu Feb 05 00:15:33 2009 +0000 @@ -58,6 +58,7 @@ #include "mediamanager.h" #include "roomlist.h" #include "sslconn.h" +#include "dnsquery.h" #include "jutil.h" #include "xmlnode.h" @@ -250,6 +251,12 @@ #ifdef USE_VV GHashTable *medias; #endif + + /* maybe this should only be present when USE_VV? */ + gchar *stun_ip; + int stun_port; + PurpleDnsQueryData *stun_query; + /* later add stuff to handle TURN relays... */ }; typedef gboolean (JabberFeatureEnabled)(JabberStream *js, const gchar *shortname, const gchar *namespace);
--- a/libpurple/protocols/jabber/jingle/jingle.c Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.c Thu Feb 05 00:15:33 2009 +0000 @@ -20,6 +20,7 @@ */ #include "internal.h" +#include "network.h" #include "content.h" #include "debug.h" @@ -438,3 +439,32 @@ jingle_terminate_sessions_gh, NULL); } +GParameter * +jingle_get_params(JabberStream *js, guint *num) +{ + /* don't set a STUN server if one is set globally in prefs, in that case + this will be handled in media.c */ + gboolean has_account_stun = js->stun_ip && !purple_network_get_stun_ip(); + guint num_params = has_account_stun ? 2 : 0; + GParameter *params = NULL; + + if (num_params > 0) { + params = g_new0(GParameter, num_params); + + purple_debug_info("jabber", + "setting param stun-ip for stream using Google auto-config: %s\n", + js->stun_ip); + params[0].name = "stun-ip"; + g_value_init(¶ms[0].value, G_TYPE_STRING); + g_value_set_string(¶ms[0].value, js->stun_ip); + purple_debug_info("jabber", + "setting param stun-port for stream using Google auto-config: %d\n", + js->stun_port); + params[1].name = "stun-port"; + g_value_init(¶ms[1].value, G_TYPE_UINT); + g_value_set_uint(¶ms[1].value, js->stun_port); + } + + *num = num_params; + return params; +}
--- a/libpurple/protocols/jabber/jingle/jingle.h Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/jingle.h Thu Feb 05 00:15:33 2009 +0000 @@ -72,6 +72,10 @@ void jingle_terminate_sessions(JabberStream *js); +/* create a GParam array given autoconfigured STUN (and later perhaps TURN). + if google_talk is TRUE, set compatability mode to GOOGLE_TALK */ +GParameter *jingle_get_params(JabberStream *js, guint *num_params); + #ifdef __cplusplus } #endif
--- a/libpurple/protocols/jabber/jingle/rtp.c Thu Feb 05 00:15:13 2009 +0000 +++ b/libpurple/protocols/jabber/jingle/rtp.c Thu Feb 05 00:15:33 2009 +0000 @@ -402,6 +402,8 @@ gboolean is_audio; PurpleMediaSessionType type; JingleTransport *transport; + GParameter *params = NULL; + guint num_params; /* maybe this create ought to just be in initiate and handle initiate */ if (media == NULL) @@ -436,13 +438,16 @@ type = is_audio == TRUE ? PURPLE_MEDIA_RECV_AUDIO : PURPLE_MEDIA_RECV_VIDEO; + params = + jingle_get_params(jingle_session_get_js(session), &num_params); purple_media_add_stream(media, name, remote_jid, - type, transmitter, 0, NULL); + type, transmitter, num_params, params); g_free(name); g_free(media_type); g_free(remote_jid); g_free(senders); + g_free(params); g_object_unref(session); return TRUE;
--- a/pidgin/gtkmedia.c Thu Feb 05 00:15:13 2009 +0000 +++ b/pidgin/gtkmedia.c Thu Feb 05 00:15:33 2009 +0000 @@ -57,6 +57,8 @@ GstElement *send_level; GstElement *recv_level; + GtkWidget *statusbar; + GtkWidget *calling; GtkWidget *accept; GtkWidget *reject; @@ -184,15 +186,58 @@ return FALSE; } +static int +pidgin_x_error_handler(Display *display, XErrorEvent *event) +{ + const gchar *error_type; + switch (event->error_code) { +#define XERRORCASE(type) case type: error_type = #type; break + XERRORCASE(BadAccess); + XERRORCASE(BadAlloc); + XERRORCASE(BadAtom); + XERRORCASE(BadColor); + XERRORCASE(BadCursor); + XERRORCASE(BadDrawable); + XERRORCASE(BadFont); + XERRORCASE(BadGC); + XERRORCASE(BadIDChoice); + XERRORCASE(BadImplementation); + XERRORCASE(BadLength); + XERRORCASE(BadMatch); + XERRORCASE(BadName); + XERRORCASE(BadPixmap); + XERRORCASE(BadRequest); + XERRORCASE(BadValue); + XERRORCASE(BadWindow); +#undef XERRORCASE + default: + error_type = "unknown"; + break; + } + purple_debug_error("media", "A %s Xlib error has occurred. " + "The program would normally crash now.\n", + error_type); + return 0; +} + static void pidgin_media_init (PidginMedia *media) { GtkWidget *vbox, *hbox; media->priv = PIDGIN_MEDIA_GET_PRIVATE(media); + XSetErrorHandler(pidgin_x_error_handler); + vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_container_add(GTK_CONTAINER(media), vbox); + media->priv->statusbar = gtk_statusbar_new(); + gtk_box_pack_end(GTK_BOX(vbox), media->priv->statusbar, + FALSE, FALSE, 0); + gtk_statusbar_push(GTK_STATUSBAR(media->priv->statusbar), + 0, _("Connecting...")); + gtk_widget_show(media->priv->statusbar); + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); gtk_widget_show(GTK_WIDGET(hbox)); @@ -215,7 +260,7 @@ gtk_widget_show_all(media->priv->accept); gtk_widget_show_all(media->priv->reject); - media->priv->display = gtk_vbox_new(TRUE, PIDGIN_HIG_BOX_SPACE); + media->priv->display = gtk_vbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); gtk_box_pack_start(GTK_BOX(vbox), media->priv->display, TRUE, TRUE, PIDGIN_HIG_BOX_SPACE); gtk_widget_show(vbox); @@ -366,6 +411,8 @@ if (conv != NULL) purple_conversation_write(conv, NULL, error, PURPLE_MESSAGE_ERROR, time(NULL)); + gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), + 0, error); } static void @@ -374,6 +421,8 @@ { pidgin_media_set_state(gtkmedia, PIDGIN_MEDIA_ACCEPTED); pidgin_media_emit_message(gtkmedia, _("Call in progress.")); + gtk_statusbar_push(GTK_STATUSBAR(gtkmedia->priv->statusbar), + 0, _("Call in progress.")); gtk_widget_show(GTK_WIDGET(gtkmedia)); } @@ -430,6 +479,7 @@ GtkWidget *remote_video; GtkWidget *plug; GtkWidget *socket; + GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); @@ -454,10 +504,11 @@ data->participant = g_strdup(gtkmedia->priv->screenname); remote_video = gtk_drawing_area_new(); + gtk_widget_modify_bg(remote_video, GTK_STATE_NORMAL, &color); 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), -1, 100); + gtk_widget_set_size_request (GTK_WIDGET(remote_video), 320, 240); gtk_widget_show(remote_video); gtk_widget_show(aspect); @@ -469,6 +520,7 @@ GtkWidget *local_video; GtkWidget *plug; GtkWidget *socket; + GdkColor color = {0, 0, 0, 0}; aspect = gtk_aspect_frame_new(NULL, 0.5, 0.5, 4.0/3.0, FALSE); gtk_frame_set_shadow_type(GTK_FRAME(aspect), GTK_SHADOW_IN); @@ -493,10 +545,11 @@ data->participant = NULL; local_video = gtk_drawing_area_new(); + gtk_widget_modify_bg(local_video, GTK_STATE_NORMAL, &color); g_signal_connect(G_OBJECT(local_video), "realize", G_CALLBACK(realize_cb), data); gtk_container_add(GTK_CONTAINER(plug), local_video); - gtk_widget_set_size_request (GTK_WIDGET(local_video), -1, 100); + gtk_widget_set_size_request (GTK_WIDGET(local_video), 160, 120); gtk_widget_show(local_video); gtk_widget_show(aspect); @@ -551,6 +604,8 @@ pidgin_media_emit_message(gtkmedia, message); g_free(message); } + + gtk_widget_show(gtkmedia->priv->display); } static void @@ -573,15 +628,6 @@ } else if (type == PURPLE_MEDIA_STATE_CHANGED_NEW && sid != NULL && name != NULL) { pidgin_media_ready_cb(media, gtkmedia, sid); - } else if (type == PURPLE_MEDIA_STATE_CHANGED_CONNECTED) { - GstElement *audiosendbin = NULL, *audiorecvbin = NULL; - GstElement *videosendbin = NULL, *videorecvbin = NULL; - - purple_media_get_elements(media, &audiosendbin, &audiorecvbin, - &videosendbin, &videorecvbin); - - if (audiorecvbin || audiosendbin || videorecvbin || videosendbin) - gtk_widget_show(gtkmedia->priv->display); } } @@ -739,11 +785,88 @@ return TRUE; } +static GstElement * +create_default_video_src(void) +{ + GstElement *ret = NULL; + purple_media_video_init_src(&ret); + return ret; +} + +static GstElement * +create_default_video_sink(void) +{ + GstElement *ret = NULL; + purple_media_video_init_recv(&ret); + return ret; +} + +static GstElement * +create_default_audio_src(void) +{ + GstElement *ret = NULL, *level = NULL; + purple_media_audio_init_src(&ret, &level); + return ret; +} + +static GstElement * +create_default_audio_sink(void) +{ + GstElement *ret = NULL, *level = NULL; + purple_media_audio_init_recv(&ret, &level); + return ret; +} + +static PurpleMediaElementInfo default_video_src = +{ + "pidgindefaultvideosrc", /* id */ + PURPLE_MEDIA_ELEMENT_VIDEO /* type */ + | PURPLE_MEDIA_ELEMENT_SRC + | PURPLE_MEDIA_ELEMENT_ONE_SRC + | PURPLE_MEDIA_ELEMENT_UNIQUE, + create_default_video_src, /* create */ +}; + +static PurpleMediaElementInfo default_video_sink = +{ + "pidgindefaultvideosink", /* id */ + PURPLE_MEDIA_ELEMENT_VIDEO /* type */ + | PURPLE_MEDIA_ELEMENT_SINK + | PURPLE_MEDIA_ELEMENT_ONE_SINK, + create_default_video_sink, /* create */ +}; + +static PurpleMediaElementInfo default_audio_src = +{ + "pidgindefaultaudiosrc", /* id */ + PURPLE_MEDIA_ELEMENT_AUDIO /* type */ + | PURPLE_MEDIA_ELEMENT_SRC + | PURPLE_MEDIA_ELEMENT_ONE_SRC + | PURPLE_MEDIA_ELEMENT_UNIQUE, + create_default_audio_src, /* create */ +}; + +static PurpleMediaElementInfo default_audio_sink = +{ + "pidgindefaultaudiosink", /* id */ + PURPLE_MEDIA_ELEMENT_AUDIO /* type */ + | PURPLE_MEDIA_ELEMENT_SINK + | PURPLE_MEDIA_ELEMENT_ONE_SINK, + create_default_audio_sink, /* create */ +}; + void pidgin_medias_init(void) { - g_signal_connect(G_OBJECT(purple_media_manager_get()), "init-media", + PurpleMediaManager *manager = purple_media_manager_get(); + g_signal_connect(G_OBJECT(manager), "init-media", G_CALLBACK(pidgin_media_new_cb), NULL); + + purple_debug_info("gtkmedia", "Registering media element types\n"); + purple_media_manager_set_active_element(manager, &default_video_src); + purple_media_manager_set_active_element(manager, &default_video_sink); + purple_media_manager_set_active_element(manager, &default_audio_src); + purple_media_manager_set_active_element(manager, &default_audio_sink); } #endif /* USE_VV */