changeset 26013:5a774d0817d8

Wait for Farsight 2's codecs-ready property to be TRUE before using codecs. This will make codecs that need extra configuration, such as THEORA and H264, work consistently.
author Mike Ruprecht <maiku@soc.pidgin.im>
date Fri, 05 Sep 2008 02:18:15 +0000
parents ef161c8f14df
children bd598b606ca4 bb99ee66120e
files libpurple/media.c libpurple/media.h libpurple/protocols/jabber/jingle.c pidgin/gtkmedia.c
diffstat 4 files changed, 153 insertions(+), 83 deletions(-) [+]
line wrap: on
line diff
--- a/libpurple/media.c	Tue Sep 02 08:04:04 2008 +0000
+++ b/libpurple/media.c	Fri Sep 05 02:18:15 2008 +0000
@@ -106,6 +106,7 @@
 	NEW_CANDIDATE,
 	CANDIDATES_PREPARED,
 	CANDIDATE_PAIR,
+	CODECS_READY,
 	LAST_SIGNAL
 };
 static guint purple_media_signals[LAST_SIGNAL] = {0};
@@ -215,6 +216,10 @@
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 purple_smarshal_VOID__BOXED_BOXED,
 					 G_TYPE_NONE, 2, FS_TYPE_CANDIDATE, FS_TYPE_CANDIDATE);
+	purple_media_signals[CODECS_READY] = g_signal_new("codecs-ready", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 g_cclosure_marshal_VOID__STRING,
+					 G_TYPE_NONE, 1, G_TYPE_STRING);
 
 	g_type_class_add_private(klass, sizeof(PurpleMediaPrivate));
 }
@@ -663,6 +668,18 @@
 				
 			} else if (gst_structure_has_name(msg->structure,
 					"farsight-codecs-changed")) {
+				GList *sessions = g_hash_table_get_values(PURPLE_MEDIA(media)->priv->sessions);
+				FsSession *fssession = g_value_get_object(gst_structure_get_value(msg->structure, "session"));
+				for (; sessions; sessions = g_list_delete_link(sessions, sessions)) {
+					PurpleMediaSession *session = sessions->data;
+					if (session->session == fssession) {
+						g_signal_emit(session->media,
+								purple_media_signals[CODECS_READY],
+								0, &session->id);
+						g_list_free(sessions);
+						break;
+					}
+				}
 			}
 			break;
 		}
@@ -880,7 +897,7 @@
 	src = gst_element_factory_make(video_plugin, "purplevideosource");
 	gst_bin_add(GST_BIN(*sendbin), src);
 
-	tee = gst_element_factory_make("tee", NULL);
+	tee = gst_element_factory_make("tee", "purplevideosrctee");
 	gst_bin_add(GST_BIN(*sendbin), tee);
 	gst_element_link(src, tee);
 
@@ -893,13 +910,14 @@
 		g_object_set (G_OBJECT(src), "is-live", TRUE, NULL);
 	}
 
-	pad = gst_element_get_pad(queue, "src");
+	pad = gst_element_get_static_pad(queue, "src");
 	ghost = gst_ghost_pad_new("ghostsrc", pad);
+	gst_object_unref(pad);
 	gst_element_add_pad(*sendbin, ghost);
 
-	queue = gst_element_factory_make("queue", NULL);
+	queue = gst_element_factory_make("queue", "purplelocalvideoqueue");
 	gst_bin_add(GST_BIN(*sendbin), queue);
-	gst_element_link(tee, queue);
+	/* The queue is linked later, when the local video is ready to be shown */
 
 	local_sink = gst_element_factory_make("autovideosink", "purplelocalvideosink");
 	gst_bin_add(GST_BIN(*sendbin), local_sink);
@@ -1316,6 +1334,15 @@
 	return TRUE;
 }
 
+gboolean
+purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id)
+{
+	PurpleMediaSession *session = purple_media_get_session(media, sess_id);
+	gboolean ret;
+	g_object_get(session->session, "codecs-ready", &ret, NULL);
+	return ret;
+}
+
 void purple_media_mute(PurpleMedia *media, gboolean active)
 {
 	GList *sessions = g_hash_table_get_values(media->priv->sessions);
--- a/libpurple/media.h	Tue Sep 02 08:04:04 2008 +0000
+++ b/libpurple/media.h	Fri Sep 05 02:18:15 2008 +0000
@@ -472,6 +472,16 @@
 gboolean purple_media_set_send_codec(PurpleMedia *media, const gchar *sess_id, FsCodec *codec);
 
 /**
+ * Gets whether a session's codecs are ready to be used.
+ *
+ * @param media The media object to find the session in.
+ * @param sess_id The session id of the session to check.
+ *
+ * @return @c TRUE The codecs are ready, or @c FALSE otherwise.
+ */
+gboolean purple_media_codecs_ready(PurpleMedia *media, const gchar *sess_id);
+
+/**
  * Mutes or unmutes all the audio local audio sources.
  *
  * @param media The media object to mute or unmute
--- a/libpurple/protocols/jabber/jingle.c	Tue Sep 02 08:04:04 2008 +0000
+++ b/libpurple/protocols/jabber/jingle.c	Fri Sep 05 02:18:15 2008 +0000
@@ -814,6 +814,21 @@
 	jabber_iq_send(result);
 }
 #endif
+
+static void
+jabber_jingle_session_accept(JingleSession *session)
+{
+	if (jabber_jingle_session_get_state(session) == ACCEPTED &&
+			purple_media_candidates_prepared(
+				jabber_jingle_session_get_media(session),
+				jabber_jingle_session_get_remote_jid(session))) {
+		jabber_iq_send(jabber_jingle_session_create_session_accept(session));
+		
+		purple_debug_info("jingle", "Sent session accept.\n");
+		jabber_jingle_session_set_state(session, ACTIVE);
+	}
+}
+
 static void
 jabber_jingle_session_send_session_accept(JingleSession *session)
 {
@@ -843,13 +858,8 @@
 				jabber_jingle_session_content_get_name(jsc))));
 	}
 
-	if (purple_media_candidates_prepared(media, remote_jid)) {
-		jabber_iq_send(jabber_jingle_session_create_session_accept(session));
-
-		purple_debug_info("jingle", "Sent session accept.\n");
-		jabber_jingle_session_set_state(session, ACTIVE);
-	} else
-		jabber_jingle_session_set_state(session, ACCEPTED);
+	jabber_jingle_session_set_state(session, ACCEPTED);
+	jabber_jingle_session_accept(session);
 }
 
 static void
@@ -942,14 +952,90 @@
 						    FsCandidate *remote_candidate,
 						    JingleSession *session)
 {
-	if (!jabber_jingle_session_is_initiator(session) &&
-			jabber_jingle_session_get_state(session) == ACCEPTED &&
-			purple_media_candidates_prepared(media,
-				jabber_jingle_session_get_remote_jid(session))) {
-		jabber_iq_send(jabber_jingle_session_create_session_accept(session));
-		
-		purple_debug_info("jingle", "Sent session accept.\n");
-		jabber_jingle_session_set_state(session, ACTIVE);
+	if (!jabber_jingle_session_is_initiator(session)) {
+		jabber_jingle_session_accept(session);
+	}
+}
+
+static void
+jabber_jingle_session_initiate_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
+{
+	const char *from = xmlnode_get_attrib(packet, "from");
+	JingleSession *session = jabber_jingle_session_find_by_jid(js, from);
+	PurpleMedia *media;
+	GList *contents;
+
+	if (!session) {
+		/* respond with an error here */
+		purple_debug_error("jingle", "Received session-initiate ack"
+				   " to nonexistent session\n");
+		return;
+	}
+
+	media = session->media;
+
+	if (!strcmp(xmlnode_get_attrib(packet, "type"), "error")) {
+		purple_media_got_hangup(media);
+		return;
+	}
+
+	/* catch errors */
+	if (xmlnode_get_child(packet, "error")) {
+		purple_media_got_hangup(media);
+		return;
+	}
+
+	/* create transport-info packages */
+	contents = jabber_jingle_session_get_contents(session);
+	for (; contents; contents = contents->next) {
+		JingleSessionContent *jsc = contents->data;
+		GList *candidates = purple_media_get_local_candidates(
+				jabber_jingle_session_get_media(session),
+				jabber_jingle_session_content_get_name(jsc),
+				jabber_jingle_session_get_remote_jid(session));
+		purple_debug_info("jingle",
+				  "jabber_session_candidates_prepared: %d candidates\n",
+				  g_list_length(candidates));
+		for (; candidates; candidates = candidates->next) {
+			FsCandidate *candidate = candidates->data;
+			JabberIq *result = jabber_jingle_session_create_transport_info(jsc,
+					candidate);
+			jabber_iq_send(result);
+		}
+		fs_candidate_list_destroy(candidates);
+	}
+
+	jabber_jingle_session_set_state(session, GOT_ACK);
+}
+
+static void
+jabber_jingle_session_codecs_ready_cb(PurpleMedia *media,
+				      const gchar *sess_id,
+				      JingleSession *session)
+{
+	GList *contents = jabber_jingle_session_get_contents(session);
+	for (; contents; contents = g_list_delete_link(contents, contents)) {
+		JingleSessionContent *jsc = contents->data;
+		if (!purple_media_codecs_ready(media,
+				jabber_jingle_session_content_get_name(jsc))) {
+			break;
+		}
+	}
+
+	if (contents != NULL)
+		g_list_free(contents);
+	else if (jabber_jingle_session_is_initiator(session)
+			&& jabber_jingle_session_get_state(session) == PENDING) {
+		JabberIq *request;
+
+		/* create request */
+		request = jabber_jingle_session_create_session_initiate(session);
+		jabber_iq_set_callback(request, jabber_jingle_session_initiate_result_cb, NULL);
+
+		/* send request to other part */	
+		jabber_iq_send(request);
+	} else {
+		jabber_jingle_session_accept(session);
 	}
 }
 
@@ -1027,69 +1113,19 @@
 				 G_CALLBACK(jabber_jingle_session_new_candidate_cb), session);
 	g_signal_connect(G_OBJECT(media), "candidate-pair", 
 				 G_CALLBACK(jabber_jingle_session_candidate_pair_established_cb), session);
+	g_signal_connect(G_OBJECT(media), "codecs-ready", 
+				 G_CALLBACK(jabber_jingle_session_codecs_ready_cb), session);
 
 	purple_media_ready(media);
 
 	return TRUE;
 }
 
-static void
-jabber_jingle_session_initiate_result_cb(JabberStream *js, xmlnode *packet, gpointer data)
-{
-	const char *from = xmlnode_get_attrib(packet, "from");
-	JingleSession *session = jabber_jingle_session_find_by_jid(js, from);
-	PurpleMedia *media;
-	GList *contents;
-
-	if (!session) {
-		/* respond with an error here */
-		purple_debug_error("jingle", "Received session-initiate ack"
-				   " to nonexistent session\n");
-		return;
-	}
-
-	media = session->media;
-
-	if (!strcmp(xmlnode_get_attrib(packet, "type"), "error")) {
-		purple_media_got_hangup(media);
-		return;
-	}
-
-	/* catch errors */
-	if (xmlnode_get_child(packet, "error")) {
-		purple_media_got_hangup(media);
-		return;
-	}
-
-	/* create transport-info packages */
-	contents = jabber_jingle_session_get_contents(session);
-	for (; contents; contents = contents->next) {
-		JingleSessionContent *jsc = contents->data;
-		GList *candidates = purple_media_get_local_candidates(
-				jabber_jingle_session_get_media(session),
-				jabber_jingle_session_content_get_name(jsc),
-				jabber_jingle_session_get_remote_jid(session));
-		purple_debug_info("jingle",
-				  "jabber_session_candidates_prepared: %d candidates\n",
-				  g_list_length(candidates));
-		for (; candidates; candidates = candidates->next) {
-			FsCandidate *candidate = candidates->data;
-			JabberIq *result = jabber_jingle_session_create_transport_info(jsc,
-					candidate);
-			jabber_iq_send(result);
-		}
-		fs_candidate_list_destroy(candidates);
-	}
-
-	jabber_jingle_session_set_state(session, GOT_ACK);
-}
-
 PurpleMedia *
 jabber_jingle_session_initiate_media(JabberStream *js, const char *who, 
 				     PurpleMediaSessionType type)
 {
 	/* create content negotiation */
-	JabberIq *request;
 	JingleSession *session;
 	JabberBuddy *jb;
 	JabberBuddyResource *jbr;
@@ -1129,13 +1165,6 @@
 	g_free(jid);
 	g_free(me);
 
-	/* create request */
-	request = jabber_jingle_session_create_session_initiate(session);
-	jabber_iq_set_callback(request, jabber_jingle_session_initiate_result_cb, NULL);
-
-	/* send request to other part */	
-	jabber_iq_send(request);
-
 	return session->media;
 }
 
--- a/pidgin/gtkmedia.c	Tue Sep 02 08:04:04 2008 +0000
+++ b/pidgin/gtkmedia.c	Fri Sep 05 02:18:15 2008 +0000
@@ -314,18 +314,18 @@
 			if (!audiosendbin && (type & PURPLE_MEDIA_SEND_AUDIO)) {
 				purple_media_audio_init_src(&audiosendbin, &audiosendlevel);
 				purple_media_set_src(media, sessions->data, audiosendbin);
-				gst_element_set_state(audiosendbin, GST_STATE_READY);
+				gst_element_set_state(audiosendbin, GST_STATE_PLAYING);
 			}
 			if (!audiorecvbin && (type & PURPLE_MEDIA_RECV_AUDIO)) {
 				purple_media_audio_init_recv(&audiorecvbin, &audiorecvlevel);
 				purple_media_set_sink(media, sessions->data, audiorecvbin);
 				gst_element_set_state(audiorecvbin, GST_STATE_READY);
 			}
-		} else if (purple_media_get_session_type(media, sessions->data) & PURPLE_MEDIA_VIDEO) {
+		} else if (type & PURPLE_MEDIA_VIDEO) {
 			if (!videosendbin && (type & PURPLE_MEDIA_SEND_VIDEO)) {
 				purple_media_video_init_src(&videosendbin);
 				purple_media_set_src(media, sessions->data, videosendbin);
-				gst_element_set_state(videosendbin, GST_STATE_READY);
+				gst_element_set_state(videosendbin, GST_STATE_PLAYING);
 			}
 			if (!videorecvbin && (type & PURPLE_MEDIA_RECV_VIDEO)) {
 				purple_media_video_init_recv(&videorecvbin);
@@ -401,6 +401,7 @@
 	if (videosendbin) {
 		GtkWidget *aspect;
 		GtkWidget *local_video;
+		GstElement *tee, *queue;
 
 		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);
@@ -414,7 +415,11 @@
 
 		gtkmedia->priv->local_video = local_video;
 
-		gst_element_set_state(videosendbin, GST_STATE_PLAYING);
+		tee = gst_bin_get_by_name(GST_BIN(videosendbin), "purplevideosrctee");
+		queue = gst_bin_get_by_name(GST_BIN(videosendbin), "purplelocalvideoqueue");
+		gst_element_link(tee, queue);
+		gst_object_unref(tee);
+		gst_object_unref(queue);
 	}
 
 	if (audiorecvbin) {
@@ -435,7 +440,6 @@
 		gtk_box_pack_end(GTK_BOX(send_widget),
 				   gtkmedia->priv->send_progress, FALSE, FALSE, 0);
 		gtk_widget_show(gtkmedia->priv->send_progress);
-		gst_element_set_state(audiosendbin, GST_STATE_PLAYING);
 
 		gtk_widget_show(gtkmedia->priv->mute);
 	}