Mercurial > pidgin.yaz
diff libpurple/protocols/jabber/google.c @ 27079:72bcdcb0629f
Add Gmail video support.
Thanks to Eion for all his testing help.
author | Mike Ruprecht <maiku@soc.pidgin.im> |
---|---|
date | Tue, 02 Jun 2009 05:00:20 +0000 |
parents | 3b1aad1cb5ce |
children | f0621e47ccf3 |
line wrap: on
line diff
--- a/libpurple/protocols/jabber/google.c Mon Jun 01 10:33:38 2009 +0000 +++ b/libpurple/protocols/jabber/google.c Tue Jun 02 05:00:20 2009 +0000 @@ -36,6 +36,9 @@ #ifdef USE_VV +#define NS_GOOGLE_VIDEO "http://www.google.com/session/video" +#define NS_GOOGLE_PHONE "http://www.google.com/session/phone" + typedef struct { char *id; char *initiator; @@ -55,6 +58,7 @@ PurpleMedia *media; JabberStream *js; char *remote_jid; + gboolean video; } GoogleSession; static gboolean @@ -104,9 +108,13 @@ google_session_send_candidates(PurpleMedia *media, gchar *session_id, gchar *participant, GoogleSession *session) { - GList *candidates = purple_media_get_local_candidates(session->media, "google-voice", - session->remote_jid); + GList *candidates = purple_media_get_local_candidates( + session->media, session_id, session->remote_jid); PurpleMediaCandidate *transport; + gboolean video = FALSE; + + if (!strcmp(session_id, "google-video")) + video = TRUE; for (;candidates;candidates = candidates->next) { JabberIq *iq; @@ -114,11 +122,10 @@ PurpleMediaCandidateType type; xmlnode *sess; xmlnode *candidate; + guint component_id; transport = (PurpleMediaCandidate*)(candidates->data); - - if (purple_media_candidate_get_component_id(transport) - != PURPLE_MEDIA_COMPONENT_RTP) - continue; + component_id = purple_media_candidate_get_component_id( + transport); iq = jabber_iq_new(session->js, JABBER_IQ_SET); sess = google_session_create_xmlnode(session, "candidates"); @@ -139,7 +146,11 @@ xmlnode_set_attrib(candidate, "address", ip); xmlnode_set_attrib(candidate, "port", port); - xmlnode_set_attrib(candidate, "name", "rtp"); + xmlnode_set_attrib(candidate, "name", + component_id == PURPLE_MEDIA_COMPONENT_RTP ? + video ? "video_rtp" : "rtp" : + component_id == PURPLE_MEDIA_COMPONENT_RTCP ? + video ? "video_rtcp" : "rtcp" : "none"); xmlnode_set_attrib(candidate, "username", username); /* * As of this writing, Farsight 2 in Google compatibility @@ -205,13 +216,38 @@ google_session_send_candidates(session->media, "google-voice", session->remote_jid, session); + google_session_send_candidates(session->media, + "google-video", session->remote_jid, + session); xmlnode_set_attrib(iq->node, "to", session->remote_jid); xmlnode_set_attrib(iq->node, "from", me); sess = google_session_create_xmlnode(session, "accept"); } xmlnode_insert_child(iq->node, sess); desc = xmlnode_new_child(sess, "description"); - xmlnode_set_namespace(desc, "http://www.google.com/session/phone"); + if (session->video) + xmlnode_set_namespace(desc, NS_GOOGLE_VIDEO); + else + xmlnode_set_namespace(desc, NS_GOOGLE_PHONE); + + codecs = purple_media_get_codecs(media, "google-video"); + + for (iter = codecs; iter; iter = g_list_next(iter)) { + PurpleMediaCodec *codec = (PurpleMediaCodec*)iter->data; + gchar *id = g_strdup_printf("%d", + purple_media_codec_get_id(codec)); + gchar *encoding_name = + purple_media_codec_get_encoding_name(codec); + payload = xmlnode_new_child(desc, "payload-type"); + xmlnode_set_attrib(payload, "id", id); + xmlnode_set_attrib(payload, "name", encoding_name); + xmlnode_set_attrib(payload, "width", "320"); + xmlnode_set_attrib(payload, "height", "200"); + xmlnode_set_attrib(payload, "framerate", "30"); + g_free(encoding_name); + g_free(id); + } + purple_media_codec_list_free(codecs); codecs = purple_media_get_codecs(media, "google-voice"); @@ -224,6 +260,8 @@ gchar *clock_rate = g_strdup_printf("%d", purple_media_codec_get_clock_rate(codec)); payload = xmlnode_new_child(desc, "payload-type"); + if (session->video) + xmlnode_set_namespace(payload, NS_GOOGLE_PHONE); xmlnode_set_attrib(payload, "id", id); xmlnode_set_attrib(payload, "name", encoding_name); xmlnode_set_attrib(payload, "clockrate", clock_rate); @@ -235,10 +273,14 @@ jabber_iq_send(iq); - if (is_initiator) + if (is_initiator) { google_session_send_candidates(session->media, "google-voice", session->remote_jid, session); + google_session_send_candidates(session->media, + "google-video", session->remote_jid, + session); + } g_signal_handlers_disconnect_by_func(G_OBJECT(session->media), G_CALLBACK(google_session_ready), session); @@ -339,6 +381,9 @@ session->js = js; session->remote_jid = jid; + if (type & PURPLE_MEDIA_VIDEO) + session->video = TRUE; + session->media = purple_media_manager_create_media( purple_media_manager_get(), purple_connection_get_account(js->gc), @@ -349,8 +394,12 @@ params = jabber_google_session_get_params(js, &num_params); if (purple_media_add_stream(session->media, "google-voice", - session->remote_jid, PURPLE_MEDIA_AUDIO, - TRUE, "nice", num_params, params) == FALSE) { + session->remote_jid, PURPLE_MEDIA_AUDIO, + TRUE, "nice", num_params, params) == FALSE || + (session->video && purple_media_add_stream( + session->media, "google-video", + session->remote_jid, PURPLE_MEDIA_VIDEO, + TRUE, "nice", num_params, params) == FALSE)) { purple_media_error(session->media, "Error adding stream."); purple_media_stream_info(session->media, PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE); @@ -378,10 +427,10 @@ google_session_handle_initiate(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { JabberIq *result; - GList *codecs = NULL; + GList *codecs = NULL, *video_codecs = NULL; xmlnode *desc_element, *codec_element; PurpleMediaCodec *codec; - const char *id, *encoding_name, *clock_rate; + const char *xmlns; GParameter *params; guint num_params; @@ -390,6 +439,19 @@ return; } + desc_element = xmlnode_get_child(sess, "description"); + xmlns = xmlnode_get_namespace(desc_element); + + if (purple_strequal(xmlns, NS_GOOGLE_PHONE)) + session->video = FALSE; + else if (purple_strequal(xmlns, NS_GOOGLE_VIDEO)) + session->video = TRUE; + else { + purple_debug_error("jabber", "Received initiate with " + "invalid namespace %s.\n", xmlns); + return; + } + session->media = purple_media_manager_create_media( purple_media_manager_get(), purple_connection_get_account(js->gc), @@ -401,7 +463,11 @@ if (purple_media_add_stream(session->media, "google-voice", session->remote_jid, PURPLE_MEDIA_AUDIO, FALSE, - "nice", num_params, params) == FALSE) { + "nice", num_params, params) == FALSE || + (session->video && purple_media_add_stream( + session->media, "google-video", + session->remote_jid, PURPLE_MEDIA_VIDEO, + FALSE, "nice", num_params, params) == FALSE)) { purple_media_error(session->media, "Error adding stream."); purple_media_stream_info(session->media, PURPLE_MEDIA_INFO_HANGUP, NULL, NULL, TRUE); @@ -412,23 +478,55 @@ g_free(params); - desc_element = xmlnode_get_child(sess, "description"); + for (codec_element = xmlnode_get_child(desc_element, "payload-type"); + codec_element; codec_element = codec_element->next) { + const char *id, *encoding_name, *clock_rate, + *width, *height, *framerate; + gboolean video; + if (codec_element->name && + strcmp(codec_element->name, "payload-type")) + continue; - for (codec_element = xmlnode_get_child(desc_element, "payload-type"); - codec_element; - codec_element = xmlnode_get_next_twin(codec_element)) { + xmlns = xmlnode_get_namespace(codec_element); encoding_name = xmlnode_get_attrib(codec_element, "name"); id = xmlnode_get_attrib(codec_element, "id"); - clock_rate = xmlnode_get_attrib(codec_element, "clockrate"); + + if (!session->video || + (xmlns && !strcmp(xmlns, NS_GOOGLE_PHONE))) { + clock_rate = xmlnode_get_attrib( + codec_element, "clockrate"); + video = FALSE; + } else { + width = xmlnode_get_attrib(codec_element, "width"); + height = xmlnode_get_attrib(codec_element, "height"); + framerate = xmlnode_get_attrib( + codec_element, "framerate"); + clock_rate = "90000"; + video = TRUE; + } if (id) { - codec = purple_media_codec_new(atoi(id), encoding_name, PURPLE_MEDIA_AUDIO, - clock_rate ? atoi(clock_rate) : 0); - codecs = g_list_append(codecs, codec); + codec = purple_media_codec_new(atoi(id), encoding_name, + video ? PURPLE_MEDIA_VIDEO : + PURPLE_MEDIA_AUDIO, + clock_rate ? atoi(clock_rate) : 0); + if (video) + video_codecs = g_list_append( + video_codecs, codec); + else + codecs = g_list_append(codecs, codec); } } - purple_media_set_remote_codecs(session->media, "google-voice", session->remote_jid, codecs); + if (codecs) + purple_media_set_remote_codecs(session->media, "google-voice", + session->remote_jid, codecs); + if (video_codecs) + purple_media_set_remote_codecs(session->media, "google-video", + session->remote_jid, video_codecs); + + purple_media_codec_list_free(codecs); + purple_media_codec_list_free(video_codecs); g_signal_connect_swapped(G_OBJECT(session->media), "accepted", G_CALLBACK(google_session_ready), session); @@ -442,8 +540,6 @@ g_signal_connect(G_OBJECT(session->media), "stream-info", G_CALLBACK(google_session_stream_info_cb), session); - purple_media_codec_list_free(codecs); - result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, iq_id); xmlnode_set_attrib(result->node, "to", session->remote_jid); @@ -454,19 +550,22 @@ google_session_handle_candidates(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { JabberIq *result; - GList *list = NULL; + GList *list = NULL, *video_list = NULL; xmlnode *cand; static int name = 0; char n[4]; - for (cand = xmlnode_get_child(sess, "candidate"); cand; cand = xmlnode_get_next_twin(cand)) { + for (cand = xmlnode_get_child(sess, "candidate"); cand; + cand = xmlnode_get_next_twin(cand)) { PurpleMediaCandidate *info; + const gchar *cname = xmlnode_get_attrib(cand, "name"); const gchar *type = xmlnode_get_attrib(cand, "type"); const gchar *protocol = xmlnode_get_attrib(cand, "protocol"); const gchar *address = xmlnode_get_attrib(cand, "address"); const gchar *port = xmlnode_get_attrib(cand, "port"); + guint component_id; - if (type && address && port) { + if (cname && type && address && port) { PurpleMediaCandidateType candidate_type; g_snprintf(n, sizeof(n), "S%d", name++); @@ -480,7 +579,13 @@ else candidate_type = PURPLE_MEDIA_CANDIDATE_TYPE_HOST; - info = purple_media_candidate_new(n, PURPLE_MEDIA_COMPONENT_RTP, + if (purple_strequal(cname, "rtcp") || + purple_strequal(cname, "video_rtcp")) + component_id = PURPLE_MEDIA_COMPONENT_RTCP; + else + component_id = PURPLE_MEDIA_COMPONENT_RTP; + + info = purple_media_candidate_new(n, component_id, candidate_type, purple_strequal(protocol, "udp") ? PURPLE_MEDIA_NETWORK_PROTOCOL_UDP : @@ -489,12 +594,23 @@ atoi(port)); g_object_set(info, "username", xmlnode_get_attrib(cand, "username"), "password", xmlnode_get_attrib(cand, "password"), NULL); - list = g_list_append(list, info); + if (!strncmp(cname, "video_", 6)) + video_list = g_list_append(video_list, info); + else + list = g_list_append(list, info); } } - purple_media_add_remote_candidates(session->media, "google-voice", session->remote_jid, list); + if (list) + purple_media_add_remote_candidates( + session->media, "google-voice", + session->remote_jid, list); + if (video_list) + purple_media_add_remote_candidates( + session->media, "google-video", + session->remote_jid, video_list); purple_media_candidate_list_free(list); + purple_media_candidate_list_free(video_list); result = jabber_iq_new(js, JABBER_IQ_RESULT); jabber_iq_set_id(result, iq_id); @@ -506,28 +622,57 @@ google_session_handle_accept(JabberStream *js, GoogleSession *session, xmlnode *sess, const char *iq_id) { xmlnode *desc_element = xmlnode_get_child(sess, "description"); - xmlnode *codec_element = xmlnode_get_child(desc_element, "payload-type"); - GList *codecs = NULL; + xmlnode *codec_element = xmlnode_get_child( + desc_element, "payload-type"); + GList *codecs = NULL, *video_codecs = NULL; JabberIq *result = NULL; + const gchar *xmlns = xmlnode_get_namespace(desc_element); + gboolean video = (xmlns && !strcmp(xmlns, NS_GOOGLE_VIDEO)); + + for (; codec_element; codec_element = codec_element->next) { + const gchar *xmlns, *encoding_name, *id, + *clock_rate, *width, *height, *framerate; + gboolean video_codec = FALSE; + + if (!purple_strequal(codec_element->name, "payload-type")) + continue; - for (; codec_element; codec_element = - xmlnode_get_next_twin(codec_element)) { - const gchar *encoding_name = - xmlnode_get_attrib(codec_element, "name"); - const gchar *id = xmlnode_get_attrib(codec_element, "id"); - const gchar *clock_rate = - xmlnode_get_attrib(codec_element, "clockrate"); + xmlns = xmlnode_get_namespace(codec_element); + encoding_name = xmlnode_get_attrib(codec_element, "name"); + id = xmlnode_get_attrib(codec_element, "id"); + + if (!video || purple_strequal(xmlns, NS_GOOGLE_PHONE)) + clock_rate = xmlnode_get_attrib( + codec_element, "clockrate"); + else { + clock_rate = "90000"; + width = xmlnode_get_attrib(codec_element, "width"); + height = xmlnode_get_attrib(codec_element, "height"); + framerate = xmlnode_get_attrib( + codec_element, "framerate"); + video_codec = TRUE; + } if (id && encoding_name) { - PurpleMediaCodec *codec = purple_media_codec_new(atoi(id), - encoding_name, PURPLE_MEDIA_AUDIO, + PurpleMediaCodec *codec = purple_media_codec_new( + atoi(id), encoding_name, + video_codec ? PURPLE_MEDIA_VIDEO : + PURPLE_MEDIA_AUDIO, clock_rate ? atoi(clock_rate) : 0); - codecs = g_list_append(codecs, codec); + if (video_codec) + video_codecs = g_list_append( + video_codecs, codec); + else + codecs = g_list_append(codecs, codec); } } - purple_media_set_remote_codecs(session->media, "google-voice", - session->remote_jid, codecs); + if (codecs) + purple_media_set_remote_codecs(session->media, "google-voice", + session->remote_jid, codecs); + if (video_codecs) + purple_media_set_remote_codecs(session->media, "google-video", + session->remote_jid, video_codecs); purple_media_stream_info(session->media, PURPLE_MEDIA_INFO_ACCEPT, NULL, NULL, FALSE);