Re: [Rhythmbox-devel] iTunes Music Sharing



On Thu, Jul 07, 2005 at 09:43:30AM +1000, Jonathan Matthew wrote:
> So, what I propose is that when you register a RhythmDBEntryType for
> your source, you also create a new RBPlayer subclass (RBCDAudioPlayer,
> RBDAAPPlayer, etc.) and associate it with that entry type.  These
> subclasses store the information required to play songs of their
> type, and know how to manipulate GStreamer to make use of it.
> They'd all share a playbin element, but that's just an implementation detail.

What I ended up coding was almost completely different to that.
Instead of an RBPlayer subclass, there are things I'm currently calling
RBPlayerSubtypes (suggestions for a better name are welcome..), that
hook into the main RBPlayer in a few places.

I've got a 'player-subtype' property on RBSource, so every song
from the same source uses the same subtype instance.  Subtype instances
can also be shared between sources, if required.  RBShellPlayer
retrieves the subtype instance from the playing source and passes it to
rb_player_open.

The hooks I've got at the moment are:
 
 gboolean (*pre_open) (RBPlayerSubtype *subtype, GstElement *playbin, 
                       const char *uri, GError **error);

called just before setting the URI property on the playbin and starting
playback.

 void (*got_source)   (RBPlayerSubtype *subtype, GstElement *playbin, 
                       GstElement *source);

called when playbin has created a source element.  While the subtype
could do this itself by setting up a signal handler in pre_open, I
figured this would be a pretty common thing to do, so I might as well
just write the code once.

 void (*pre_close)    (RBPlayerSubtype *subtype, GstElement *playbin, 
                       GError **error);

called just before stopping playback.

It looks like a seek hook may be required for DAAP, but I don't know
exactly what that should look like or how it should effect the existing
seek code, so I've left it out.

The attached patch contains the current code.  It doesn't actually do
anything, because none of the existing sources need to use it at all.
Comments are welcome.

-jonathan.
Index: player/rb-player-gst.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/player/rb-player-gst.c,v
retrieving revision 1.35
diff -u -r1.35 rb-player-gst.c
--- player/rb-player-gst.c	14 Jun 2005 12:09:54 -0000	1.35
+++ player/rb-player-gst.c	12 Jul 2005 13:03:29 -0000
@@ -37,8 +37,35 @@
 #include "rb-debug.h"
 #include "rb-marshal.h"
 
+#define RB_TYPE_PLAYER_SUBTYPE 		(rb_player_subtype_get_type ())
+#define RB_PLAYER_SUBTYPE(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_PLAYER_SUBTYPE, RBPlayerSubtype))
+#define RB_PLAYER_SUBTYPE_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), RB_TYPE_PLAYER_SUBTYPE, RBPlayerSubtypeClass))
+#define RB_IS_PLAYER_SUBTYPE(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), RB_TYPE_PLAYER_SUBTYPE))
+#define RB_IS_PLAYER_SUBTYPE_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), RB_TYPE_PLAYER_SUBTYPE))
+#define RB_PLAYER_SUBTYPE_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), RB_TYPE_PLAYER_SUBTYPE, RBPlayerSubtypeClass))
+
+GType rb_player_cdda_subtype_get_type (void);
+
 G_DEFINE_TYPE(RBPlayer, rb_player, G_TYPE_OBJECT)
 
+typedef struct
+{
+	GObject parent;
+} RBPlayerSubtype;
+
+typedef struct
+{
+	GObjectClass parent_class;
+
+	gboolean (*pre_open)		(RBPlayerSubtype *subtype, GstElement *playbin, const char *uri, GError **error);
+	void (*got_source)		(RBPlayerSubtype *subtype, GstElement *playbin, GstElement *source);
+	void (*pre_close)		(RBPlayerSubtype *subtype, GstElement *playbin, GError **error);
+} RBPlayerSubtypeClass;
+
+GType rb_player_subtype_get_type (void);
+	
+G_DEFINE_TYPE(RBPlayerSubtype, rb_player_subtype, G_TYPE_OBJECT)
+	
 static void rb_player_finalize (GObject *object);
 
 struct RBPlayerPrivate
@@ -59,6 +86,8 @@
 	long timer_add;
 
 	guint tick_timeout_id;
+
+	RBPlayerSubtype *subtype;
 };
 
 typedef enum
@@ -303,6 +332,20 @@
 }
 
 static void
+got_source_cb (GObject *object, GParamSpec *pspec, RBPlayer *player)
+{
+	GstElement *playbin = GST_ELEMENT (object);
+	GstElement *source;
+
+	gst_element_get (playbin, "source", &source, NULL);
+	rb_debug ("got source %p", source);
+	if (source && player->priv->subtype) {
+		RBPlayerSubtypeClass *subtype_class = RB_PLAYER_SUBTYPE_GET_CLASS (player->priv->subtype);
+		(*subtype_class->got_source) (player->priv->subtype, playbin, source);
+	}
+}
+
+static void
 rb_player_construct (RBPlayer *mp, GError **error)
 {
 	char *element_name = NULL;
@@ -318,6 +361,10 @@
 				 "deep_notify",
 				 G_CALLBACK (deep_notify_cb),
 				 mp, 0);
+	g_signal_connect_object (G_OBJECT (mp->priv->playbin),
+				 "notify::source",
+				 G_CALLBACK (got_source_cb),
+				 mp, 0);
 
 	mp->priv->error_signal_id =
 		g_signal_connect_object (G_OBJECT (mp->priv->playbin),
@@ -430,6 +477,7 @@
 void
 rb_player_open (RBPlayer *mp,
 		const char *uri,
+		GObject *subtype,
 		GError **error)
 {
 	g_return_if_fail (RB_IS_PLAYER (mp));
@@ -449,6 +497,18 @@
 		mp->priv->playing = FALSE;
 		return;
 	}
+
+	if (subtype) {
+		g_return_if_fail (RB_IS_PLAYER_SUBTYPE (subtype));
+
+		g_object_ref (subtype);
+		mp->priv->subtype = RB_PLAYER_SUBTYPE (subtype);
+
+		RBPlayerSubtypeClass *subtype_class = RB_PLAYER_SUBTYPE_GET_CLASS (mp->priv->subtype);
+		if ((*subtype_class->pre_open) (mp->priv->subtype, mp->priv->playbin, uri, error) == FALSE)
+			return;
+	}
+	
 	g_object_set (G_OBJECT (mp->priv->playbin), "uri", uri, NULL);	
 	mp->priv->uri = g_strdup (uri);
 
@@ -474,6 +534,14 @@
 	if (mp->priv->playbin == NULL)
 		return;
 
+	if (mp->priv->subtype) {
+		RBPlayerSubtypeClass *subtype_class = RB_PLAYER_SUBTYPE_GET_CLASS (mp->priv->subtype);
+		(*subtype_class->pre_close) (mp->priv->subtype, mp->priv->playbin, error);
+
+		g_object_unref (mp->priv->subtype);
+		mp->priv->subtype = NULL;
+	}
+
 	if (gst_element_set_state (mp->priv->playbin,
 				   GST_STATE_READY) != GST_STATE_SUCCESS) {
 		g_set_error (error,
@@ -642,3 +710,98 @@
 	else
 		return -1;
 }
+
+/**** subtype handling ****/
+	
+static gboolean default_subtype_pre_open (RBPlayerSubtype *subtype, GstElement *playbin, const char *uri, GError **error);
+static void default_subtype_got_source (RBPlayerSubtype *subtype, GstElement *playbin, GstElement *source);
+static void default_subtype_pre_close (RBPlayerSubtype *subtype, GstElement *playbin, GError **error);
+
+static void
+rb_player_subtype_class_init (RBPlayerSubtypeClass *klass)
+{
+	klass->pre_open = default_subtype_pre_open;
+	klass->got_source = default_subtype_got_source;
+	klass->pre_close = default_subtype_pre_close;
+}
+
+static void
+rb_player_subtype_init (RBPlayerSubtype *subtype)
+{
+	/* nothing */
+}
+
+static gboolean
+default_subtype_pre_open (RBPlayerSubtype *subtype, GstElement *playbin, const char *uri, GError **error)
+{
+	return TRUE;
+}
+
+static void
+default_subtype_got_source (RBPlayerSubtype *subtype, GstElement *playbin, GstElement *source)
+{
+	/* nothing */
+}
+
+static void
+default_subtype_pre_close (RBPlayerSubtype *subtype, GstElement *playbin, GError **error)
+{
+	/* nothing */
+}
+
+/* CD audio subtype implementation */
+
+#define RB_TYPE_PLAYER_CDDA_SUBTYPE 	(rb_player_cdda_subtype_get_type ())
+#define RB_PLAYER_CDDA_SUBTYPE(o)	(G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_PLAYER_CDDA_SUBTYPE, RBPlayerCDDASubtype))
+
+typedef struct
+{
+	RBPlayerSubtype parent;
+	char *cdda_device;
+} RBPlayerCDDASubtype;
+	
+typedef struct
+{
+	RBPlayerSubtypeClass parent_class;
+} RBPlayerCDDASubtypeClass;
+
+G_DEFINE_TYPE(RBPlayerCDDASubtype, rb_player_cdda_subtype, RB_TYPE_PLAYER_SUBTYPE)
+
+static void rb_player_cdda_subtype_got_source (RBPlayerSubtype *subtype, 
+					       GstElement *playbin, 
+					       GstElement *source);
+
+static void
+rb_player_cdda_subtype_class_init (RBPlayerCDDASubtypeClass *klass)
+{
+	RBPlayerSubtypeClass *subtype_class = RB_PLAYER_SUBTYPE_CLASS (klass);
+	subtype_class->got_source = rb_player_cdda_subtype_got_source;
+}
+
+static void
+rb_player_cdda_subtype_init (RBPlayerCDDASubtype *subtype)
+{
+	/* nothing */
+}
+
+static void 
+rb_player_cdda_subtype_got_source (RBPlayerSubtype *subtype, 
+				   GstElement *playbin, 
+				   GstElement *source)
+{
+	RBPlayerCDDASubtype *st = RB_PLAYER_CDDA_SUBTYPE (subtype);
+
+	if (source && st->cdda_device) {
+		rb_debug ("setting device %s on source element", st->cdda_device);
+		gst_element_set (source, "device", st->cdda_device, NULL);
+	}
+}
+
+GObject *
+rb_player_cdda_subtype_new (const char *device)
+{
+	RBPlayerCDDASubtype *subtype = g_object_new (RB_TYPE_PLAYER_CDDA_SUBTYPE, "device", device, NULL);
+	subtype->cdda_device = g_strdup (device);
+	return G_OBJECT (subtype);
+}
+
Index: player/rb-player.h
===================================================================
RCS file: /cvs/gnome/rhythmbox/player/rb-player.h,v
retrieving revision 1.8
diff -u -r1.8 rb-player.h
--- player/rb-player.h	26 Mar 2004 02:41:54 -0000	1.8
+++ player/rb-player.h	12 Jul 2005 13:03:29 -0000
@@ -82,6 +82,7 @@
 
 void		rb_player_open       (RBPlayer *mp,
 				      const char *uri,
+				      GObject *subtype,
 				      GError **error);
 
 gboolean	rb_player_opened     (RBPlayer *mp);
@@ -107,6 +108,8 @@
 				      long time);
 long		rb_player_get_time   (RBPlayer *mp);
 
+GObject *	rb_player_cdda_subtype_new (const char *device);
+
 G_END_DECLS
 
 #endif /* __RB_PLAYER_H */
Index: sources/rb-source.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/sources/rb-source.c,v
retrieving revision 1.35
diff -u -r1.35 rb-source.c
--- sources/rb-source.c	12 Jun 2005 17:16:49 -0000	1.35
+++ sources/rb-source.c	12 Jul 2005 13:03:30 -0000
@@ -67,6 +67,7 @@
 	
 	RBShell *shell;
 	gboolean visible;
+	GObject *player_subtype;
 };
 
 enum
@@ -76,7 +77,8 @@
 	PROP_INTERNAL_NAME,
 	PROP_SHELL,
 	PROP_UI_MANAGER,
-	PROP_VISIBLE
+	PROP_VISIBLE,
+	PROP_PLAYER_SUBTYPE
 };
 
 enum
@@ -194,6 +196,14 @@
 							       TRUE,
 							       G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
 
+	g_object_class_install_property (object_class,
+					 PROP_PLAYER_SUBTYPE,
+					 g_param_spec_object ("player-subtype",
+							      "GObject",
+							      "RBPlayerSubtype object",
+							      G_TYPE_OBJECT,
+							      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
 	rb_source_signals[DELETED] =
 		g_signal_new ("deleted",
 			      RB_TYPE_SOURCE,
@@ -285,6 +295,9 @@
 			  source->priv->name, 
 			  source->priv->visible);
 		break;
+	case PROP_PLAYER_SUBTYPE:
+		source->priv->player_subtype = g_value_get_object (value);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
@@ -323,6 +336,9 @@
 		g_object_unref (G_OBJECT(manager));
 		break;
 	}
+	case PROP_PLAYER_SUBTYPE:
+		g_value_set_object (value, source->priv->player_subtype);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 		break;
Index: shell/rb-shell-player.c
===================================================================
RCS file: /cvs/gnome/rhythmbox/shell/rb-shell-player.c,v
retrieving revision 1.154
diff -u -r1.154 rb-shell-player.c
--- shell/rb-shell-player.c	19 Jun 2005 18:30:57 -0000	1.154
+++ shell/rb-shell-player.c	12 Jul 2005 13:03:32 -0000
@@ -896,11 +896,13 @@
 					RBShellPlayer *player)
 {
 	GError *error = NULL;
+	GObject *player_subtype;
 
 	if (rb_player_playing (player->priv->mmplayer))
 		return;
 
-	rb_player_open (player->priv->mmplayer, uri, &error);
+	g_object_get (player->priv->source, "player-subtype", &player_subtype, NULL);
+	rb_player_open (player->priv->mmplayer, uri, player_subtype, &error);
 	if (error != NULL) {
 		if (player->priv->playlist_parse_error != NULL) {
 			g_error_free (player->priv->playlist_parse_error);
@@ -943,21 +945,25 @@
 	g_free (player->priv->song);
 	player->priv->song = NULL;
 	
-	playlist = totem_pl_parser_new ();
-	g_signal_connect_object (G_OBJECT (playlist), "entry",
-				 G_CALLBACK (rb_shell_player_open_playlist_location),
-				 player, 0);
-	totem_pl_parser_add_ignored_scheme (playlist, "x-directory/normal");
 
 	playlist_parsed = FALSE;
 	if (rb_uri_is_iradio (location)) {
+		playlist = totem_pl_parser_new ();
+		g_signal_connect_object (G_OBJECT (playlist), "entry",
+					 G_CALLBACK (rb_shell_player_open_playlist_location),
+					 player, 0);
+		totem_pl_parser_add_ignored_scheme (playlist, "x-directory/normal");
+
 		playlist_result = totem_pl_parser_parse (playlist, location, FALSE);
 		playlist_parsed = (playlist_result != TOTEM_PL_PARSER_RESULT_UNHANDLED);
+
+		g_object_unref (playlist);
 	}
-	g_object_unref (playlist);
 	if (!playlist_parsed) {
 		/* We get here if we failed to parse as a playlist */
-		rb_player_open (player->priv->mmplayer, location, error);
+		GObject *player_subtype;
+		g_object_get (player->priv->source, "player-subtype", &player_subtype, NULL);
+		rb_player_open (player->priv->mmplayer, location, player_subtype, error);
 	} else if (playlist_result == TOTEM_PL_PARSER_RESULT_ERROR
 		   && error
 		   && player->priv->playlist_parse_error) {


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