From 8acb7ee5c15c12ecbf08522a170972d6c337c43b Mon Sep 17 00:00:00 2001 From: Andy Owen Date: Sun, 8 Nov 2009 00:31:01 +1100 Subject: [PATCH] Attempt to keep elapsed time consistent when changing streams. --- backends/gstreamer/rb-player-gst.c | 175 +++++++++++++++++++++++------------- 1 files changed, 114 insertions(+), 61 deletions(-) diff --git a/backends/gstreamer/rb-player-gst.c b/backends/gstreamer/rb-player-gst.c index b08112e..c8efd1b 100644 --- a/backends/gstreamer/rb-player-gst.c +++ b/backends/gstreamer/rb-player-gst.c @@ -92,16 +92,20 @@ struct _RBPlayerGstPrivate gpointer stream_data; GDestroyNotify stream_data_destroy; + /* in the overlap between two streams, we need to track two stream_data objects */ + gpointer stream_data_next; + GDestroyNotify stream_data_destroy_next; + GstElement *playbin; GstElement *audio_sink; guint buffer_size; gboolean playing; gboolean buffering; + gint64 last_stream_position; gboolean stream_change_pending; gboolean current_track_finishing; - gboolean fake_playing_position; gboolean emitted_error; @@ -122,6 +126,8 @@ struct _RBPlayerGstPrivate GstElement *filterbin; }; +static void begin_stream_playback (RBPlayerGst *mp); + static gboolean tick_timeout (RBPlayerGst *mp) { @@ -130,7 +136,26 @@ tick_timeout (RBPlayerGst *mp) mp->priv->stream_data, rb_player_get_time (RB_PLAYER (mp)), -1); - mp->priv->fake_playing_position = FALSE; + if (mp->priv->last_stream_position > 0) { + gint64 position; + GstFormat fmt = GST_FORMAT_TIME; + + /* this is a hack to try to decide when the next stream has begun + * playbin2 doesn't (yet) have a way of finding out when it switches + * from one URI to the next, so we guess based on playing position + * sometimes this will trigger a bit too early, so at the end of a + * stream, the display will jump to the next track prematurely. + * working around this would be an uphill battle, and it would be + * better to just fix playbin2. */ + if (gst_element_query_position(mp->priv->playbin, &fmt, &position)) { + if (position < mp->priv->last_stream_position) { + begin_stream_playback (mp); + } + } + else { + begin_stream_playback (mp); + } + } } return TRUE; } @@ -662,6 +687,16 @@ _destroy_stream_data (RBPlayerGst *player) player->priv->stream_data_destroy = NULL; } +static void +_get_next_stream_data (RBPlayerGst *mp) +{ + mp->priv->stream_data = mp->priv->stream_data_next; + mp->priv->stream_data_next = NULL; + + mp->priv->stream_data_destroy = mp->priv->stream_data_destroy_next; + mp->priv->stream_data_destroy_next = NULL; +} + static gboolean impl_close (RBPlayer *player, const char *uri, GError **error) { @@ -676,6 +711,8 @@ impl_close (RBPlayer *player, const char *uri, GError **error) mp->priv->buffering = FALSE; _destroy_stream_data (mp); + _get_next_stream_data (mp); + _destroy_stream_data (mp); g_free (mp->priv->uri); g_free (mp->priv->prev_uri); mp->priv->uri = NULL; @@ -715,12 +752,11 @@ impl_open (RBPlayer *player, } rb_debug ("setting new uri to %s", uri); - _destroy_stream_data (mp); g_free (mp->priv->prev_uri); mp->priv->prev_uri = mp->priv->uri; mp->priv->uri = g_strdup (uri); - mp->priv->stream_data = stream_data; - mp->priv->stream_data_destroy = stream_data_destroy; + mp->priv->stream_data_next = stream_data; + mp->priv->stream_data_destroy_next = stream_data_destroy; mp->priv->emitted_error = FALSE; mp->priv->stream_change_pending = TRUE; @@ -754,6 +790,64 @@ set_playbin_volume (RBPlayerGst *player, float volume) g_signal_handlers_unblock_by_func (player->priv->playbin, volume_notify_cb, player); } +static void +begin_stream_playback (RBPlayerGst *mp) +{ + GList *t; + + mp->priv->current_track_finishing = FALSE; + mp->priv->buffering = FALSE; + mp->priv->playing = TRUE; + if (mp->priv->stream_data_next) { + _destroy_stream_data(mp); + _get_next_stream_data (mp); + } + + _rb_player_emit_playing_stream (RB_PLAYER (mp), mp->priv->stream_data); + + if (mp->priv->tick_timeout_id == 0) { + mp->priv->tick_timeout_id = + g_timeout_add (1000 / RB_PLAYER_GST_TICK_HZ, + (GSourceFunc) tick_timeout, + mp); + } + + if (mp->priv->volume_applied == 0) { + GstElement *e; + + /* if the sink provides volume control, ignore the first + * volume setting, allowing the sink to restore its own + * volume. + */ + e = rb_player_gst_find_element_with_property (mp->priv->audio_sink, "volume"); + if (e != NULL) { + mp->priv->volume_applied = 1; + gst_object_unref (e); + } + + if (mp->priv->volume_applied < mp->priv->volume_changed) { + float volume = mp->priv->cur_volume * mp->priv->replaygain_scale; + rb_debug ("applying initial volume: %f", volume); + set_playbin_volume (mp, volume); + } + + mp->priv->volume_applied = mp->priv->volume_changed; + } + + /* process any tag lists we received while starting the stream */ + for (t = mp->priv->stream_tags; t != NULL; t = t->next) { + GstTagList *tags; + + tags = (GstTagList *)t->data; + rb_debug ("processing buffered taglist"); + gst_tag_list_foreach (tags, (GstTagForeachFunc) process_tag, mp); + gst_tag_list_free (tags); + } + g_list_free (mp->priv->stream_tags); + mp->priv->stream_tags = NULL; + mp->priv->last_stream_position = 0; +} + static gboolean impl_play (RBPlayer *player, RBPlayerPlayType play_type, gint64 crossfade, GError **error) { @@ -770,13 +864,6 @@ impl_play (RBPlayer *player, RBPlayerPlayType play_type, gint64 crossfade, GErro rb_debug ("current track finishing -> just setting URI on playbin"); g_object_set (mp->priv->playbin, "uri", mp->priv->uri, NULL); result = TRUE; - - /* since we don't know exactly when the change occurs, pretend the playing position - * is 0 until the next tick. otherwise we sometimes get the playing position from the - * previous track. - */ - mp->priv->fake_playing_position = TRUE; - } else { gboolean reused = FALSE; @@ -811,54 +898,23 @@ impl_play (RBPlayer *player, RBPlayerPlayType play_type, gint64 crossfade, GErro mp->priv->stream_change_pending = FALSE; if (result) { - GList *t; - - mp->priv->current_track_finishing = FALSE; - mp->priv->buffering = FALSE; - mp->priv->playing = TRUE; - - _rb_player_emit_playing_stream (RB_PLAYER (mp), mp->priv->stream_data); - - if (mp->priv->tick_timeout_id == 0) { - mp->priv->tick_timeout_id = - g_timeout_add (1000 / RB_PLAYER_GST_TICK_HZ, - (GSourceFunc) tick_timeout, - mp); - } - - if (mp->priv->volume_applied == 0) { - GstElement *e; - - /* if the sink provides volume control, ignore the first - * volume setting, allowing the sink to restore its own - * volume. - */ - e = rb_player_gst_find_element_with_property (mp->priv->audio_sink, "volume"); - if (e != NULL) { - mp->priv->volume_applied = 1; - gst_object_unref (e); - } - - if (mp->priv->volume_applied < mp->priv->volume_changed) { - float volume = mp->priv->cur_volume * mp->priv->replaygain_scale; - rb_debug ("applying initial volume: %f", volume); - set_playbin_volume (mp, volume); + if (play_type == RB_PLAYER_PLAY_AFTER_EOS) { + /* playbin2 doesn't provide a method for us to tell us when + * it starts playing our new URI. to work around this we + * store the current position of the stream, which will + * continue increasing, until it starts the new URI, and + * gets reset to 0 */ + GstFormat fmt = GST_FORMAT_TIME; + if (!gst_element_query_position(mp->priv->playbin, &fmt, + &mp->priv->last_stream_position)) { + /* couldn't get current position, start the next track */ + mp->priv->last_stream_position = 0; + begin_stream_playback(mp); } - - mp->priv->volume_applied = mp->priv->volume_changed; } - - /* process any tag lists we received while starting the stream */ - for (t = mp->priv->stream_tags; t != NULL; t = t->next) { - GstTagList *tags; - - tags = (GstTagList *)t->data; - rb_debug ("processing buffered taglist"); - gst_tag_list_foreach (tags, (GstTagForeachFunc) process_tag, mp); - gst_tag_list_free (tags); + else { + begin_stream_playback(mp); } - g_list_free (mp->priv->stream_tags); - mp->priv->stream_tags = NULL; } return result; @@ -1025,10 +1081,7 @@ impl_get_time (RBPlayer *player) { RBPlayerGst *mp = RB_PLAYER_GST (player); - if (mp->priv->fake_playing_position) { - /* track transition occurring, so pretend we're at the start of the new one */ - return 0; - } else if (mp->priv->playbin != NULL) { + if (mp->priv->playbin != NULL) { gint64 position = -1; GstFormat fmt = GST_FORMAT_TIME; -- 1.6.3.3