rhythmbox r6175 - in trunk: . backends/gstreamer



Author: jmatthew
Date: Tue Mar  3 02:00:15 2009
New Revision: 6175
URL: http://svn.gnome.org/viewvc/rhythmbox?rev=6175&view=rev

Log:
2009-03-03  Jonathan Matthew  <jonathan d14n org>

	based on patch by:  Bastien Nocera  <hadess hadess net>

	* backends/gstreamer/Makefile.am:
	* backends/gstreamer/rb-player-gst-helper.c:
	(rb_player_gst_try_audio_sink), (find_property_element),
	(rb_player_gst_find_element_with_property):
	* backends/gstreamer/rb-player-gst-helper.h:
	Add helper functions to testing audio sinks (making sure they can at
	least go to READY state) and for examining sink bins to find elements
	that provide a particular property.

	* backends/gstreamer/rb-player-gst-xfade.c:
	(rb_player_gst_xfade_dispose), (start_sink_locked), (stop_sink),
	(create_sink), (rb_player_gst_xfade_set_volume):
	* backends/gstreamer/rb-player-gst.c: (rb_player_gst_finalize),
	(rb_player_gst_construct), (rb_player_gst_close),
	(find_volume_handler), (rb_player_gst_set_replaygain),
	(rb_player_gst_set_volume):
	If the audio sink provides a 'volume' property, use that instead of
	a volume element (or the 'volume' property on playbin) for setting
	output volume.  The practical effect is that when using pulseaudio,
	the volume control uses the pulseaudio stream volume, which has much
	lower latency.  Fixes #571606.


Added:
   trunk/backends/gstreamer/rb-player-gst-helper.c
   trunk/backends/gstreamer/rb-player-gst-helper.h
Modified:
   trunk/ChangeLog
   trunk/backends/gstreamer/Makefile.am
   trunk/backends/gstreamer/rb-player-gst-xfade.c
   trunk/backends/gstreamer/rb-player-gst.c

Modified: trunk/backends/gstreamer/Makefile.am
==============================================================================
--- trunk/backends/gstreamer/Makefile.am	(original)
+++ trunk/backends/gstreamer/Makefile.am	Tue Mar  3 02:00:15 2009
@@ -5,6 +5,8 @@
 librbbackendsgstreamer_la_SOURCES =			\
 	rb-encoder-gst.h				\
 	rb-encoder-gst.c				\
+	rb-player-gst-helper.c				\
+	rb-player-gst-helper.h				\
 	rb-player-gst.h					\
 	rb-player-gst.c					\
 	rb-player-gst-xfade.h				\

Added: trunk/backends/gstreamer/rb-player-gst-helper.c
==============================================================================
--- (empty file)
+++ trunk/backends/gstreamer/rb-player-gst-helper.c	Tue Mar  3 02:00:15 2009
@@ -0,0 +1,127 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2009 Bastien Nocera <hadess hadess net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The Rhythmbox authors hereby grant permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <gst/gst.h>
+
+#include <rb-player-gst-helper.h>
+#include <rb-debug.h>
+
+GstElement *
+rb_player_gst_try_audio_sink (const char *plugin_name, const char *name)
+{
+	GstElement *audio_sink;
+
+	audio_sink = gst_element_factory_make (plugin_name, name);
+	if (audio_sink == NULL)
+		return NULL;
+
+	/* Assume the fakesink will work */
+	if (g_str_equal (plugin_name, "fakesink")) {
+		g_object_set (audio_sink, "sync", TRUE, NULL);
+		return audio_sink;
+	}
+
+	if (audio_sink) {
+		GstStateChangeReturn ret;
+		GstBus *bus;
+
+		/* use the 'music and movies' profile for gconfaudiosink */
+		if (strcmp (plugin_name, "gconfaudiosink") == 0 &&
+		    g_object_class_find_property (G_OBJECT_GET_CLASS (audio_sink), "profile")) {
+			rb_debug ("setting profile property on gconfaudiosink");
+			g_object_set (audio_sink, "profile", 1, NULL);
+		}
+
+		/* need to set bus explicitly as it's not in a bin yet and
+		 * we need one to catch error messages */
+		bus = gst_bus_new ();
+		gst_element_set_bus (audio_sink, bus);
+
+		/* state change NULL => READY should always be synchronous */
+		ret = gst_element_set_state (audio_sink, GST_STATE_READY);
+		gst_element_set_bus (audio_sink, NULL);
+
+		if (ret == GST_STATE_CHANGE_FAILURE) {
+			/* doesn't work, drop this audio sink */
+			rb_debug ("audio sink %s failed to change to READY state", plugin_name);
+			gst_element_set_state (audio_sink, GST_STATE_NULL);
+			gst_object_unref (audio_sink);
+			audio_sink = NULL;
+		} else {
+			rb_debug ("audio sink %s changed to READY state successfully", plugin_name);
+		}
+		gst_object_unref (bus);
+	}
+
+	return audio_sink;
+}
+
+static gint
+find_property_element (GstElement *element, const char *property)
+{
+	gint res = 1;
+	char *name = gst_element_get_name (element);
+
+	if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), property) != NULL) {
+		rb_debug ("found property \"%s\" on element %s", property, name);
+		return 0;
+	} else {
+		rb_debug ("didn't find property \"%s\" on element %s", property, name);
+		g_object_unref (element);
+	}
+		
+	g_free (name);
+	return res;
+}
+
+GstElement *
+rb_player_gst_find_element_with_property (GstElement *element, const char *property)
+{
+	GstIterator *iter;
+	GstElement *result;
+
+	if (GST_IS_BIN (element) == FALSE) {
+		if (g_object_class_find_property (G_OBJECT_GET_CLASS (element),
+						  property) != NULL) {
+			return g_object_ref (element);
+		}
+		return NULL;
+	}
+
+	rb_debug ("iterating bin looking for property %s", property);
+	iter = gst_bin_iterate_recurse (GST_BIN (element));
+	result = gst_iterator_find_custom (iter,
+					   (GCompareFunc) find_property_element,
+					   (gpointer) property);
+	gst_iterator_free (iter);
+	return result;
+}

Added: trunk/backends/gstreamer/rb-player-gst-helper.h
==============================================================================
--- (empty file)
+++ trunk/backends/gstreamer/rb-player-gst-helper.h	Tue Mar  3 02:00:15 2009
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ *  Copyright (C) 2009 Bastien Nocera <hadess hadess net>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The Rhythmbox authors hereby grant permission for non-GPL compatible
+ *  GStreamer plugins to be used and distributed together with GStreamer
+ *  and Rhythmbox. This permission is above and beyond the permissions granted
+ *  by the GPL license by which Rhythmbox is covered. If you modify this code
+ *  you may extend this exception to your version of the code, but you are not
+ *  obligated to do so. If you do not wish to do so, delete this exception
+ *  statement from your version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA.
+ *
+ */
+
+#ifndef __RB_PLAYER_GST_HELPER_H
+#define __RB_PLAYER_GST_HELPER_H
+
+GstElement *rb_player_gst_try_audio_sink (const char *plugin_name, const char *name);
+
+GstElement *rb_player_gst_find_element_with_property (GstElement *element, const char *property);
+
+#endif /* __RB_PLAYER_GST_HELPER_H */

Modified: trunk/backends/gstreamer/rb-player-gst-xfade.c
==============================================================================
--- trunk/backends/gstreamer/rb-player-gst-xfade.c	(original)
+++ trunk/backends/gstreamer/rb-player-gst-xfade.c	Tue Mar  3 02:00:15 2009
@@ -169,6 +169,7 @@
 #include "rb-marshal.h"
 #include "rb-player-gst-tee.h"
 #include "rb-player-gst-filter.h"
+#include "rb-player-gst-helper.h"
 
 static void rb_player_init (RBPlayerIface *iface);
 static void rb_player_gst_tee_init (RBPlayerGstTeeIface *iface);
@@ -357,6 +358,7 @@
 	GstElement *sink;
 	GstElement *tee;
 	GstElement *filterbin;
+	GstElement *volume_handler;
 	enum {
 		SINK_NULL,
 		SINK_STOPPED,
@@ -742,6 +744,11 @@
 	player->priv->streams = NULL;
 	g_static_rec_mutex_unlock (&player->priv->stream_list_lock);
 
+	if (player->priv->volume_handler) {
+		g_object_unref (player->priv->volume_handler);
+		player->priv->volume_handler = NULL;
+	}
+
 	g_static_rec_mutex_lock (&player->priv->sink_lock);
 	stop_sink (player);
 	g_static_rec_mutex_unlock (&player->priv->sink_lock);
@@ -2828,6 +2835,16 @@
 			gst_message_unref (message);
 		}
 		gst_object_unref (bus);
+	
+		/* if the sink provides a 'volume' property, use that to control output volume */
+		player->priv->volume_handler = rb_player_gst_find_element_with_property (player->priv->sink, "volume");
+		if (player->priv->volume_handler == NULL) {
+			rb_debug ("sink doesn't provide volume control, using volume element");
+			player->priv->volume_handler = g_object_ref (player->priv->volume);
+		}
+
+		g_object_set (player->priv->volume_handler, "volume", player->priv->cur_volume, NULL);
+
 
 		sr = gst_element_set_state (player->priv->silencebin, GST_STATE_PLAYING);
 		if (sr == GST_STATE_CHANGE_FAILURE) {
@@ -2932,6 +2949,11 @@
 			rb_debug ("couldn't set audio sink to NULL state");
 		}
 
+		if (player->priv->volume_handler) {
+			g_object_unref (player->priv->volume_handler);
+			player->priv->volume_handler = NULL;
+		}
+
 		player->priv->sink_state = SINK_STOPPED;
 		break;
 
@@ -3012,11 +3034,9 @@
 		return FALSE;
 	}
 
-	g_object_set (player->priv->volume, "volume", player->priv->cur_volume, NULL);
-
-	player->priv->sink = gst_element_factory_make ("gconfaudiosink", NULL);
+	player->priv->sink = rb_player_gst_try_audio_sink ("gconfaudiosink", NULL);
 	if (player->priv->sink == NULL) {
-		player->priv->sink = gst_element_factory_make ("autoaudiosink", NULL);
+		player->priv->sink = rb_player_gst_try_audio_sink ("autoaudiosink", NULL);
 		if (player->priv->sink == NULL) {
 			g_set_error (error,
 				     RB_PLAYER_ERROR,
@@ -3024,10 +3044,6 @@
 				     _("Failed to create audio output element; check your installation"));
 			return FALSE;
 		}
-	} else {
-		/* set the profile property on the gconfaudiosink to "music and movies" */
-		if (g_object_class_find_property (G_OBJECT_GET_CLASS (player->priv->sink), "profile"))
-			g_object_set (player->priv->sink, "profile", 1, NULL);
 	}
 
 	g_object_set (player->priv->capsfilter, "caps", caps, NULL);
@@ -3671,11 +3687,11 @@
 {
 	RBPlayerGstXFade *player = RB_PLAYER_GST_XFADE (iplayer);
 
-	if (player->priv->volume != NULL) {
+	if (player->priv->volume_handler != NULL) {
 		gdouble v = (gdouble)volume;
 
 		/* maybe use a controller here for smoother changes? */
-		g_object_set (player->priv->volume, "volume", v, NULL);
+		g_object_set (player->priv->volume_handler, "volume", v, NULL);
 	}
 	player->priv->cur_volume = volume;
 }

Modified: trunk/backends/gstreamer/rb-player-gst.c
==============================================================================
--- trunk/backends/gstreamer/rb-player-gst.c	(original)
+++ trunk/backends/gstreamer/rb-player-gst.c	Tue Mar  3 02:00:15 2009
@@ -48,6 +48,7 @@
 #include "rb-player-gst-tee.h"
 /*#include "rb-player-gst-data-tee.h"*/
 #include "rb-player-gst.h"
+#include "rb-player-gst-helper.h"
 
 static void rb_player_init (RBPlayerIface *iface);
 static void rb_player_gst_filter_init (RBPlayerGstFilterIface *iface);
@@ -112,6 +113,7 @@
 	GDestroyNotify stream_data_destroy;
 
 	GstElement *playbin;
+	GstElement *volume_handler;
 
 	gboolean can_signal_direct_error;
 	GError *error;
@@ -262,6 +264,10 @@
 
 		rb_player_gst_gst_free_playbin (mp);
 	}
+	if (mp->priv->volume_handler) {
+		g_object_unref (mp->priv->volume_handler);
+		mp->priv->volume_handler = NULL;
+	}
 
 	if (mp->priv->waiting_tees) {
 		g_list_foreach (mp->priv->waiting_tees, (GFunc)gst_object_sink, NULL);
@@ -599,17 +605,13 @@
 	/* Use gconfaudiosink for audio if there's no audio sink yet */
 	g_object_get (G_OBJECT (mp->priv->playbin), "audio-sink", &sink, NULL);
 	if (sink == NULL) {
-		sink = gst_element_factory_make ("gconfaudiosink", "audiosink");
+		sink = rb_player_gst_try_audio_sink ("gconfaudiosink", "audiosink");
 		if (sink == NULL) {
 			/* fall back to autoaudiosink */
-			sink = gst_element_factory_make ("autoaudiosink", "audiosink");
+			sink = rb_player_gst_try_audio_sink ("autoaudiosink", "audiosink");
 		}
 
 		if (sink != NULL) {
-			/* set the profile property on the gconfaudiosink to "music and movies" */
-			if (g_object_class_find_property (G_OBJECT_GET_CLASS (sink), "profile"))
-				g_object_set (G_OBJECT (sink), "profile", 1, NULL);
-
 			g_object_set (G_OBJECT (mp->priv->playbin), "audio-sink", sink, NULL);
 		}
 	} else {
@@ -939,6 +941,12 @@
 	begin_gstreamer_operation (mp);
 	ret = gst_element_set_state (mp->priv->playbin, GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS;
 	end_gstreamer_operation (mp, !ret, error);
+
+	if (mp->priv->volume_handler) {
+		g_object_unref (mp->priv->volume_handler);
+		mp->priv->volume_handler = NULL;
+	}
+
 	return ret;
 }
 
@@ -1002,6 +1010,25 @@
 }
 
 static void
+find_volume_handler (RBPlayerGst *mp)
+{
+	/* look for a 'volume' property provided by the sink */
+	if (mp->priv->volume_handler == NULL && mp->priv->playbin != NULL) {
+		GstElement *sink;
+
+		g_object_get (mp->priv->playbin, "audio-sink", &sink, NULL);
+		if (sink != NULL) {
+			mp->priv->volume_handler = rb_player_gst_find_element_with_property (sink, "volume");
+			g_object_unref (sink);
+		}
+
+		if (mp->priv->volume_handler == NULL) {
+			mp->priv->volume_handler = g_object_ref (mp->priv->playbin);
+		}
+	}
+}
+
+static void
 rb_player_gst_set_replaygain (RBPlayer *player,
 			      const char *uri,
 			      double track_gain, double track_peak,
@@ -1037,11 +1064,12 @@
 
 	rb_debug ("Scale : %f New volume : %f", scale, mp->priv->cur_volume * scale);
 
-	if (mp->priv->playbin != NULL) {
+	find_volume_handler (mp);
+	if (mp->priv->volume_handler != NULL) {
 		GParamSpec *volume_pspec;
 		GValue val = {0,};
 
-		volume_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (mp->priv->playbin),
+		volume_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (mp->priv->volume_handler),
 							     "volume");
 		g_value_init (&val, G_TYPE_DOUBLE);
 
@@ -1049,7 +1077,7 @@
 		if (g_param_value_validate (volume_pspec, &val))
 			rb_debug ("replay gain too high, reducing value to %f", g_value_get_double (&val));
 
-		g_object_set_property (G_OBJECT (mp->priv->playbin), "volume", &val);
+		g_object_set_property (G_OBJECT (mp->priv->volume_handler), "volume", &val);
 		g_value_unset (&val);
 	}
 }
@@ -1061,11 +1089,9 @@
 	RBPlayerGst *mp = RB_PLAYER_GST (player);
 	g_return_if_fail (volume >= 0.0 && volume <= 1.0);
 
-	if (mp->priv->playbin != NULL) {
-		g_object_set (G_OBJECT (mp->priv->playbin),
-			      "volume",
-			      volume,
-			      NULL);
+	find_volume_handler (mp);
+	if (mp->priv->volume_handler != NULL) {
+		g_object_set (mp->priv->volume_handler, "volume", volume, NULL);
 	}
 
 	mp->priv->cur_volume = volume;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]