pidgin.vv.yahoo.voice: 77a5acca: Add support to receive a voice call. Thi...

maiku at pidgin.im maiku at pidgin.im
Fri Aug 28 23:05:21 EDT 2009


-----------------------------------------------------------------
Revision: 77a5accab64083fba4a6d68d1275b135288234a1
Ancestor: bad95bf79c54addda84101885ba9fc4b2f17c12d
Author: maiku at pidgin.im
Date: 2009-08-29T02:58:21
Branch: im.pidgin.pidgin.vv.yahoo.voice
URL: http://d.pidgin.im/viewmtn/revision/info/77a5accab64083fba4a6d68d1275b135288234a1

Modified files:
        libpurple/protocols/yahoo/yahoo_sip.c

ChangeLog: 

Add support to receive a voice call. This requires an uncommited Yahoo
compatibility mode to both Farsight2's nice transmitter plugin and libnice.

For some reason SPEEX/8000 has poor audio quality and SPEEX/16000 isn't
received at all. PCMU, however, works perfectly. The SPEEX modes may need
to be ignored if not resolved.

-------------- next part --------------
============================================================
--- libpurple/protocols/yahoo/yahoo_sip.c	e7371b3954a1b79de94601ab7b89f2bae32a39dc
+++ libpurple/protocols/yahoo/yahoo_sip.c	73fde3dbf78211630f110ec6c86fbecf2bd3ba9d
@@ -26,6 +26,7 @@
 
 #include "cipher.h"
 #include "debug.h"
+#include "mediamanager.h"
 
 #include "yahoo_sip.h"
 #include "libymsg.h"
@@ -33,7 +34,9 @@
 #include <glib.h>
 #include <sofia-sip/nua.h>
 #include <sofia-sip/su_glib.h>
+#include <sofia-sip/soa.h>
 #include <sofia-sip/sip_header.h>
+#include <sofia-sip/sdp.h>
 
 /* This is the y64 alphabet... it's like base64, but has a . and a _ */
 static const char base64digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
@@ -66,6 +69,11 @@ static void to_y64(char *out, const unsi
 	*out = '\0';
 }
 
+static void yahoo_sip_candidates_prepared_cb(PurpleMedia *media,
+		gchar *sid, gchar *name, nua_handle_t *nh);
+static void yahoo_sip_codecs_changed_cb(PurpleMedia *media,
+		gchar *sid, nua_handle_t *nh);
+
 static gboolean
 send_yahooref(gpointer nh)
 {
@@ -74,6 +82,131 @@ static void
 }
 
 static void
+yahoo_sip_send_response(PurpleMedia *media, nua_handle_t *nh)
+{
+	const sip_to_t *remote = nua_handle_remote(nh);
+	GList *codecs, *candidates;
+	gchar **codec_strs, **candidate_strs,
+			*codec_str, *candidate_str, *sdp_str;
+	guint port = 0;
+	guint i = 0;
+
+	codecs = purple_media_get_codecs(media, "yahoo-voice");
+	codec_strs = g_new0(gchar*, g_list_length(codecs) + 1);
+
+	for (; codecs; codecs = g_list_delete_link(codecs, codecs)) {
+		PurpleMediaCodec *codec = codecs->data;
+		gchar *encoding_name =
+				purple_media_codec_get_encoding_name(codec);
+		codec_strs[i] = g_strdup_printf("a=rtpmap:%d %s/%d\r\n",
+				purple_media_codec_get_id(codec), encoding_name,
+				purple_media_codec_get_clock_rate(codec));
+		g_free(encoding_name);
+		++i;
+	}
+	codec_str = g_strjoinv(NULL, codec_strs);
+	g_strfreev(codec_strs);
+	purple_debug_info("yahoo", "local codecs:\n%s", codec_str);
+
+	candidates = purple_media_get_local_candidates(media,
+			"yahoo-voice", remote->a_display);
+	candidate_strs = g_new0(gchar*, g_list_length(candidates) + 1);
+
+	if (candidates->data)
+		port = purple_media_candidate_get_port(candidates->data);
+
+	i = 0;
+	for (; candidates; candidates =
+			g_list_delete_link(candidates, candidates)) {
+		PurpleMediaCandidate *candidate = candidates->data;
+		gchar *ip = purple_media_candidate_get_ip(candidate);
+		PurpleMediaCandidateType type =
+				purple_media_candidate_get_candidate_type(candidate);
+		gchar *username = purple_media_candidate_get_username(candidate);
+		gchar *password = purple_media_candidate_get_password(candidate);
+		candidate_strs[i] = g_strdup_printf(
+				"a=candidate:%s %d %s %s 1 %s %d channel-type %d\r\n", username,
+				purple_media_candidate_get_component_id(candidate), password,
+				purple_media_candidate_get_protocol(candidate) ==
+				PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ? "UDP" : "TCP",
+				ip, purple_media_candidate_get_port(candidate),
+				type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? 1 : type ==
+				PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? 2 : type ==
+				PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? 3 : 0);
+		g_free(ip);
+		++i;
+	}
+	candidate_str = g_strjoinv(NULL, candidate_strs);
+	g_strfreev(candidate_strs);
+	purple_debug_info("yahoo", "local candidates:\n%s", candidate_str);
+
+	sdp_str = g_strdup_printf("m=audio %d RTP/AVP 97 0 8 101 100\r"
+			"\n%s%sa=sendrecv\r\n", port, codec_str, candidate_str);
+	g_free(codec_str);
+	g_free(candidate_str);
+
+	nua_respond(nh, 200, "OK",
+			SOATAG_USER_SDP_STR(sdp_str),
+			TAG_END());
+	g_free(sdp_str);
+}
+
+static void
+yahoo_sip_ready(PurpleMedia *media, nua_handle_t *nh)
+{
+	purple_debug_info("yahoo", "sip ready\n");
+	if (purple_media_candidates_prepared(media, NULL, NULL) &&
+			purple_media_codecs_ready(media, NULL) &&
+			(purple_media_is_initiator(media, NULL, NULL) ||
+			purple_media_accepted(media, NULL, NULL))) {
+		if (purple_media_is_initiator(media, NULL, NULL)) {
+			/* send initiate */
+		} else {
+			yahoo_sip_send_response(media, nh);
+		}
+
+		g_signal_handlers_disconnect_by_func(G_OBJECT(media),
+				G_CALLBACK(yahoo_sip_candidates_prepared_cb),
+				nh);
+		g_signal_handlers_disconnect_by_func(G_OBJECT(media),
+				G_CALLBACK(yahoo_sip_codecs_changed_cb),
+				nh);
+	}
+}
+
+static void
+yahoo_sip_candidates_prepared_cb(PurpleMedia *media,
+		gchar *sid, gchar *name, nua_handle_t *nh)
+{
+	if (sid == NULL && name == NULL)
+		yahoo_sip_ready(media, nh);
+}
+
+static void
+yahoo_sip_codecs_changed_cb(PurpleMedia *media, gchar *sid, nua_handle_t *nh)
+{
+		
+}
+
+static void
+yahoo_sip_state_changed_cb(PurpleMedia *media, PurpleMediaState state,
+		gchar *sid, gchar *name, nua_handle_t *nh)
+{
+	
+}
+
+static void
+yahoo_sip_stream_info_cb(PurpleMedia *media, PurpleMediaInfoType type,
+		gchar *sid, gchar *name, gboolean local,
+		nua_handle_t *nh)
+{
+	if (type == PURPLE_MEDIA_INFO_ACCEPT && local == TRUE &&
+			sid == NULL && name == NULL) {
+		yahoo_sip_ready(media, nh);
+	}
+}
+
+static void
 event_callback(nua_event_t event, int status, const gchar *phrase, nua_t *nua,
 		nua_magic_t *magic, nua_handle_t *nh, nua_hmagic_t *hmagic,
 		const sip_t *sip, tagi_t tags[])
@@ -148,6 +281,138 @@ event_callback(nua_event_t event, int st
 		nua_shutdown(nua);
 	} else if (event == nua_r_shutdown && status == 200) {
 		nua_destroy(nua);
+	} else if (event == nua_i_invite) {
+		PurpleMedia *media;
+		PurpleAccount *account = magic;
+		const sip_to_t *remote = nua_handle_remote(nh);
+		GParameter param;
+		purple_debug_info("yahoo", "INVITE status %d display: %s\n",
+				status, remote->a_display);
+		nua_respond(nh, 180, "Ringing", TAG_END());
+		media = purple_media_manager_create_media(
+				purple_media_manager_get(),
+				account, "fsrtpconference",
+				remote->a_display, FALSE);
+
+		if (!media) {
+			purple_debug_error("yahoo",
+					"Couldn't create media session\n");
+			return;
+		}
+
+		purple_media_set_prpl_data(media, nh);
+
+		/* connect callbacks */
+		g_signal_connect(G_OBJECT(media), "candidates-prepared",
+				G_CALLBACK(yahoo_sip_candidates_prepared_cb), nh);
+		g_signal_connect(G_OBJECT(media), "codecs-changed",
+				G_CALLBACK(yahoo_sip_codecs_changed_cb), nh);
+		g_signal_connect(G_OBJECT(media), "state-changed",
+				G_CALLBACK(yahoo_sip_state_changed_cb), nh);
+		g_signal_connect(G_OBJECT(media), "stream-info",
+				G_CALLBACK(yahoo_sip_stream_info_cb), nh);
+
+		param.name = "compatibility-mode";
+		param.value.g_type = 0;
+		g_value_init(&param.value, G_TYPE_UINT);
+		g_value_set_uint(&param.value, 4); /* NICE_COMPATIBILITY_YAHOO */
+		purple_media_add_stream(media, "yahoo-voice", remote->a_display,
+				PURPLE_MEDIA_AUDIO, FALSE, "nice", 1, &param);
+	} else if (event == nua_i_state && status == 100) {
+		/*
+		 * This has a phrase of "Trying" if not,
+		 * I need to add the check back.
+		 */
+		PurpleAccount *account = magic;
+		const sdp_session_t *sdp_session = NULL;
+		const sip_to_t *remote = nua_handle_remote(nh);
+		sdp_media_t *sdp_media;
+		sdp_rtpmap_t *codec_iter;
+		sdp_attribute_t *conn_iter;
+		GList *codecs = NULL, *candidates = NULL;
+		PurpleMedia *media = NULL;
+		GList *iter;
+
+		tl_gets(tags, SOATAG_REMOTE_SDP_REF(sdp_session), TAG_END());
+		if (!sdp_session) {
+			purple_debug_error("yahoo", "Couldn't retrieve "
+					"remote SDP from invitation.\n");
+			return;
+		}
+		sdp_media = sdp_session->sdp_media;
+		codec_iter = sdp_media->m_rtpmaps;
+		conn_iter = sdp_media->m_attributes;
+
+		/*
+		 * Find PurpleMedia associated with this handle.
+		 * Need to use a structure to hold this information.
+		 * Then this won't be necessary.
+		 */
+		iter = purple_media_manager_get_media_by_account(
+				purple_media_manager_get(), account);
+		for (; iter; iter = g_list_delete_link(iter, iter)) {
+			nua_handle_t *nh_media =
+					purple_media_get_prpl_data(iter->data);
+			if (nh_media == nh) {
+				media = iter->data;
+				break;
+			}
+		}
+		g_list_free(iter);
+
+		if (!media) {
+			purple_debug_error("yahoo", "Couldn't find media "
+					"session associated with this SIP call.\n");
+			return;
+		}
+
+		for (; codec_iter; codec_iter = codec_iter->rm_next) {
+			PurpleMediaCodec *codec = purple_media_codec_new(
+					codec_iter->rm_pt, codec_iter->rm_encoding,
+					PURPLE_MEDIA_AUDIO, codec_iter->rm_rate);
+			/* XXX: Need to add FsCodec parameters if they exist */
+			purple_debug_info("yahoo", "codec: a=rtpmap:%d %s/%lu"
+					" rm_params: %s rm_fmtp: %s\n",
+					codec_iter->rm_pt, codec_iter->rm_encoding,
+					codec_iter->rm_rate, codec_iter->rm_params,
+					codec_iter->rm_fmtp);
+			codecs = g_list_append(codecs, codec);
+		}
+		purple_media_set_remote_codecs(media, "yahoo-voice",
+				remote->a_display, codecs);
+		purple_media_codec_list_free(codecs);
+
+		for (; conn_iter; conn_iter = conn_iter->a_next) {
+			PurpleMediaCandidate *candidate;
+			gchar **c;
+			guint type;
+			if (strcmp(conn_iter->a_name, "candidate"))
+				continue;
+			c = g_strsplit(conn_iter->a_value, " ", 0);
+			purple_debug_info("yahoo", "candidate ?: %s "
+					"component-id: %s ?: %s network-type:"
+					" %s ?: %s ip: %s port: %s "
+					"channel-type: %s\n", c[0], c[1],
+					c[2], c[3], c[4], c[5], c[6], c[8]);
+			type = atoi(c[8]);
+			candidate= purple_media_candidate_new(c[0], atoi(c[1]), type == 1 ?
+					PURPLE_MEDIA_CANDIDATE_TYPE_HOST : type == 2 ?
+					PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX :
+					PURPLE_MEDIA_CANDIDATE_TYPE_RELAY,
+					!strcmp(c[3], "UDP") ?
+					PURPLE_MEDIA_NETWORK_PROTOCOL_UDP :
+					PURPLE_MEDIA_NETWORK_PROTOCOL_TCP,
+					c[5], atoi(c[6]));
+			g_object_set(G_OBJECT(candidate), "username",
+					c[0], "password", c[2], NULL);
+			purple_debug_info("yahoo", "candidate a_value: %s\n",
+					conn_iter->a_value);
+			g_strfreev(c);
+			candidates = g_list_append(candidates, candidate);
+		}
+		purple_media_add_remote_candidates(media, "yahoo-voice",
+				remote->a_display, candidates);		
+		purple_media_codec_list_free(candidates);
 	}
 }
 
@@ -158,7 +423,6 @@ yahoo_sip_init(PurpleAccount *account)
 	YahooData *yd = purple_connection_get_protocol_data(pc);
 	GSource *gsource;
 	su_root_t *sofia_event_loop;
-	su_timer_t *timer;
 	gchar *sip_str;
 
 	purple_debug_info("yahoo", "Starting Sofia-SIP\n");
@@ -168,9 +432,6 @@ yahoo_sip_init(PurpleAccount *account)
 	gsource = su_glib_root_gsource(sofia_event_loop);
 	g_source_attach(gsource, g_main_context_default());
 
-	timer = su_timer_create(su_root_task(sofia_event_loop), 200L);
-
-
 	yd->nua = nua_create(sofia_event_loop, event_callback, account,
 			NUTAG_URL("sip:*:*;transport=tcp"),
 			NUTAG_M_PARAMS("transport=tcp"),


More information about the Commits mailing list