[rhythmbox] player-intf: use nanoseconds for times, add new param for play behaviour



commit eb529f721d13da10c51a710c5541a79498c118e6
Author: Jonathan Matthew <jonathan d14n org>
Date:   Fri Jun 5 13:14:55 2009 +1000

    player-intf: use nanoseconds for times, add new param for play behaviour
    
    Convert all times used in the player interface to nanoseconds, so we can
    do fractional second crossfades and (theoretically) make the song
    position slider move more smoothly.
    
    Rather than overloading the crossfade duration to control how
    rb_player_play behaves, add a new enum parameter with values for
    crossfading, replacing the current stream, and playing after the current
    stream ends.
---
 backends/gstreamer/rb-player-gst-xfade.c |   92 +++++++++++++-----------
 backends/gstreamer/rb-player-gst.c       |   13 ++--
 backends/rb-player.c                     |   60 +++++++++++-----
 backends/rb-player.h                     |   33 ++++++---
 bindings/python/rb.defs                  |   14 ++++-
 lib/rb-marshal.list                      |    1 +
 shell/rb-shell-player.c                  |  114 ++++++++++++------------------
 7 files changed, 181 insertions(+), 146 deletions(-)

diff --git a/backends/gstreamer/rb-player-gst-xfade.c b/backends/gstreamer/rb-player-gst-xfade.c
index 489b963..a69fb02 100644
--- a/backends/gstreamer/rb-player-gst-xfade.c
+++ b/backends/gstreamer/rb-player-gst-xfade.c
@@ -68,10 +68,10 @@
  *
  * from WAITING:
  *
- * - rb_player_play(), crossfade == 0, other stream playing:  -> WAITING_EOS
- * - rb_player_play(), crossfade > 0, other stream playing:   -> FADING IN, link to adder, unblock
+ * - rb_player_play(), _AFTER_EOS, other stream playing:  -> WAITING_EOS
+ * - rb_player_play(), _CROSSFADE, other stream playing:   -> FADING IN, link to adder, unblock
  *      + fade out existing stream
- * - rb_player_play(), crossfade < 0, other stream playing:   -> PLAYING, link to adder, unblock
+ * - rb_player_play(), _REPLACE, other stream playing:   -> PLAYING, link to adder, unblock
  *      + stop existing stream
  * - rb_player_play(), existing stream paused:  -> PLAYING, link to adder, unblock
  *      + stop existing stream
@@ -79,10 +79,10 @@
  *
  * from PREROLL_PLAY:
  *
- * - preroll finishes, crossfade == 0, other stream playing:  -> WAITING_EOS
- * - preroll finishes, crossfade > 0, other stream playing:  -> FADING_IN, link to adder, unblock
+ * - preroll finishes, _AFTER_EOS, other stream playing:  -> WAITING_EOS
+ * - preroll finishes, _CROSSFADE, other stream playing:  -> FADING_IN, link to adder, unblock
  *   	+ fade out existing stream
- * - preroll finishes, crossfade < 0, other stream playing:  -> PLAYING, link to adder, unblock
+ * - preroll finishes, _REPLACE, other stream playing:  -> PLAYING, link to adder, unblock
  *      + stop existing stream
  * - preroll finishes, existing stream paused:  -> PLAYING, link to adder, unblock
  *      + stop existing stream
@@ -183,12 +183,12 @@ static gboolean rb_player_gst_xfade_open (RBPlayer *player,
 					  GError **error);
 static gboolean rb_player_gst_xfade_opened (RBPlayer *player);
 static gboolean rb_player_gst_xfade_close (RBPlayer *player, const char *uri, GError **error);
-static gboolean rb_player_gst_xfade_play (RBPlayer *player, gint crossfade, GError **error);
+static gboolean rb_player_gst_xfade_play (RBPlayer *player, RBPlayerPlayType play_type, gint64 crossfade, GError **error);
 static void rb_player_gst_xfade_pause (RBPlayer *player);
 static gboolean rb_player_gst_xfade_playing (RBPlayer *player);
 static gboolean rb_player_gst_xfade_seekable (RBPlayer *player);
-static void rb_player_gst_xfade_set_time (RBPlayer *player, long time);
-static long rb_player_gst_xfade_get_time (RBPlayer *player);
+static void rb_player_gst_xfade_set_time (RBPlayer *player, gint64 time);
+static gint64 rb_player_gst_xfade_get_time (RBPlayer *player);
 static void rb_player_gst_xfade_set_volume (RBPlayer *player, float volume);
 static float rb_player_gst_xfade_get_volume (RBPlayer *player);
 static void rb_player_gst_xfade_set_replaygain (RBPlayer *player,
@@ -361,7 +361,8 @@ typedef struct
 
 	GstController *fader;
 	StreamState state;
-	gint crossfade;
+	RBPlayerPlayType play_type;
+	gint64 crossfade;
 	gboolean fading;
 
 	gulong adjust_probe_id;
@@ -2225,14 +2226,14 @@ create_stream (RBPlayerGstXFade *player, const char *uri, gpointer stream_data,
 
 /* starts playback for a stream.
  * - links to adder and unblocks
- * - if crossfading:
+ * - if play_type is CROSSFADE:
  *   - starts the fade in of the new stream
  *   - starts the fade out of the old stream
  *   - sets the stream to PLAYING state
- * - if following (crossfade == 0)
+ * - if play_type is WAIT_EOS:
  *   - if something is playing, set the stream to wait-eos state
  *   - otherwise, starts it
- * - if replacing (crossfade == -1)
+ * - if play_type is REPLACE:
  *   - stops any existing stream
  *   - starts the new stream
  */
@@ -2242,13 +2243,15 @@ actually_start_stream (RBXFadeStream *stream, GError **error)
 	RBPlayerGstXFade *player = stream->player;
 	gboolean ret = TRUE;
 	gboolean need_reap = FALSE;
+	gboolean playing;
+	GList *l;
+	GList *to_fade;
 
+	rb_debug ("going to start playback for stream %s (play type %d, crossfade %" G_GINT64_FORMAT ") -> FADING_IN | PLAYING", stream->uri, stream->play_type, stream->crossfade);
+	switch (stream->play_type) {
+	case RB_PLAYER_PLAY_CROSSFADE:
 
-	rb_debug ("going to start playback for stream %s (crossfade %d) -> FADING_IN | PLAYING", stream->uri, stream->crossfade);
-	if (stream->crossfade > 0) {
-		GList *l;
-		GList *to_fade = NULL;
-
+		to_fade = NULL;
 		g_static_rec_mutex_lock (&player->priv->stream_list_lock);
 		for (l = player->priv->streams; l != NULL; l = l->next) {
 			RBXFadeStream *pstream = (RBXFadeStream *)l->data;
@@ -2286,20 +2289,20 @@ actually_start_stream (RBXFadeStream *stream, GError **error)
 		for (l = to_fade; l != NULL; l = l->next) {
 			RBXFadeStream *pstream = (RBXFadeStream *)l->data;
 			double fade_out_start = 1.0f;
-			gint64 fade_out_time = stream->crossfade * GST_SECOND;
+			gint64 fade_out_time = stream->crossfade;
 
 			switch (pstream->state) {
 			case FADING_IN:
 				/* fade out from where the fade in got up to */
 				g_object_get (pstream->volume, "volume", &fade_out_start, NULL);
-				fade_out_time = (gint64)(((double) stream->crossfade) * fade_out_start) * GST_SECOND;
+				fade_out_time = (gint64)(((double) stream->crossfade) * fade_out_start);
 				/* fall through */
 
 			case PLAYING:
 				start_stream_fade (pstream, fade_out_start, 0.0f, fade_out_time);
 				pstream->state = FADING_OUT;
 
-				start_stream_fade (stream, 0.0f, 1.0f, stream->crossfade * GST_SECOND);
+				start_stream_fade (stream, 0.0f, 1.0f, stream->crossfade);
 				break;
 
 			default:
@@ -2324,12 +2327,13 @@ actually_start_stream (RBXFadeStream *stream, GError **error)
 		}
 
 		ret = link_and_unblock_stream (stream, error);
-	} else if (stream->crossfade == 0) {
-		GList *l;
-		gboolean playing = FALSE;
+		break;
+
+	case RB_PLAYER_PLAY_AFTER_EOS:
 
 		g_static_rec_mutex_lock (&player->priv->stream_list_lock);
 
+		playing = FALSE;
 		for (l = player->priv->streams; l != NULL; l = l->next) {
 			RBXFadeStream *pstream = (RBXFadeStream *)l->data;
 			if (pstream == stream)
@@ -2363,10 +2367,10 @@ actually_start_stream (RBXFadeStream *stream, GError **error)
 			rb_debug ("no playing stream found, so starting immediately");
 			ret = link_and_unblock_stream (stream, error);
 		}
-	} else {
-		/* replace any existing playing stream */
-		GList *l;
+		break;
 
+	case RB_PLAYER_PLAY_REPLACE:
+		/* replace any existing playing stream */
 		g_static_rec_mutex_lock (&player->priv->stream_list_lock);
 
 		for (l = player->priv->streams; l != NULL; l = l->next) {
@@ -2394,6 +2398,10 @@ actually_start_stream (RBXFadeStream *stream, GError **error)
 		g_static_rec_mutex_unlock (&player->priv->stream_list_lock);
 
 		ret = link_and_unblock_stream (stream, error);
+		break;
+
+	default:
+		g_assert_not_reached ();
 	}
 
 	if (need_reap) {
@@ -2563,9 +2571,6 @@ get_times_and_stream (RBPlayerGstXFade *player, RBXFadeStream **pstream, gint64
 				*pos = -1;
 
 				gst_element_query_position (stream->volume, &format, pos);
-				if (*pos != -1) {
-					*pos /= GST_SECOND;
-				}
 			} else {
 				/* for playing streams, we subtract the current output position
 				 * (a running counter generated by the adder) from the position
@@ -2576,7 +2581,6 @@ get_times_and_stream (RBPlayerGstXFade *player, RBXFadeStream **pstream, gint64
 				gst_element_query_position (player->priv->pipeline, &format, pos);
 				if (*pos != -1) {
 					*pos -= stream->base_time;
-					*pos /= GST_SECOND;
 				} else {
 					rb_debug ("position query failed");
 				}
@@ -2591,9 +2595,6 @@ get_times_and_stream (RBPlayerGstXFade *player, RBXFadeStream **pstream, gint64
 			 * linked element.
 			 */
 			gst_element_query_duration (stream->volume, &format, duration);
-			if (*duration != -1) {
-				*duration /= GST_SECOND;
-			}
 		}
 		got_time = TRUE;
 		if (pstream == NULL) {
@@ -3365,7 +3366,10 @@ rb_player_gst_xfade_opened (RBPlayer *iplayer)
 }
 
 static gboolean
-rb_player_gst_xfade_play (RBPlayer *iplayer, gint crossfade, GError **error)
+rb_player_gst_xfade_play (RBPlayer *iplayer,
+			  RBPlayerPlayType play_type,
+			  gint64 crossfade,
+			  GError **error)
 {
 	RBXFadeStream *stream;
 	int stream_state;
@@ -3397,7 +3401,7 @@ rb_player_gst_xfade_play (RBPlayer *iplayer, gint crossfade, GError **error)
 
 	g_mutex_lock (stream->lock);
 
-	rb_debug ("playing stream %s, crossfade %d", stream->uri, crossfade);
+	rb_debug ("playing stream %s, play type %d, crossfade %" G_GINT64_FORMAT, stream->uri, play_type, crossfade);
 
 	/* handle transitional states while holding the lock, and handle states that
 	 * require action outside it (lock precedence, mostly)
@@ -3406,6 +3410,7 @@ rb_player_gst_xfade_play (RBPlayer *iplayer, gint crossfade, GError **error)
 	case PREROLLING:
 	case PREROLL_PLAY:
 		rb_debug ("stream %s is prerolling; will start playback once prerolling is complete -> PREROLL_PLAY", stream->uri);
+		stream->play_type = play_type;
 		stream->crossfade = crossfade;
 		stream->state = PREROLL_PLAY;
 		break;
@@ -3446,12 +3451,15 @@ rb_player_gst_xfade_play (RBPlayer *iplayer, gint crossfade, GError **error)
 
 	case WAITING_EOS:
 	case WAITING:
+		stream->play_type = play_type;
 		stream->crossfade = crossfade;
 		ret = actually_start_stream (stream, error);
 		break;
 
 	case REUSING:
-		if (crossfade > 0) {
+		switch (play_type) {
+		case RB_PLAYER_PLAY_REPLACE:
+		case RB_PLAYER_PLAY_CROSSFADE:
 			/* probably should split this into two states.. */
 			if (stream->src_blocked) {
 				rb_debug ("reusing and restarting paused stream %s", stream->uri);
@@ -3461,8 +3469,10 @@ rb_player_gst_xfade_play (RBPlayer *iplayer, gint crossfade, GError **error)
 				rb_debug ("unlinking stream %s for reuse", stream->uri);
 				unlink_and_block_stream (stream);
 			}
-		} else {
+			break;
+		case RB_PLAYER_PLAY_AFTER_EOS:
 			rb_debug ("waiting for EOS before reusing stream %s", stream->uri);
+			break;
 		}
 		break;
 
@@ -3728,7 +3738,7 @@ rb_player_gst_xfade_seekable (RBPlayer *iplayer)
 }
 
 static void
-rb_player_gst_xfade_set_time (RBPlayer *iplayer, long time)
+rb_player_gst_xfade_set_time (RBPlayer *iplayer, gint64 time)
 {
 	RBPlayerGstXFade *player = RB_PLAYER_GST_XFADE (iplayer);
 	RBXFadeStream *stream;
@@ -3742,7 +3752,7 @@ rb_player_gst_xfade_set_time (RBPlayer *iplayer, long time)
 		return;
 	}
 
-	stream->seek_target = time * GST_SECOND;
+	stream->seek_target = time;
 	switch (stream->state) {
 	case PAUSED:
 		rb_debug ("seeking in paused stream %s; target %" 
@@ -3788,7 +3798,7 @@ rb_player_gst_xfade_set_time (RBPlayer *iplayer, long time)
 	g_object_unref (stream);
 }
 
-static long
+static gint64
 rb_player_gst_xfade_get_time (RBPlayer *iplayer)
 {
 	gint64 pos = -1;
diff --git a/backends/gstreamer/rb-player-gst.c b/backends/gstreamer/rb-player-gst.c
index 1e87467..1257668 100644
--- a/backends/gstreamer/rb-player-gst.c
+++ b/backends/gstreamer/rb-player-gst.c
@@ -712,7 +712,7 @@ impl_opened (RBPlayer *player)
 }
 
 static gboolean
-impl_play (RBPlayer *player, gint crossfade, GError **error)
+impl_play (RBPlayer *player, RBPlayerPlayType play_type, gint64 crossfade, GError **error)
 {
 	RBPlayerGst *mp = RB_PLAYER_GST (player);
 	gboolean result;
@@ -902,7 +902,7 @@ impl_seekable (RBPlayer *player)
 }
 
 static void
-impl_set_time (RBPlayer *player, long time)
+impl_set_time (RBPlayer *player, gint64 time)
 {
 	RBPlayerGst *mp = RB_PLAYER_GST (player);
 	GError *error = NULL;
@@ -920,7 +920,7 @@ impl_set_time (RBPlayer *player, long time)
 
 	gst_element_seek (mp->priv->playbin, 1.0,
 			  GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
-			  GST_SEEK_TYPE_SET, time * GST_SECOND,
+			  GST_SEEK_TYPE_SET, time,
 			  GST_SEEK_TYPE_NONE, -1);
 
 	if (mp->priv->playing) {
@@ -931,7 +931,7 @@ impl_set_time (RBPlayer *player, long time)
 	}
 }
 
-static long
+static gint64
 impl_get_time (RBPlayer *player)
 {
 	RBPlayerGst *mp = RB_PLAYER_GST (player);
@@ -941,10 +941,7 @@ impl_get_time (RBPlayer *player)
 		GstFormat fmt = GST_FORMAT_TIME;
 
 		gst_element_query_position (mp->priv->playbin, &fmt, &position);
-		if (position != -1)
-			position /= GST_SECOND;
-
-		return (long) position;
+		return position;
 	} else {
 		return -1;
 	}
diff --git a/backends/rb-player.c b/backends/rb-player.c
index 960fa11..9f4d3af 100644
--- a/backends/rb-player.c
+++ b/backends/rb-player.c
@@ -149,8 +149,9 @@ rb_player_interface_init (RBPlayerIface *iface)
 	 * RBPlayer::tick:
 	 * @player: the #RBPlayer
 	 * @stream_data: the data associated with the stream
-	 * @elapsed: playback position in the stream
+	 * @elapsed: playback position in the stream (in nanoseconds)
 	 * @duration: current estimate of the duration of the stream
+	 *  (in nanoseconds)
 	 *
 	 * The 'tick' signal is emitted repeatedly while the stream is
 	 * playing. Signal handlers can use this to update UI and to
@@ -162,10 +163,10 @@ rb_player_interface_init (RBPlayerIface *iface)
 			      G_SIGNAL_RUN_LAST,
 			      G_STRUCT_OFFSET (RBPlayerIface, tick),
 			      NULL, NULL,
-			      rb_marshal_VOID__POINTER_LONG_LONG,
+			      rb_marshal_VOID__POINTER_INT64_INT64,
 			      G_TYPE_NONE,
 			      3,
-			      G_TYPE_POINTER, G_TYPE_LONG, G_TYPE_LONG);
+			      G_TYPE_POINTER, G_TYPE_INT64, G_TYPE_INT64);
 
 	/**
 	 * RBPlayer::buffering:
@@ -360,34 +361,37 @@ rb_player_close (RBPlayer *player, const char *uri, GError **error)
 /**
  * rb_player_play:
  * @player:	a #RBPlayer
- * @crossfade:	requested crossfade duration
+ * @play_type:  requested playback start type
+ * @crossfade:	requested crossfade duration (nanoseconds)
  * @error:	returns error information
  *
  * Starts playback of the most recently opened stream.
- * If @crossfade is greater than zero, the player may attempt
- * to crossfade the new stream with any existing streams.
+ * if @play_type is #RB_PLAYER_PLAY_CROSSFADE, the player
+ * may attempt to crossfade the new stream with any existing
+ * streams.  If it does this, the it will use @crossfade as the
+ * duration of the fade.
  *
- * If @crossfade is zero, the player may attempt to start the
- * stream immediately after the current playing stream reaches
- * EOS.  This may or may not result in the phenomemon known
- * as 'gapless playback'.
+ * If @play_type is #RB_PLAYER_PLAY_AFTER_EOS, the player may
+ * attempt to start the stream immediately after the current
+ * playing stream reaches EOS.  This may or may not result in
+ * the phenomemon known as 'gapless playback'.
  *
- * If @crossfade is less than zero, the player will stop any
+ * If @play_type is #RB_PLAYER_PLAY_REPLACE, the player will stop any
  * existing stream before starting the new stream. It may do
- * this anyway, regardless of the value of @crossfade.
+ * this anyway, regardless of the value of @play_type.
  *
  * The 'playing-stream' signal will be emitted when the new stream
  * is actually playing. This may be before or after control returns
  * to the caller.
  *
- * Return value: TRUE if playback started successfully
+ * Return value: %TRUE if playback started successfully
  */
 gboolean
-rb_player_play (RBPlayer *player, gint crossfade, GError **error)
+rb_player_play (RBPlayer *player, RBPlayerPlayType play_type, gint64 crossfade, GError **error)
 {
 	RBPlayerIface *iface = RB_PLAYER_GET_IFACE (player);
 
-	return iface->play (player, crossfade, error);
+	return iface->play (player, play_type, crossfade, error);
 }
 
 /**
@@ -498,7 +502,7 @@ rb_player_seekable (RBPlayer *player)
  * The seek may take place asynchronously.
  */
 void
-rb_player_set_time (RBPlayer *player, long newtime)
+rb_player_set_time (RBPlayer *player, gint64 newtime)
 {
 	RBPlayerIface *iface = RB_PLAYER_GET_IFACE (player);
 
@@ -510,8 +514,9 @@ rb_player_set_time (RBPlayer *player, long newtime)
  * @player:	a #RBPlayer
  *
  * Return value: the current playback position in the current stream
+ *  in nanoseconds.
  */
-long
+gint64
 rb_player_get_time (RBPlayer *player)
 {
 	RBPlayerIface *iface = RB_PLAYER_GET_IFACE (player);
@@ -630,7 +635,7 @@ _rb_player_emit_error (RBPlayer *player, gpointer stream_data, GError *error)
  * To be used by implementations only.
  */
 void
-_rb_player_emit_tick (RBPlayer *player, gpointer stream_data, long elapsed, long duration)
+_rb_player_emit_tick (RBPlayer *player, gpointer stream_data, gint64 elapsed, gint64 duration)
 {
 	g_signal_emit (player, signals[TICK], 0, stream_data, elapsed, duration);
 }
@@ -726,3 +731,22 @@ rb_player_error_get_type (void)
 	return etype;
 }
 
+GType
+rb_player_play_type_get_type (void)
+{
+	static GType etype = 0;
+
+	if (etype == 0)	{
+		static const GEnumValue values[] = {
+			ENUM_ENTRY (RB_PLAYER_PLAY_REPLACE, "Replace existing stream"),
+			ENUM_ENTRY (RB_PLAYER_PLAY_AFTER_EOS, "Start new stream after EOS of existing stream"),
+			ENUM_ENTRY (RB_PLAYER_PLAY_CROSSFADE, "Crossfade between streams"),
+			{ 0, 0, 0 }
+		};
+
+		etype = g_enum_register_static ("RBPlayerPlayType", values);
+	}
+
+	return etype;
+}
+
diff --git a/backends/rb-player.h b/backends/rb-player.h
index e9aa61a..0936074 100644
--- a/backends/rb-player.h
+++ b/backends/rb-player.h
@@ -39,6 +39,16 @@ G_BEGIN_DECLS
 
 typedef enum
 {
+	RB_PLAYER_PLAY_REPLACE,
+	RB_PLAYER_PLAY_AFTER_EOS,
+	RB_PLAYER_PLAY_CROSSFADE
+} RBPlayerPlayType;
+
+GType rb_player_play_type_get_type (void);
+#define RB_TYPE_PLAYER_PLAY_TYPE (rb_player_play_type_get_type())
+
+typedef enum
+{
 	RB_PLAYER_ERROR_NO_AUDIO,
 	RB_PLAYER_ERROR_GENERAL,
 	RB_PLAYER_ERROR_INTERNAL
@@ -49,6 +59,7 @@ GType rb_player_error_get_type (void);
 GQuark rb_player_error_quark (void);
 #define RB_PLAYER_ERROR rb_player_error_quark ()
 
+#define RB_PLAYER_SECOND	(G_USEC_PER_SEC * 1000)
 
 #define RB_TYPE_PLAYER         (rb_player_get_type ())
 #define RB_PLAYER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), RB_TYPE_PLAYER, RBPlayer))
@@ -76,7 +87,8 @@ struct _RBPlayerIface
 						 GError **error);
 
 	gboolean	(*play)			(RBPlayer *player,
-						 gint crossfade,
+						 RBPlayerPlayType play_type,
+						 gint64 crossfade,
 						 GError **error);
 	void		(*pause)		(RBPlayer *player);
 	gboolean	(*playing)		(RBPlayer *player);
@@ -93,8 +105,8 @@ struct _RBPlayerIface
 
 	gboolean	(*seekable)		(RBPlayer *player);
 	void		(*set_time)		(RBPlayer *player,
-						 long time);
-	long		(*get_time)		(RBPlayer *player);
+						 gint64 time);
+	gint64		(*get_time)		(RBPlayer *player);
 	gboolean	(*multiple_open)	(RBPlayer *player);
 
 
@@ -115,8 +127,8 @@ struct _RBPlayerIface
 						 GError *error);
 	void		(*tick)            	(RBPlayer *player,
 						 gpointer stream_data,
-						 long elapsed,
-						 long duration);
+						 gint64 elapsed,
+						 gint64 duration);
 	void		(*event)		(RBPlayer *player,
 						 gpointer stream_data,
 						 gpointer data);
@@ -141,7 +153,10 @@ gboolean        rb_player_close      (RBPlayer *player,
 				      const char *uri,
 				      GError **error);
 
-gboolean	rb_player_play       (RBPlayer *player, gint crossfade, GError **error);
+gboolean	rb_player_play       (RBPlayer *player,
+				      RBPlayerPlayType play_type,
+				      gint64 crossfade,
+				      GError **error);
 void		rb_player_pause      (RBPlayer *player);
 gboolean	rb_player_playing    (RBPlayer *player);
 
@@ -153,8 +168,8 @@ void		rb_player_set_replaygain (RBPlayer *player,
 					  double album_gain, double album_peak);
 
 gboolean	rb_player_seekable   (RBPlayer *player);
-void		rb_player_set_time   (RBPlayer *player, long newtime);
-long		rb_player_get_time   (RBPlayer *player);
+void		rb_player_set_time   (RBPlayer *player, gint64 newtime);
+gint64		rb_player_get_time   (RBPlayer *player);
 
 gboolean	rb_player_multiple_open (RBPlayer *player);
 
@@ -163,7 +178,7 @@ void	_rb_player_emit_eos (RBPlayer *player, gpointer stream_data);
 void	_rb_player_emit_info (RBPlayer *player, gpointer stream_data, RBMetaDataField field, GValue *value);
 void	_rb_player_emit_buffering (RBPlayer *player, gpointer stream_data, guint progress);
 void	_rb_player_emit_error (RBPlayer *player, gpointer stream_data, GError *error);
-void	_rb_player_emit_tick (RBPlayer *player, gpointer stream_data, long elapsed, long duration);
+void	_rb_player_emit_tick (RBPlayer *player, gpointer stream_data, gint64 elapsed, gint64 duration);
 void	_rb_player_emit_event (RBPlayer *player, gpointer stream_data, const char *name, gpointer data);
 void	_rb_player_emit_playing_stream (RBPlayer *player, gpointer stream_data);
 void	_rb_player_emit_volume_changed (RBPlayer *player, float volume);
diff --git a/bindings/python/rb.defs b/bindings/python/rb.defs
index 1a03381..ef006cb 100644
--- a/bindings/python/rb.defs
+++ b/bindings/python/rb.defs
@@ -190,6 +190,17 @@
   )
 )
 
+(define-enum PlayerPlayType
+  (in-module "RB")
+  (c-name "RBPlayerPlayType")
+  (gtype-id "RB_TYPE_PLAYER_PLAY_TYPE")
+  (values
+    '("replace" "RB_PLAYER_PLAY_REPLACE")
+    '("crossfade" "RB_PLAYER_PLAY_CROSSFADE")
+    '("after-eos" "RB_PLAYER_PLAY_AFTER_EOS")
+  )
+)
+
 (define-enum SourceEOFType
   (in-module "RB")
   (c-name "RBSourceEOFType")
@@ -2308,7 +2319,8 @@
   (c-name "rb_player_play")
   (return-type "gboolean")
   (parameters
-    '("gint" "crossfade")
+    '("RBPlayerPlayType" "play_type")
+    '("gint64" "crossfade")
     '("GError**" "error")
   )
 )
diff --git a/lib/rb-marshal.list b/lib/rb-marshal.list
index 32db07b..46e9289 100644
--- a/lib/rb-marshal.list
+++ b/lib/rb-marshal.list
@@ -26,6 +26,7 @@ VOID:OBJECT,INT,INT
 VOID:OBJECT,INT,INT,BOXED,UINT,UINT
 VOID:OBJECT,INT,POINTER
 VOID:POINTER,INT
+VOID:POINTER,INT64,INT64
 VOID:POINTER,INT,POINTER
 VOID:POINTER,LONG,LONG
 VOID:POINTER,OBJECT
diff --git a/shell/rb-shell-player.c b/shell/rb-shell-player.c
index a478144..23d22ab 100644
--- a/shell/rb-shell-player.c
+++ b/shell/rb-shell-player.c
@@ -152,7 +152,7 @@ static void rb_shell_player_property_row_activated_cb (RBPropertyView *view,
 static void rb_shell_player_sync_volume (RBShellPlayer *player, gboolean notify, gboolean set_volume);
 static void rb_shell_player_sync_replaygain (RBShellPlayer *player,
                                              RhythmDBEntry *entry);
-static void tick_cb (RBPlayer *player, RhythmDBEntry *entry, long elapsed, long duration, gpointer data);
+static void tick_cb (RBPlayer *player, RhythmDBEntry *entry, gint64 elapsed, gint64 duration, gpointer data);
 static void error_cb (RBPlayer *player, RhythmDBEntry *entry, const GError *err, gpointer data);
 static void missing_plugins_cb (RBPlayer *player, RhythmDBEntry *entry, const char **details, const char **descriptions, RBShellPlayer *sp);
 static void playing_stream_cb (RBPlayer *player, RhythmDBEntry *entry, RBShellPlayer *shell_player);
@@ -218,14 +218,8 @@ static RBPlayOrder* rb_play_order_new (RBShellPlayer *player, const char* porder
 
 #define CONF_STATE		CONF_PREFIX "/state"
 
-/* number of seconds before the end of a track to start prerolling the next */
-#define PREROLL_TIME		2
-
-typedef enum {
-	STOP_CURRENT = 0,
-	CROSSFADE,
-	WAIT_EOS
-} PlaybackStartType;
+/* number of nanoseconds before the end of a track to start prerolling the next */
+#define PREROLL_TIME		RB_PLAYER_SECOND
 
 struct RBShellPlayerPrivate
 {
@@ -252,7 +246,7 @@ struct RBShellPlayerPrivate
 	RBPlayer *mmplayer;
 
 	guint elapsed;
-	gint track_transition_time;
+	gint64 track_transition_time;
 	RhythmDBEntry *playing_entry;
 	gboolean playing_entry_eos;
 	gboolean jump_to_playing_entry;
@@ -707,33 +701,13 @@ reemit_playing_signal (RBShellPlayer *player,
 		       rb_player_playing (player->priv->mmplayer));
 }
 
-static int
-rb_shell_player_get_crossfade (RBShellPlayer *player, PlaybackStartType play_type)
-{
-	switch (play_type) {
-	case STOP_CURRENT:
-		return -1;
-		break;
-	case CROSSFADE:
-		return player->priv->track_transition_time;
-		break;
-	case WAIT_EOS:
-		return 0;
-		break;
-	default:
-		g_assert_not_reached ();
-	}
-}
-
-
 static void
 rb_shell_player_open_playlist_url (RBShellPlayer *player,
 				   const char *location,
 				   RhythmDBEntry *entry,
-				   PlaybackStartType play_type)
+				   RBPlayerPlayType play_type)
 {
 	GError *error = NULL;
-	gint crossfade = rb_shell_player_get_crossfade (player, play_type);
 
 	rb_debug ("playing stream url %s", location);
 	rb_player_open (player->priv->mmplayer,
@@ -742,7 +716,7 @@ rb_shell_player_open_playlist_url (RBShellPlayer *player,
 			(GDestroyNotify) rhythmdb_entry_unref,
 			&error);
 	if (error == NULL)
-		rb_player_play (player->priv->mmplayer, crossfade, &error);
+		rb_player_play (player->priv->mmplayer, play_type, player->priv->track_transition_time, &error);
 
 	if (error) {
 		GDK_THREADS_ENTER ();
@@ -1004,7 +978,7 @@ rb_shell_player_init (RBShellPlayer *player)
 
 	player->priv->volume = eel_gconf_get_float (CONF_STATE_VOLUME);
 
-	player->priv->track_transition_time = (int)(eel_gconf_get_float (CONF_PLAYER_TRANSITION_TIME));
+	player->priv->track_transition_time = eel_gconf_get_float (CONF_PLAYER_TRANSITION_TIME) * RB_PLAYER_SECOND;
 	player->priv->gconf_track_transition_time_id =
 		eel_gconf_notification_add (CONF_PLAYER_TRANSITION_TIME,
 					    (GConfClientNotifyFunc) gconf_track_transition_time_changed,
@@ -1456,7 +1430,7 @@ typedef struct {
 	RBShellPlayer *player;
 	char *location;
 	RhythmDBEntry *entry;
-	PlaybackStartType play_type;
+	RBPlayerPlayType play_type;
 } OpenLocationThreadData;
 
 static void
@@ -1517,7 +1491,7 @@ open_location_thread (OpenLocationThreadData *data)
 static gboolean
 rb_shell_player_open_location (RBShellPlayer *player,
 			       RhythmDBEntry *entry,
-			       PlaybackStartType play_type,
+			       RBPlayerPlayType play_type,
 			       GError **error)
 {
 	char *location;
@@ -1559,13 +1533,10 @@ rb_shell_player_open_location (RBShellPlayer *player,
 
 		g_thread_create ((GThreadFunc)open_location_thread, data, FALSE, NULL);
 	} else {
-		gint crossfade;
-		crossfade = rb_shell_player_get_crossfade (player, play_type);
-
 		rhythmdb_entry_ref (entry);
 		ret = ret && rb_player_open (player->priv->mmplayer, location, entry, (GDestroyNotify) rhythmdb_entry_unref, error);
 
-		ret = ret && rb_player_play (player->priv->mmplayer, crossfade, error);
+		ret = ret && rb_player_play (player->priv->mmplayer, play_type, player->priv->track_transition_time, error);
 	}
 
 	g_free (location);
@@ -1597,7 +1568,7 @@ rb_shell_player_play (RBShellPlayer *player,
 		return TRUE;
 
 	/* we're obviously not playing anything, so crossfading is irrelevant */
-	if (!rb_player_play (player->priv->mmplayer, FALSE, error)) {
+	if (!rb_player_play (player->priv->mmplayer, RB_PLAYER_PLAY_REPLACE, 0.0f, error)) {
 		rb_debug ("player doesn't want to");
 		return FALSE;
 	}
@@ -1647,12 +1618,12 @@ rb_shell_player_set_playing_entry (RBShellPlayer *player,
 {
 	GError *tmp_error = NULL;
 	GValue val = {0,};
-	PlaybackStartType play_type;
+	RBPlayerPlayType play_type;
 
 	g_return_val_if_fail (player->priv->current_playing_source != NULL, TRUE);
 	g_return_val_if_fail (entry != NULL, TRUE);
 
-	play_type = wait_for_eos ? WAIT_EOS : STOP_CURRENT;
+	play_type = wait_for_eos ? RB_PLAYER_PLAY_AFTER_EOS : RB_PLAYER_PLAY_REPLACE;
 
 	if (out_of_order) {
 		RBPlayOrder *porder;
@@ -1677,7 +1648,7 @@ rb_shell_player_set_playing_entry (RBShellPlayer *player,
 		if (wait_for_eos == FALSE ||
 		    strcmp (album, _("Unknown")) == 0 ||
 		    strcmp (album, previous_album) != 0) {
-			play_type = CROSSFADE;
+			play_type = RB_PLAYER_PLAY_CROSSFADE;
 		}
 	}
 
@@ -1992,7 +1963,7 @@ rb_shell_player_do_previous (RBShellPlayer *player,
 	 */
 	if (player->priv->current_playing_source != NULL
 	    && rb_source_can_pause (player->priv->source)
-	    && rb_player_get_time (player->priv->mmplayer) > 3) {
+	    && rb_player_get_time (player->priv->mmplayer) > (G_GINT64_CONSTANT (3) * RB_PLAYER_SECOND)) {
 		rb_debug ("after 3 second previous, restarting song");
 		rb_player_set_time (player->priv->mmplayer, 0);
 		rb_shell_player_sync_with_source (player);
@@ -2897,7 +2868,7 @@ rb_shell_player_sync_with_source (RBShellPlayer *player)
 	char *streaming_artist = NULL;
 	RhythmDBEntry *entry;
 	char *title = NULL;
-	long elapsed;
+	gint64 elapsed;
 
 	entry = rb_shell_player_get_playing_entry (player);
 	rb_debug ("playing source: %p, active entry: %p", player->priv->current_playing_source, entry);
@@ -2955,7 +2926,7 @@ rb_shell_player_sync_with_source (RBShellPlayer *player)
 	elapsed = rb_player_get_time (player->priv->mmplayer);
 	if (elapsed < 0)
 		elapsed = 0;
-	player->priv->elapsed = elapsed;
+	player->priv->elapsed = elapsed / RB_PLAYER_SECOND;
 
 	g_signal_emit (G_OBJECT (player), rb_shell_player_signals[WINDOW_TITLE_CHANGED], 0,
 		       title);
@@ -3259,7 +3230,7 @@ rb_shell_player_get_playing_time (RBShellPlayer *player,
 	ptime = rb_player_get_time (player->priv->mmplayer);
 	if (ptime >= 0) {
 		if (time != NULL) {
-			*time = (guint)ptime;
+			*time = (guint)(ptime / RB_PLAYER_SECOND);
 		}
 		return TRUE;
 	} else {
@@ -3292,7 +3263,7 @@ rb_shell_player_set_playing_time (RBShellPlayer *player,
 			rb_debug ("forgetting that playing entry had EOS'd due to seek");
 			player->priv->playing_entry_eos = FALSE;
 		}
-		rb_player_set_time (player->priv->mmplayer, (long) time);
+		rb_player_set_time (player->priv->mmplayer, ((gint64) time) * RB_PLAYER_SECOND);
 		return TRUE;
 	} else {
 		g_set_error (error,
@@ -3317,10 +3288,10 @@ rb_shell_player_seek (RBShellPlayer *player, long offset)
 	g_return_if_fail (RB_IS_SHELL_PLAYER (player));
 
 	if (rb_player_seekable (player->priv->mmplayer)) {
-		long t = rb_player_get_time (player->priv->mmplayer);
+		gint64 t = rb_player_get_time (player->priv->mmplayer);
 		if (t < 0)
 			t = 0;
-		rb_player_set_time (player->priv->mmplayer, t + offset);
+		rb_player_set_time (player->priv->mmplayer, t + (offset * RB_PLAYER_SECOND));
 	}
 }
 
@@ -3493,14 +3464,15 @@ error_cb (RBPlayer *mmplayer,
 static void
 tick_cb (RBPlayer *mmplayer,
 	 RhythmDBEntry *entry,
-	 long elapsed,
-	 long duration,
+	 gint64 elapsed,
+	 gint64 duration,
 	 gpointer data)
 {
  	RBShellPlayer *player = RB_SHELL_PLAYER (data);
-	gint remaining_check = 0;
+	gint64 remaining_check = 0;
 	gboolean duration_from_player = TRUE;
 	const char *uri;
+	long elapsed_sec;
 
 	GDK_THREADS_ENTER ();
 
@@ -3514,35 +3486,36 @@ tick_cb (RBPlayer *mmplayer,
 	 * value from the entry, if any.
 	 */
 	if (duration < 1) {
-		duration = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION);
+		duration = rhythmdb_entry_get_ulong (entry, RHYTHMDB_PROP_DURATION) * RB_PLAYER_SECOND;
 		duration_from_player = FALSE;
 	}
 
 	uri = rhythmdb_entry_get_string (entry, RHYTHMDB_PROP_LOCATION);
-	rb_debug ("tick: [%s, %lu:%lu(%d)]",
+	rb_debug ("tick: [%s, %" G_GINT64_FORMAT ":%" G_GINT64_FORMAT "(%d)]",
 		  uri,
 		  elapsed,
 		  duration,
 		  duration_from_player);
 
-	if (TRUE /* rb_player_playing (mmplayer)*/) {		/* why do we care whether it's playing or not? */
-		if (elapsed < 0)
-			elapsed = 0;
+	if (elapsed < 0) {
+		elapsed_sec = 0;
+	} else {
+		elapsed_sec = elapsed / RB_PLAYER_SECOND;
+	}
 
-		if (player->priv->elapsed != elapsed) {
-			player->priv->elapsed = elapsed;
-			g_signal_emit (G_OBJECT (player), rb_shell_player_signals[ELAPSED_CHANGED],
-				       0, player->priv->elapsed);
-		}
+	if (player->priv->elapsed != elapsed_sec) {
+		player->priv->elapsed = elapsed_sec;
+		g_signal_emit (G_OBJECT (player), rb_shell_player_signals[ELAPSED_CHANGED],
+			       0, player->priv->elapsed);
+	}
 
-		if (duration_from_player) {
-			/* XXX update duration in various things? */
-		}
+	if (duration_from_player) {
+		/* XXX update duration in various things? */
 	}
 
 	/* check if we should start a crossfade */
 	if (rb_player_multiple_open (mmplayer)) {
-		if (player->priv->track_transition_time == 0) {
+		if (player->priv->track_transition_time < PREROLL_TIME) {
 			remaining_check = PREROLL_TIME;
 		} else {
 			remaining_check = player->priv->track_transition_time;
@@ -3558,7 +3531,10 @@ tick_cb (RBPlayer *mmplayer,
 	    duration > 0 &&
 	    elapsed > 0 &&
 	    ((duration - elapsed) <= remaining_check)) {
-		rb_debug ("%ld seconds remaining in stream %s; need %d for transition", duration - elapsed, uri, remaining_check);
+		rb_debug ("%" G_GINT64_FORMAT " ns remaining in stream %s; need %" G_GINT64_FORMAT " for transition",
+			  duration - elapsed,
+			  uri,
+			  remaining_check);
 		rb_shell_player_handle_eos_unlocked (player, entry, FALSE);
 	}
 
@@ -3787,7 +3763,7 @@ gconf_track_transition_time_changed (GConfClient *client,
 				     RBShellPlayer *player)
 {
 	rb_debug ("track transition time changed");
-	player->priv->track_transition_time = (int)(eel_gconf_get_float (CONF_PLAYER_TRANSITION_TIME));
+	player->priv->track_transition_time = eel_gconf_get_float (CONF_PLAYER_TRANSITION_TIME) * RB_PLAYER_SECOND;
 }
 
 static void



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