diff libpurple/protocols/jabber/jingle.c @ 23797:e1c8ec1259de

Updates voice and video to use Farsight 2, gets XMPP voice conferences closer to XEP-0167, and fixes a lot of bugs.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Fri, 23 May 2008 02:42:32 +0000
parents 9f36ed35615e
children 16543025aed1
line wrap: on
line diff
--- a/libpurple/protocols/jabber/jingle.c	Sun Apr 13 17:53:46 2008 +0000
+++ b/libpurple/protocols/jabber/jingle.c	Fri May 23 02:42:32 2008 +0000
@@ -28,8 +28,7 @@
 
 #ifdef USE_FARSIGHT
 
-#include <farsight/farsight.h>
-#include <farsight/farsight-transport.h>
+#include <gst/farsight/fs-candidate.h>
 
 /* keep a hash table of JingleSessions */
 static GHashTable *sessions = NULL;
@@ -66,10 +65,9 @@
 	purple_debug_info("jingle", "inserting session with key: %s into table\n",
 					  sess->id);
 	g_hash_table_insert(sessions, sess->id, sess);
-	
-	sess->remote_candidates = NULL;
-    sess->remote_codecs = NULL;
-	
+
+	sess->session_started = FALSE;
+
 	return sess;
 }
 
@@ -106,8 +104,6 @@
 {
 	g_hash_table_remove(sessions, sess->id);
 	g_free(sess->id);
-    farsight_codec_list_destroy(sess->remote_codecs);
-    g_list_free(sess->remote_candidates);
 	g_free(sess);
 }
 
@@ -115,20 +111,55 @@
 jabber_jingle_session_find_by_id(const char *id)
 {
 	purple_debug_info("jingle", "find_by_id %s\n", id);
-	purple_debug_info("jingle", "hash table: %lx\n", sessions);
+	purple_debug_info("jingle", "hash table: %p\n", sessions);
 	purple_debug_info("jingle", "hash table size %d\n",
 					  g_hash_table_size(sessions));
-	purple_debug_info("jingle", "lookup: %lx\n", g_hash_table_lookup(sessions, id));  
+	purple_debug_info("jingle", "lookup: %p\n", g_hash_table_lookup(sessions, id));  
 	return (JingleSession *) g_hash_table_lookup(sessions, id);
 }
 
 GList *
+jabber_jingle_session_find_by_js(JabberStream *js)
+{
+	GList *values = g_hash_table_get_values(sessions);
+	GList *iter = values;
+	GList *found = NULL;
+
+	for (; iter; iter = iter->next) {
+		JingleSession *session = (JingleSession *)iter->data;
+		if (session->js == js) {
+			found = g_list_prepend(found, session);
+		}
+	}
+
+	g_list_free(values);
+	return found;
+}
+
+JingleSession *jabber_jingle_session_find_by_jid(JabberStream *js, const char *jid)
+{
+	GList *values = g_hash_table_get_values(sessions);
+	GList *iter = values;
+
+	for (; iter; iter = iter->next) {
+		JingleSession *session = (JingleSession *)iter->data;
+		if (session->js == js && !strcmp(jid, session->remote_jid)) {
+			g_list_free(values);
+			return session;
+		}
+	}
+
+	g_list_free(values);
+	return NULL;	
+}
+
+GList *
 jabber_jingle_get_codecs(const xmlnode *description)
 {
 	GList *codecs = NULL;
 	xmlnode *codec_element = NULL;
 	const char *encoding_name,*id, *clock_rate;
-	FarsightCodec *codec;
+	FsCodec *codec;
 	
 	for (codec_element = xmlnode_get_child(description, "payload-type") ;
 		 codec_element ;
@@ -137,10 +168,9 @@
 		id = xmlnode_get_attrib(codec_element, "id");
 		clock_rate = xmlnode_get_attrib(codec_element, "clockrate");
 
-		codec = g_new0(FarsightCodec, 1);
-		farsight_codec_init(codec, atoi(id), encoding_name, 
-							FARSIGHT_MEDIA_TYPE_AUDIO, 
-							clock_rate ? atoi(clock_rate) : 0);
+		codec = fs_codec_new(atoi(id), encoding_name, 
+				     FS_MEDIA_TYPE_AUDIO, 
+				     clock_rate ? atoi(clock_rate) : 0);
 		codecs = g_list_append(codecs, codec);		 
 	}
 	return codecs;
@@ -151,48 +181,31 @@
 {
 	GList *candidates = NULL;
 	xmlnode *candidate = NULL;
-	FarsightTransportInfo *ti;
+	FsCandidate *c;
 	
 	for (candidate = xmlnode_get_child(transport, "candidate") ;
 		 candidate ;
 		 candidate = xmlnode_get_next_twin(candidate)) {
 		const char *type = xmlnode_get_attrib(candidate, "type");
-		ti = g_new0(FarsightTransportInfo, 1);
-		ti->component = atoi(xmlnode_get_attrib(candidate, "component"));
-		ti->ip = xmlnode_get_attrib(candidate, "ip");
-		ti->port = atoi(xmlnode_get_attrib(candidate, "port"));
-		ti->proto = strcmp(xmlnode_get_attrib(candidate, "protocol"),
+		c = fs_candidate_new(xmlnode_get_attrib(candidate, "component"), 
+							atoi(xmlnode_get_attrib(candidate, "component")),
+							strcmp(type, "host") == 0 ?
+								FS_CANDIDATE_TYPE_HOST :
+								strcmp(type, "prflx") == 0 ?
+									FS_CANDIDATE_TYPE_PRFLX :
+									FS_CANDIDATE_TYPE_RELAY,
+							strcmp(xmlnode_get_attrib(candidate, "protocol"),
 							  "udp") == 0 ? 
-				 				FARSIGHT_NETWORK_PROTOCOL_UDP :
-				 				FARSIGHT_NETWORK_PROTOCOL_TCP;
-		/* it seems Farsight RTP doesn't handle this correctly right now */
-		ti->username = xmlnode_get_attrib(candidate, "ufrag");
-		ti->password = xmlnode_get_attrib(candidate, "pwd");
-		/* not quite sure about this */
-		ti->type = strcmp(type, "host") == 0 ?
-					FARSIGHT_CANDIDATE_TYPE_LOCAL :
-					strcmp(type, "prflx") == 0 ?
-					 FARSIGHT_CANDIDATE_TYPE_DERIVED :
-					 FARSIGHT_CANDIDATE_TYPE_RELAY;
-			 
-		candidates = g_list_append(candidates, ti);
+				 				FS_NETWORK_PROTOCOL_UDP :
+				 				FS_NETWORK_PROTOCOL_TCP,
+							xmlnode_get_attrib(candidate, "ip"),
+							atoi(xmlnode_get_attrib(candidate, "port")));
+		candidates = g_list_append(candidates, c);
 	}
 	
 	return candidates;
 }
 
-FarsightStream *
-jabber_jingle_session_get_stream(const JingleSession *sess)
-{
-	return sess->stream;
-}
-
-void
-jabber_jingle_session_set_stream(JingleSession *sess, FarsightStream *stream)
-{
-	sess->stream = stream;
-}
-
 PurpleMedia *
 jabber_jingle_session_get_media(const JingleSession *sess)
 {
@@ -237,36 +250,6 @@
 	return sess->is_initiator;
 }
 
-void
-jabber_jingle_session_add_remote_candidate(JingleSession *sess,
-										   const GList *candidate)
-{
-	/* the length of the candidate list should be 1... */
-	GList *cand = candidate;
-	for (; cand ; cand = cand->next) {
-		purple_debug_info("jingle", "Adding remote candidate with id = %s\n",
-						  ((FarsightTransportInfo *) cand->data)->candidate_id);
-		sess->remote_candidates = g_list_append(sess->remote_candidates, 
-												cand->data);
-	}
-}
-
-static GList *
-jabber_jingle_session_get_remote_candidate(const JingleSession *sess,
-										   const gchar *id)
-{
-	GList *candidates = NULL;
-	GList *find = sess->remote_candidates;
-	for (; find ; find = find->next) {
-		const FarsightTransportInfo *candidate =
-			(FarsightTransportInfo *) find->data;
-		if (!strcmp(candidate->candidate_id, id)) {
-			candidates = g_list_append(candidates, (void *) candidate);
-		}
-	}
-	return candidates;
-}
-
 static xmlnode *
 jabber_jingle_session_create_jingle_element(const JingleSession *sess,
 											const char *action)
@@ -301,32 +284,105 @@
 	return jingle;
 }
 
-static xmlnode *
+xmlnode *
 jabber_jingle_session_create_description(const JingleSession *sess)
 {
-    GList *codecs =
-		farsight_stream_get_local_codecs(jabber_jingle_session_get_stream(sess));
-    
+    GList *codecs = purple_media_get_local_audio_codecs(sess->media);
     xmlnode *description = xmlnode_new("description");
+
 	xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp");
 	
 	/* get codecs */
 	for (; codecs ; codecs = codecs->next) {
-		FarsightCodec *codec = (FarsightCodec*)codecs->data;
-		char id[8], clockrate[10];
+		FsCodec *codec = (FsCodec*)codecs->data;
+		char id[8], clockrate[10], channels[10];
 		xmlnode *payload = xmlnode_new_child(description, "payload-type");
 		
 		g_snprintf(id, sizeof(id), "%d", codec->id);
 		g_snprintf(clockrate, sizeof(clockrate), "%d", codec->clock_rate);
+		g_snprintf(channels, sizeof(channels), "%d", codec->channels);
 		
 		xmlnode_set_attrib(payload, "name", codec->encoding_name);
 		xmlnode_set_attrib(payload, "id", id);
 		xmlnode_set_attrib(payload, "clockrate", clockrate);
+		xmlnode_set_attrib(payload, "channels", channels);
     }
     
+    fs_codec_list_destroy(codecs);
     return description;
 }
     
+static guint
+jabber_jingle_get_priority(guint type, guint network)
+{
+	switch (type) {
+		case FS_CANDIDATE_TYPE_HOST:
+			return network == 0 ? 4096 : network == 1 ? 2048 : 1024;
+			break;
+		case FS_CANDIDATE_TYPE_PRFLX:
+			return 126;
+			break;
+		case FS_CANDIDATE_TYPE_RELAY:
+			return 100;
+			break;
+		default:
+			return 0; /* unknown type, should not happen */
+	}
+}
+
+static xmlnode *
+jabber_jingle_session_create_candidate_info(FsCandidate *c, FsCandidate *remote)
+{
+	char port[8];
+	char prio[8];
+	char component[8];
+	xmlnode *candidate = NULL;
+	
+	candidate = xmlnode_new("candidate");
+	
+	g_snprintf(port, sizeof(port), "%d", c->port);
+	g_snprintf(prio, sizeof(prio), "%d", 
+		   jabber_jingle_get_priority(c->type, 0));
+	g_snprintf(component, sizeof(component), "%d", c->component_id);
+	
+	xmlnode_set_attrib(candidate, "component", component);
+	xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */
+	xmlnode_set_attrib(candidate, "generation", "0"); /* ? */
+	xmlnode_set_attrib(candidate, "ip", c->ip);
+	xmlnode_set_attrib(candidate, "network", "0"); /* ? */
+	xmlnode_set_attrib(candidate, "port", port);
+	xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */
+	xmlnode_set_attrib(candidate, "protocol",
+			   c->proto == FS_NETWORK_PROTOCOL_UDP ?
+			   "udp" : "tcp");
+	if (c->username)
+		xmlnode_set_attrib(candidate, "ufrag", c->username);
+	if (c->password)
+		xmlnode_set_attrib(candidate, "pwd", c->password);
+	
+	xmlnode_set_attrib(candidate, "type", 
+			   c->type == FS_CANDIDATE_TYPE_HOST ? 
+			   "host" :
+			   c->type == FS_CANDIDATE_TYPE_PRFLX ? 
+			   "prflx" :
+		       	   c->type == FS_CANDIDATE_TYPE_RELAY ? 
+			   "relay" : NULL);
+
+	/* relay */
+	if (c->type == FS_CANDIDATE_TYPE_RELAY) {
+		/* set rel-addr and rel-port? How? */
+	}
+
+	if (remote) {
+		char remote_port[8];
+		g_snprintf(remote_port, sizeof(remote_port), "%d", remote->port);
+		xmlnode_set_attrib(candidate, "rem-addr", remote->ip);
+		xmlnode_set_attrib(candidate, "rem-port", remote_port);
+	}
+
+	return candidate;
+}
+
 /* split into two separate methods, one to generate session-accept
 	(includes codecs) and one to generate transport-info (includes transports
 	candidates) */
@@ -337,6 +393,8 @@
 		jabber_jingle_session_create_jingle_element(sess, "session-accept");
 	xmlnode *content = NULL;
 	xmlnode *description = NULL;
+	xmlnode *transport = NULL;
+	xmlnode *candidate = NULL;
 	
 	
 	content = xmlnode_new_child(jingle, "content");
@@ -345,199 +403,82 @@
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
 	
 	description = jabber_jingle_session_create_description(sess);
-    xmlnode_insert_child(jingle, description);
-	
+	xmlnode_insert_child(content, description);
+
+	transport = xmlnode_new_child(content, "transport");
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
+	candidate = jabber_jingle_session_create_candidate_info(
+			purple_media_get_local_candidate(sess->media),
+			purple_media_get_remote_candidate(sess->media));
+	xmlnode_insert_child(transport, candidate);
+
 	return jingle;
 }
 
-static guint
-jabber_jingle_get_priority(guint type, guint network)
-{
-	switch (type) {
-		case FARSIGHT_CANDIDATE_TYPE_LOCAL:
-			return network == 0 ? 4096 : network == 1 ? 2048 : 1024;
-			break;
-		case FARSIGHT_CANDIDATE_TYPE_DERIVED:
-			return 126;
-			break;
-		case FARSIGHT_CANDIDATE_TYPE_RELAY:
-			return 100;
-			break;
-		default:
-			return 0; /* unknown type, should not happen */
-	}
-}
 
 xmlnode *
-jabber_jingle_session_create_transport_info(const JingleSession *sess,
-											gchar *candidate_id)
+jabber_jingle_session_create_transport_info(const JingleSession *sess)
 {
 	xmlnode *jingle = 
 		jabber_jingle_session_create_jingle_element(sess, "transport-info");
 	xmlnode *content = NULL;
 	xmlnode *transport = NULL;
-	GList *candidates =
-		farsight_stream_get_native_candidate(
-			jabber_jingle_session_get_stream(sess), candidate_id);
-	
+	GList *candidates = purple_media_get_local_audio_candidates(sess->media);
+
 	content = xmlnode_new_child(jingle, "content");
 	xmlnode_set_attrib(content, "creator", "initiator");
 	xmlnode_set_attrib(content, "name", "audio-content");
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
 		
 	transport = xmlnode_new_child(content, "transport");
-	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp");
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
 	
 	/* get transport candidate */
 	for (; candidates ; candidates = candidates->next) {
-		FarsightTransportInfo *transport_info = 
-			(FarsightTransportInfo *) candidates->data;
-		char port[8];
-		char prio[8];
-		char component[8];
-		xmlnode *candidate = NULL;
-		
-		if (!strcmp(transport_info->ip, "127.0.0.1")) {
+		FsCandidate *c = (FsCandidate *) candidates->data;
+
+		if (!strcmp(c->ip, "127.0.0.1")) {
 			continue;
 		}
-		
-		candidate = xmlnode_new_child(transport, "candidate");
-		
-		g_snprintf(port, sizeof(port), "%d", transport_info->port);
-		g_snprintf(prio, sizeof(prio), "%d", 
-				   jabber_jingle_get_priority(transport_info->type, 0));
-		g_snprintf(component, sizeof(component), "%d", transport_info->component);
-		
-		xmlnode_set_attrib(candidate, "component", component);
-		xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */
-		xmlnode_set_attrib(candidate, "generation", "0"); /* ? */
-		xmlnode_set_attrib(candidate, "ip", transport_info->ip);
-		xmlnode_set_attrib(candidate, "network", "0"); /* ? */
-		xmlnode_set_attrib(candidate, "port", port);
-		xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */
-		xmlnode_set_attrib(candidate, "protocol",
-						   transport_info->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ?
-						   "udp" : "tcp");
-		if (transport_info->username)
-			xmlnode_set_attrib(candidate, "ufrag", transport_info->username);
-		if (transport_info->password)
-			xmlnode_set_attrib(candidate, "pwd", transport_info->password);
-		
-		xmlnode_set_attrib(candidate, "type", 
-						   transport_info->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ? 
-						   "host" :
-						   transport_info->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ? 
-						   "prflx" :
-					       transport_info->type == FARSIGHT_CANDIDATE_TYPE_RELAY ? 
-						   "relay" : NULL);
-		
+
+		xmlnode_insert_child(transport,
+				     jabber_jingle_session_create_candidate_info(c, NULL));
 	}
-	farsight_transport_list_destroy(candidates);
+	fs_candidate_list_destroy(candidates);
 	
 	return jingle;
 }
 
 xmlnode *
 jabber_jingle_session_create_content_replace(const JingleSession *sess,
-											 gchar *native_candidate_id,
-											 gchar *remote_candidate_id)
+					     FsCandidate *native_candidate,
+					     FsCandidate *remote_candidate)
 {
 	xmlnode *jingle = 
 		jabber_jingle_session_create_jingle_element(sess, "content-replace");
 	xmlnode *content = NULL;
 	xmlnode *transport = NULL;
-	xmlnode *candidate = NULL;
-	xmlnode *description = NULL;
-	xmlnode *payload_type = NULL;
-	char local_port[8];
-	char remote_port[8];
-	char prio[8];
-	char component[8];
-	
+
 	purple_debug_info("jingle", "creating content-modify for native candidate %s " \
-					  ", remote candidate %s\n", native_candidate_id,
-					  remote_candidate_id);
-	
-	/* It seems the ID's we get from the Farsight callback is phony... */
-	/*
-	GList *native_candidates = 
-		farsight_stream_get_native_candidate(jabber_jingle_session_get_stream(sess),
-											 native_candidate_id);
-	purple_debug_info("jingle", "found %d native candidates with id %s\n",
-					  g_list_length(native_candidates), native_candidate_id);
-	GList *remote_candidates =
-		jabber_jingle_session_get_remote_candidate(sess, remote_candidate_id);
-	*/
-	
-	/* we assume lists have length == 1, I think they must... */
-	/*
-	FarsightTransportInfo *native = 
-		(FarsightTransportInfo *) native_candidates->data;
-	FarsightTransportInfo *remote =
-		(FarsightTransportInfo *) remote_candidates->data;
-	*/
-	
+			  ", remote candidate %s\n", native_candidate->candidate_id,
+			  remote_candidate->candidate_id);
+
 	content = xmlnode_new_child(jingle, "content");
 	xmlnode_set_attrib(content, "creator", "initiator");
 	xmlnode_set_attrib(content, "name", "audio-content");
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
 	
-	description = xmlnode_new_child(content, "description");
-	xmlnode_set_namespace(description, "urn:xmpp:tmp:jingle:apps:audio-rtp");
-	
-	payload_type = xmlnode_new_child(description, "payload-type");
 	/* get top codec from codec_intersection to put here... */
 	/* later on this should probably handle changing codec */
-	
-	/* Skip creating the actual "content" element for now, since we don't
-		get enough info in the Farsight callback */
-#if 0
-	transport = xmlnode_new_child(content, "transport");
-	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-tcp");
-	
-	candidate = xmlnode_new_child(transport, "candidate");
-	
-	g_snprintf(local_port, sizeof(local_port), "%d", native->port);
-	g_snprintf(remote_port, sizeof(remote_port), "%d", remote->port);
-	g_snprintf(prio, sizeof(prio), "%d",
-			   jabber_jingle_get_priority(native->type, 0));
-	g_snprintf(component, sizeof(component), "%d", native->component);
+
+	xmlnode_insert_child(content, jabber_jingle_session_create_description(sess));
 
-	xmlnode_set_attrib(candidate, "component", component);
-	xmlnode_set_attrib(candidate, "foundation", "1"); /* what about this? */
-	xmlnode_set_attrib(candidate, "generation", "0"); /* ? */
-	xmlnode_set_attrib(candidate, "ip", native->ip);
-	xmlnode_set_attrib(candidate, "network", "1"); /* ? */
-	xmlnode_set_attrib(candidate, "port", local_port);
-	xmlnode_set_attrib(candidate, "priority", prio); /* Is this correct? */
-	xmlnode_set_attrib(candidate, "protocol", 
-					   native->proto == FARSIGHT_NETWORK_PROTOCOL_UDP ? 
-					   "udp" : "tcp");
-	if (native->username)
-		xmlnode_set_attrib(candidate, "ufrag", native->username);
-	if (native->password)
-		xmlnode_set_attrib(candidate, "pwd", native->password);
-		
-	xmlnode_set_attrib(candidate, "type", 
-					   native->type == FARSIGHT_CANDIDATE_TYPE_LOCAL ? 
-					   "host" : 
-					   native->type == FARSIGHT_CANDIDATE_TYPE_DERIVED ? 
-					   "prflx" : 
-					   native->type == FARSIGHT_CANDIDATE_TYPE_RELAY ? 
-					   "relay" : NULL);
-	/* relay */
-	if (native->type == FARSIGHT_CANDIDATE_TYPE_RELAY) {
-		/* set rel-addr and rel-port? How? */
-	}
-	
-	xmlnode_set_attrib(candidate, "rem-addr", remote->ip);
-	xmlnode_set_attrib(candidate, "rem-port", remote_port);
-	
-	farsight_transport_list_destroy(native_candidates);
-	farsight_transport_list_destroy(remote_candidates);
-	
-#endif /* 0 */
-	
+	transport = xmlnode_new_child(content, "transport");
+	xmlnode_set_namespace(transport, "urn:xmpp:tmp:jingle:transports:ice-udp");
+	xmlnode_insert_child(transport,
+			     jabber_jingle_session_create_candidate_info(native_candidate,
+									 remote_candidate));
+
 	purple_debug_info("jingle", "End create content modify\n");
 	
 	return jingle;
@@ -556,24 +497,9 @@
 	xmlnode_set_attrib(content, "name", "audio-content");
 	xmlnode_set_attrib(content, "profile", "RTP/AVP");
 	
-    xmlnode_insert_child(jingle, description);
+    xmlnode_insert_child(content, description);
     
 	return jingle;
 }
 
-void
-jabber_jingle_session_set_remote_codecs(JingleSession *sess, GList *codecs)
-{
-    if (sess->remote_codecs) {
-        farsight_codec_list_destroy(sess->remote_codecs);
-    }
-    sess->remote_codecs = codecs;
-}
-
-GList *
-jabber_jingle_session_get_remote_codecs(const JingleSession *sess)
-{
-    return sess->remote_codecs;
-}
-
 #endif /* USE_FARSIGHT */