diff libpurple/media.c @ 29298:fb99a0067812

propagate from branch 'im.pidgin.pidgin' (head 70d69397ed952b26b453423c381c70d6783eb66d) to branch 'im.pidgin.cpw.attention_ui' (head 1cf0dea282a0d0e4aeac4770e0150d6d0c10830a)
author Marcus Lundblad <ml@update.uu.se>
date Thu, 13 Aug 2009 17:42:44 +0000
parents f77978e6968e
children 98d8c11e4937 8c991e09efcb
line wrap: on
line diff
--- a/libpurple/media.c	Tue Jul 28 20:52:33 2009 +0000
+++ b/libpurple/media.c	Thu Aug 13 17:42:44 2009 +0000
@@ -94,6 +94,8 @@
 	FsStream *stream;
 	GstElement *src;
 	GstElement *tee;
+	GstElement *volume;
+	GstElement *level;
 
 	GList *local_candidates;
 	GList *remote_candidates;
@@ -157,9 +159,9 @@
 
 enum {
 	S_ERROR,
-	ACCEPTED,
 	CANDIDATES_PREPARED,
 	CODECS_CHANGED,
+	LEVEL,
 	NEW_CANDIDATE,
 	STATE_CHANGED,
 	STREAM_INFO,
@@ -272,6 +274,10 @@
 					"PURPLE_MEDIA_INFO_MUTE", "mute" },
 			{ PURPLE_MEDIA_INFO_UNMUTE,
 					"PURPLE_MEDIA_INFO_UNMUTE", "unmute" },
+			{ PURPLE_MEDIA_INFO_PAUSE,
+					"PURPLE_MEDIA_INFO_PAUSE", "pause" },
+			{ PURPLE_MEDIA_INFO_UNPAUSE,
+					"PURPLE_MEDIA_INFO_UNPAUSE", "unpause" },
 			{ PURPLE_MEDIA_INFO_HOLD,
 					"PURPLE_MEDIA_INFO_HOLD", "hold" },
 			{ PURPLE_MEDIA_INFO_UNHOLD,
@@ -332,10 +338,6 @@
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 g_cclosure_marshal_VOID__STRING,
 					 G_TYPE_NONE, 1, G_TYPE_STRING);
-	purple_media_signals[ACCEPTED] = g_signal_new("accepted", G_TYPE_FROM_CLASS(klass),
-					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
-					 purple_smarshal_VOID__STRING_STRING,
-					 G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
 	purple_media_signals[CANDIDATES_PREPARED] = g_signal_new("candidates-prepared", G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 purple_smarshal_VOID__STRING_STRING,
@@ -345,6 +347,11 @@
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 g_cclosure_marshal_VOID__STRING,
 					 G_TYPE_NONE, 1, G_TYPE_STRING);
+	purple_media_signals[LEVEL] = g_signal_new("level", G_TYPE_FROM_CLASS(klass),
+					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
+					 purple_smarshal_VOID__STRING_STRING_DOUBLE,
+					 G_TYPE_NONE, 3, G_TYPE_STRING,
+					 G_TYPE_STRING, G_TYPE_DOUBLE);
 	purple_media_signals[NEW_CANDIDATE] = g_signal_new("new-candidate", G_TYPE_FROM_CLASS(klass),
 					 G_SIGNAL_RUN_LAST, 0, NULL, NULL,
 					 purple_smarshal_VOID__POINTER_POINTER_OBJECT,
@@ -1899,11 +1906,31 @@
 		gst_element_add_pad(session->media->priv->confbin, ghost);
 	}
 
+	gst_element_set_state(session->tee, GST_STATE_PLAYING);
 	gst_element_link(session->src, session->media->priv->confbin);
-	gst_element_set_state(session->tee, GST_STATE_PLAYING);
 
 	g_object_get(session->session, "sink-pad", &sinkpad, NULL);
-	srcpad = gst_element_get_request_pad(session->tee, "src%d");
+	if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
+		gchar *name = g_strdup_printf("volume_%s", session->id);
+		GstElement *level;
+		GstElement *volume = gst_element_factory_make("volume", name);
+		double input_volume = purple_prefs_get_int(
+				"/purple/media/audio/volume/input")/10.0;
+		g_free(name);
+		name = g_strdup_printf("sendlevel_%s", session->id);
+		level = gst_element_factory_make("level", name);
+		g_free(name);
+		gst_bin_add(GST_BIN(session->media->priv->confbin), volume);
+		gst_bin_add(GST_BIN(session->media->priv->confbin), level);
+		gst_element_set_state(level, GST_STATE_PLAYING);
+		gst_element_set_state(volume, GST_STATE_PLAYING);
+		gst_element_link(volume, level);
+		gst_element_link(session->tee, volume);
+		srcpad = gst_element_get_static_pad(level, "src");
+		g_object_set(volume, "volume", input_volume, NULL);
+	} else {
+		srcpad = gst_element_get_request_pad(session->tee, "src%d");
+	}
 	purple_debug_info("media", "connecting pad: %s\n", 
 			  gst_pad_link(srcpad, sinkpad) == GST_PAD_LINK_OK
 			  ? "success" : "failure");
@@ -1960,6 +1987,55 @@
 {
 	switch(GST_MESSAGE_TYPE(msg)) {
 		case GST_MESSAGE_ELEMENT: {
+			if (g_signal_has_handler_pending(media,
+					purple_media_signals[LEVEL], 0, FALSE)
+					&& gst_structure_has_name(
+					gst_message_get_structure(msg), "level")) {
+				GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg));
+				gchar *name;
+				gchar *participant = NULL;
+				PurpleMediaSession *session = NULL;
+				gdouble rms_db;
+				gdouble percent;
+				const GValue *list;
+				const GValue *value;
+
+				if (!PURPLE_IS_MEDIA(media) ||
+						GST_ELEMENT_PARENT(src) !=
+						media->priv->confbin)
+					break;
+
+				name = gst_element_get_name(src);
+				if (!strncmp(name, "sendlevel_", 10)) {
+					session = purple_media_get_session(
+							media, name+10);
+				} else {
+					GList *iter = media->priv->streams;
+					for (; iter; iter = g_list_next(iter)) {
+						PurpleMediaStream *stream = iter->data;
+						if (stream->level == src) {
+							session = stream->session;
+							participant = stream->participant;
+							break;
+						}
+					}
+				}
+				g_free(name);
+				if (!session)
+					break;
+
+				list = gst_structure_get_value(
+						gst_message_get_structure(msg), "rms");
+				value = gst_value_list_get_value(list, 0);
+				rms_db = g_value_get_double(value);
+				percent = pow(10, rms_db / 20) * 5;
+				if(percent > 1.0)
+					percent = 1.0;
+
+				g_signal_emit(media, purple_media_signals[LEVEL],
+						0, session->id, participant, percent);
+				break;
+			}
 			if (!FS_IS_CONFERENCE(GST_MESSAGE_SRC(msg)) ||
 					!PURPLE_IS_MEDIA(media) ||
 					media->priv->conference !=
@@ -2155,9 +2231,6 @@
 					stream->session->type), NULL);
 			stream->accepted = TRUE;
 		}
-
-		g_signal_emit(media, purple_media_signals[ACCEPTED],
-				0, NULL, NULL);
 	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_MUTE ||
 			type == PURPLE_MEDIA_INFO_UNMUTE)) {
 		GList *sessions;
@@ -2180,12 +2253,30 @@
 				sessions, sessions)) {
 			PurpleMediaSession *session = sessions->data;
 			if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
+				gchar *name = g_strdup_printf("volume_%s",
+						session->id);
 				GstElement *volume = gst_bin_get_by_name(
-						GST_BIN(session->src),
-						"purpleaudioinputvolume");
+						GST_BIN(session->media->
+						priv->confbin), name);
+				g_free(name);
 				g_object_set(volume, "mute", active, NULL);
 			}
 		}
+	} else if (local == TRUE && (type == PURPLE_MEDIA_INFO_PAUSE ||
+			type == PURPLE_MEDIA_INFO_UNPAUSE)) {
+		gboolean active = (type == PURPLE_MEDIA_INFO_PAUSE);
+		GList *streams = purple_media_get_streams(media,
+				session_id, participant);
+		for (; streams; streams = g_list_delete_link(streams, streams)) {
+			PurpleMediaStream *stream = streams->data;
+			if (stream->session->type & PURPLE_MEDIA_SEND_VIDEO) {
+				g_object_set(stream->stream, "direction",
+						purple_media_to_fs_stream_direction(
+						stream->session->type & ((active) ?
+						~PURPLE_MEDIA_SEND_VIDEO :
+						PURPLE_MEDIA_VIDEO)), NULL);
+			}
+		}
 	}
 
 	g_signal_emit(media, purple_media_signals[STREAM_INFO],
@@ -2346,11 +2437,21 @@
 		GstElement *sink = NULL;
 
 		if (codec->media_type == FS_MEDIA_TYPE_AUDIO) {
+			GstElement *queue = NULL;
+			double output_volume = purple_prefs_get_int(
+					"/purple/media/audio/volume/output")/10.0;
 			/*
 			 * Should this instead be:
 			 *  audioconvert ! audioresample ! liveadder !
 			 *   audioresample ! audioconvert ! realsink
 			 */
+			queue = gst_element_factory_make("queue", NULL);
+			stream->volume = gst_element_factory_make(
+					"volume", NULL);
+			g_object_set(stream->volume, "volume",
+					output_volume, NULL);
+			stream->level = gst_element_factory_make(
+					"level", NULL);
 			stream->src = gst_element_factory_make(
 					"liveadder", NULL);
 			sink = purple_media_manager_get_element(priv->manager,
@@ -2358,19 +2459,32 @@
 					stream->session->media,
 					stream->session->id,
 					stream->participant);
+			gst_bin_add(GST_BIN(priv->confbin), queue);
+			gst_bin_add(GST_BIN(priv->confbin), stream->volume);
+			gst_bin_add(GST_BIN(priv->confbin), stream->level);
+			gst_bin_add(GST_BIN(priv->confbin), sink);
+			gst_element_set_state(sink, GST_STATE_PLAYING);
+			gst_element_set_state(stream->level, GST_STATE_PLAYING);
+			gst_element_set_state(stream->volume, GST_STATE_PLAYING);
+			gst_element_set_state(queue, GST_STATE_PLAYING);
+			gst_element_link(stream->level, sink);
+			gst_element_link(stream->volume, stream->level);
+			gst_element_link(queue, stream->volume);
+			sink = queue;
 		} else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) {
 			stream->src = gst_element_factory_make(
 					"fsfunnel", NULL);
 			sink = gst_element_factory_make(
 					"fakesink", NULL);
 			g_object_set(G_OBJECT(sink), "async", FALSE, NULL);
+			gst_bin_add(GST_BIN(priv->confbin), sink);
+			gst_element_set_state(sink, GST_STATE_PLAYING);
 		}
 		stream->tee = gst_element_factory_make("tee", NULL);
 		gst_bin_add_many(GST_BIN(priv->confbin),
-				stream->src, stream->tee, sink, NULL);
-		gst_element_sync_state_with_parent(sink);
-		gst_element_sync_state_with_parent(stream->tee);
-		gst_element_sync_state_with_parent(stream->src);
+				stream->src, stream->tee, NULL);
+		gst_element_set_state(stream->tee, GST_STATE_PLAYING);
+		gst_element_set_state(stream->src, GST_STATE_PLAYING);
 		gst_element_link_many(stream->src, stream->tee, sink, NULL);
 	}
 
@@ -2504,26 +2618,27 @@
 				0, PURPLE_MEDIA_STATE_NEW,
 				session->id, NULL);
 
-		session_type = purple_media_from_fs(media_type,
-				FS_DIRECTION_SEND);
-		src = purple_media_manager_get_element(
-				media->priv->manager, session_type,
-				media, session->id, who);
-		if (!GST_IS_ELEMENT(src)) {
-			purple_debug_error("media",
-					"Error creating src for session %s\n",
-					session->id);
-			purple_media_end(media, session->id, NULL);
-			return FALSE;
+		if (type_direction & FS_DIRECTION_SEND) {
+			session_type = purple_media_from_fs(media_type,
+					FS_DIRECTION_SEND);
+			src = purple_media_manager_get_element(
+					media->priv->manager, session_type,
+					media, session->id, who);
+			if (!GST_IS_ELEMENT(src)) {
+				purple_debug_error("media",
+						"Error creating src for session %s\n",
+						session->id);
+				purple_media_end(media, session->id, NULL);
+				return FALSE;
+			}
+
+			purple_media_set_src(media, session->id, src);
+			gst_element_set_state(session->src, GST_STATE_PLAYING);
+			purple_media_manager_create_output_window(
+					media->priv->manager,
+					session->media,
+					session->id, NULL);
 		}
-
-		purple_media_set_src(media, session->id, src);
-		gst_element_set_state(session->src, GST_STATE_PLAYING);
-
-		purple_media_manager_create_output_window(
-				media->priv->manager,
-				session->media,
-				session->id, NULL);
 	}
 
 	if (!(participant = purple_media_add_participant(media, who))) {
@@ -2889,14 +3004,23 @@
 
 		if (session == NULL)
 			return FALSE;
-
-		g_object_get(session->session, "codecs-ready", &ret, NULL);
+		if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
+				PURPLE_MEDIA_SEND_VIDEO))
+			g_object_get(session->session,
+					"codecs-ready", &ret, NULL);
+		else
+			ret = TRUE;
 	} else {
 		GList *values = g_hash_table_get_values(media->priv->sessions);
 		for (; values; values = g_list_delete_link(values, values)) {
 			PurpleMediaSession *session = values->data;
-			g_object_get(session->session,
-					"codecs-ready", &ret, NULL);
+			if (session->type & (PURPLE_MEDIA_SEND_AUDIO |
+					PURPLE_MEDIA_SEND_VIDEO))
+				g_object_get(session->session,
+						"codecs-ready", &ret, NULL);
+			else
+				ret = TRUE;
+
 			if (ret == FALSE)
 				break;
 		}
@@ -2983,6 +3107,8 @@
 
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
 
+	purple_prefs_set_int("/purple/media/audio/volume/input", level);
+
 	if (session_id == NULL)
 		sessions = g_hash_table_get_values(media->priv->sessions);
 	else
@@ -2993,10 +3119,13 @@
 		PurpleMediaSession *session = sessions->data;
 
 		if (session->type & PURPLE_MEDIA_SEND_AUDIO) {
+			gchar *name = g_strdup_printf("volume_%s",
+					session->id);
 			GstElement *volume = gst_bin_get_by_name(
-					GST_BIN(session->src),
-					"purpleaudioinputvolume");
-			g_object_set(volume, "volume", level, NULL);
+					GST_BIN(session->media->priv->confbin),
+					name);
+			g_free(name);
+			g_object_set(volume, "volume", level/10.0, NULL);
 		}
 	}
 #endif
@@ -3011,34 +3140,17 @@
 
 	g_return_if_fail(PURPLE_IS_MEDIA(media));
 
+	purple_prefs_set_int("/purple/media/audio/volume/output", level);
+
 	streams = purple_media_get_streams(media,
 			session_id, participant);
 
 	for (; streams; streams = g_list_delete_link(streams, streams)) {
 		PurpleMediaStream *stream = streams->data;
 
-		if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO) {
-			GstElement *tee = stream->tee;
-			GstIterator *iter = gst_element_iterate_src_pads(tee);
-			GstPad *sinkpad;
-			while (gst_iterator_next(iter, (gpointer)&sinkpad)
-					 == GST_ITERATOR_OK) {
-				GstPad *peer = gst_pad_get_peer(sinkpad);
-				GstElement *volume;
-
-				if (peer == NULL) {
-					gst_object_unref(sinkpad);
-					continue;
-				}
-					
-				volume = gst_bin_get_by_name(GST_BIN(
-						GST_OBJECT_PARENT(peer)),
-						"purpleaudiooutputvolume");
-				g_object_set(volume, "volume", level, NULL);
-				gst_object_unref(peer);
-				gst_object_unref(sinkpad);
-			}
-			gst_iterator_free(iter);
+		if (stream->session->type & PURPLE_MEDIA_RECV_AUDIO
+				&& GST_IS_ELEMENT(stream->volume)) {
+			g_object_set(stream->volume, "volume", level/10.0, NULL);
 		}
 	}
 #endif