Beispiel #1
0
gboolean
xmr_player_play(XmrPlayer *player)
{
	XmrPlayerPrivate *priv;

	g_return_val_if_fail( player != NULL, FALSE);
	priv = player->priv;

	g_return_val_if_fail( priv->playbin != NULL, FALSE);
	g_return_val_if_fail( priv->uri != NULL, FALSE);

	if (priv->stream_change_pending == FALSE)
	{
		xmr_debug ("no stream change pending, just restarting playback");
		start_state_change (player, GST_STATE_PLAYING, FINISH_TRACK_CHANGE);
	}
	else if (priv->current_track_finishing)
	{
		xmr_debug ("current track finishing -> just setting URI on playbin");
		g_object_set(priv->playbin, "uri", priv->uri, NULL);

		track_change_done(player, NULL);
	}
	else
	{
		xmr_debug ("play next song");
		start_state_change(player, GST_STATE_READY, SET_NEXT_URI);
	}

	return TRUE;
}
Beispiel #2
0
static gboolean
impl_play (RBPlayer *player, RBPlayerPlayType play_type, gint64 crossfade, GError **error)
{
	RBPlayerGst *mp = RB_PLAYER_GST (player);

	g_return_val_if_fail (mp->priv->playbin != NULL, FALSE);

	mp->priv->track_change = TRUE;

	if (mp->priv->stream_change_pending == FALSE) {
		rb_debug ("no stream change pending, just restarting playback");
		mp->priv->track_change = FALSE;
		start_state_change (mp, GST_STATE_PLAYING, FINISH_TRACK_CHANGE);
	} else if (mp->priv->current_track_finishing) {
		switch (play_type) {
		case RB_PLAYER_PLAY_AFTER_EOS:
			rb_debug ("current track finishing -> just setting URI on playbin");
			g_object_set (mp->priv->playbin, "uri", mp->priv->uri, NULL);
			mp->priv->playbin_stream_changing = TRUE;
			track_change_done (mp, NULL);
			break;

		case RB_PLAYER_PLAY_REPLACE:
		case RB_PLAYER_PLAY_CROSSFADE:
			rb_debug ("current track finishing, waiting for EOS to start next");
			break;

		default:
			g_assert_not_reached ();
		}
	} else {
		gboolean reused = FALSE;

		/* try to reuse the stream */
		if (mp->priv->prev_uri != NULL) {
			g_signal_emit (mp,
				       signals[CAN_REUSE_STREAM], 0,
				       mp->priv->uri, mp->priv->prev_uri, mp->priv->playbin,
				       &reused);

			if (reused) {
				rb_debug ("reusing stream to switch from %s to %s", mp->priv->prev_uri, mp->priv->uri);
				g_signal_emit (player,
					       signals[REUSE_STREAM], 0,
					       mp->priv->uri, mp->priv->prev_uri, mp->priv->playbin);
				track_change_done (mp, *error);
			}
		}

		/* no stream reuse, so stop, set the new URI, then start */
		if (reused == FALSE) {
			rb_debug ("not in transition, stopping current track to start the new one");
			start_state_change (mp, GST_STATE_READY, SET_NEXT_URI);
		}

	}

	return TRUE;
}
Beispiel #3
0
static gboolean
impl_close (RBPlayer *player, const char *uri, GError **error)
{
	RBPlayerGst *mp = RB_PLAYER_GST (player);

	if ((uri != NULL) && (mp->priv->uri != NULL) && strcmp (mp->priv->uri, uri) == 0) {
		rb_debug ("URI doesn't match current playing URI; ignoring");
		return TRUE;
	}

	mp->priv->playing = FALSE;
	mp->priv->buffering = FALSE;
	mp->priv->current_track_finishing = FALSE;

	_destroy_stream_data (mp);
	if (uri == NULL) {
		_destroy_next_stream_data (mp);
	}
	g_free (mp->priv->uri);
	g_free (mp->priv->prev_uri);
	mp->priv->uri = NULL;
	mp->priv->prev_uri = NULL;

	if (mp->priv->tick_timeout_id != 0) {
		g_source_remove (mp->priv->tick_timeout_id);
		mp->priv->tick_timeout_id = 0;
	}

	if (mp->priv->playbin != NULL) {
		start_state_change (mp, GST_STATE_NULL, PLAYER_SHUTDOWN);
	}
	return TRUE;
}
Beispiel #4
0
static void
state_change_finished (RBPlayerGst *mp, GError *error)
{
	enum StateChangeAction action = mp->priv->state_change_action;
	mp->priv->state_change_action = DO_NOTHING;

	switch (action) {
	case DO_NOTHING:
		break;

	case PLAYER_SHUTDOWN:
		if (error != NULL) {
			g_warning ("unable to shut down player pipeline: %s\n", error->message);
		}
		break;

	case SET_NEXT_URI:
		if (error != NULL) {
			g_warning ("unable to stop playback: %s\n", error->message);
		} else {
			GstBus *bus;

			/* flush bus to ensure tags from the previous stream don't
			 * get applied to the new one
			 */
			bus = gst_element_get_bus (mp->priv->playbin);
			gst_bus_set_flushing (bus, TRUE);
			gst_bus_set_flushing (bus, FALSE);
			gst_object_unref (bus);

			rb_debug ("setting new playback URI %s", mp->priv->uri);
			g_object_set (mp->priv->playbin, "uri", mp->priv->uri, NULL);
			start_state_change (mp, GST_STATE_PLAYING, FINISH_TRACK_CHANGE);
		}
		break;

	case STOP_TICK_TIMER:
		if (error != NULL) {
			g_warning ("unable to pause playback: %s\n", error->message);
		} else {
			if (mp->priv->tick_timeout_id != 0) {
				g_source_remove (mp->priv->tick_timeout_id);
				mp->priv->tick_timeout_id = 0;
			}
		}
		break;

	case FINISH_TRACK_CHANGE:
		track_change_done (mp, error);
		break;
	}
}
Beispiel #5
0
gboolean
xmr_player_resume(XmrPlayer *player)
{
	g_return_val_if_fail( player != NULL, FALSE);
	g_return_val_if_fail( player->priv->playbin != NULL, FALSE);

	if (player->priv->playing)
		return TRUE;

	start_state_change(player, GST_STATE_PLAYING, FINISH_TRACK_CHANGE);

	return TRUE;
}
Beispiel #6
0
static void
impl_pause (RBPlayer *player)
{
	RBPlayerGst *mp = RB_PLAYER_GST (player);

	if (!mp->priv->playing)
		return;

	mp->priv->playing = FALSE;

	g_return_if_fail (mp->priv->playbin != NULL);

	start_state_change (mp, GST_STATE_PAUSED, STOP_TICK_TIMER);
}
Beispiel #7
0
void	
xmr_player_pause(XmrPlayer *player)
{
	XmrPlayerPrivate *priv;

	g_return_if_fail( player != NULL);
	priv = player->priv;

	if (!priv->playing)
		return ;

	priv->playing = FALSE;
	g_return_if_fail( priv->playbin != NULL);

	start_state_change(player, GST_STATE_PAUSED, STOP_TICK_TIMER);
}
Beispiel #8
0
static void
state_change_finished(XmrPlayer *player, GError *error)
{
	XmrPlayerPrivate *priv = player->priv;
	enum StateChangeAction action = priv->state_change_action;
	priv->state_change_action = DO_NOTHING;

	switch (action)
	{
	case DO_NOTHING:
		break;

	case PLAYER_SHUTDOWN:
		if (error != NULL) {
			g_warning ("unable to shut down player pipeline: %s\n", error->message);
		}
		break;

	case SET_NEXT_URI:
		if (error != NULL) {
			g_warning ("unable to stop playback: %s\n", error->message);
		} else {
			xmr_debug ("setting new playback URI %s", priv->uri);
			g_object_set (priv->playbin, "uri", priv->uri, NULL);
			start_state_change (player, GST_STATE_PLAYING, FINISH_TRACK_CHANGE);
		}
		break;

	case STOP_TICK_TIMER:
		if (error != NULL) {
			g_warning ("unable to pause playback: %s\n", error->message);
		} else {
			if (priv->tick_timeout_id != 0) {
				g_source_remove (priv->tick_timeout_id);
				priv->tick_timeout_id = 0;
			}
		}
		break;

	case FINISH_TRACK_CHANGE:
		track_change_done (player, error);
		break;
	}
}
Beispiel #9
0
static gboolean
bus_cb (GstBus *bus, GstMessage *message, RBPlayerGst *mp)
{
	const GstStructure *structure;
	g_return_val_if_fail (mp != NULL, FALSE);

	switch (GST_MESSAGE_TYPE (message)) {
	case GST_MESSAGE_ERROR: {
		char *debug = NULL;
		GError *error = NULL;
		GError *sig_error = NULL;
		int code;
		gboolean emit = TRUE;

		gst_message_parse_error (message, &error, &debug);

		/* If we've already got an error, ignore 'internal data flow error'
		 * type messages, as they're too generic to be helpful.
		 */
		if (mp->priv->emitted_error &&
		    error->domain == GST_STREAM_ERROR &&
		    error->code == GST_STREAM_ERROR_FAILED) {
			rb_debug ("Ignoring generic error \"%s\"", error->message);
			emit = FALSE;
		}

		code = rb_gst_error_get_error_code (error);

		if (emit) {
			if (message_from_sink (mp->priv->audio_sink, message)) {
				rb_debug ("got error from sink: %s (%s)", error->message, debug);
				/* Translators: the parameter here is an error message */
				g_set_error (&sig_error,
					     RB_PLAYER_ERROR,
					     code,
					     _("Failed to open output device: %s"),
					     error->message);
			} else {
				rb_debug ("got error from stream: %s (%s)", error->message, debug);
				g_set_error (&sig_error,
					     RB_PLAYER_ERROR,
					     code,
					     "%s",
					     error->message);
			}
			state_change_finished (mp, sig_error);
			mp->priv->emitted_error = TRUE;
			if (mp->priv->playbin_stream_changing) {
				emit_playing_stream_and_tags (mp, TRUE);
			}
			_rb_player_emit_error (RB_PLAYER (mp), mp->priv->stream_data, sig_error);
		}

		/* close if not already closing */
		if (mp->priv->uri != NULL)
			rb_player_close (RB_PLAYER (mp), NULL, NULL);

		g_error_free (error);
		g_free (debug);
		break;
	}

	case GST_MESSAGE_EOS:
		if (mp->priv->stream_change_pending) {
			rb_debug ("got EOS with stream change pending");
			start_state_change (mp, GST_STATE_READY, SET_NEXT_URI);
		} else {
			_rb_player_emit_eos (RB_PLAYER (mp), mp->priv->stream_data, FALSE);
		}
		break;

	case GST_MESSAGE_STATE_CHANGED:
		{
			GstState oldstate;
			GstState newstate;
			GstState pending;
			gst_message_parse_state_changed (message, &oldstate, &newstate, &pending);
			if (GST_MESSAGE_SRC (message) == GST_OBJECT (mp->priv->playbin)) {
				if (pending == GST_STATE_VOID_PENDING) {
					rb_debug ("playbin reached state %s", gst_element_state_get_name (newstate));
					state_change_finished (mp, NULL);
				}
			}
			break;
		}

	case GST_MESSAGE_TAG: {
		GstTagList *tags;

		if (mp->priv->playbin_stream_changing) {
			rb_debug ("ignoring tags during playbin stream change");
			break;
		}

		gst_message_parse_tag (message, &tags);

		if (mp->priv->stream_change_pending) {
			mp->priv->stream_tags = g_list_append (mp->priv->stream_tags, tags);
		} else {
			gst_tag_list_foreach (tags, (GstTagForeachFunc) process_tag, mp);
			gst_tag_list_free (tags);
		}
		break;
	}


	case GST_MESSAGE_BUFFERING: {
		gint progress;

		structure = gst_message_get_structure (message);
		if (!gst_structure_get_int (structure, "buffer-percent", &progress)) {
			g_warning ("Could not get value from BUFFERING message");
			break;
		}

		if (progress >= 100) {
			mp->priv->buffering = FALSE;
			if (mp->priv->playing) {
				rb_debug ("buffering done, setting pipeline back to PLAYING");
				gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
			} else {
				rb_debug ("buffering done, leaving pipeline PAUSED");
			}
		} else if (mp->priv->buffering == FALSE && mp->priv->playing) {

			rb_debug ("buffering - temporarily pausing playback");
			gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED);
			mp->priv->buffering = TRUE;
		}

		_rb_player_emit_buffering (RB_PLAYER (mp), mp->priv->stream_data, progress);
		break;
	}

	case GST_MESSAGE_APPLICATION:
		structure = gst_message_get_structure (message);
		_rb_player_emit_event (RB_PLAYER (mp), mp->priv->stream_data, gst_structure_get_name (structure), NULL);
		break;

	case GST_MESSAGE_STREAM_START:
		if (mp->priv->playbin_stream_changing) {
			rb_debug ("got STREAM_START message");
			mp->priv->playbin_stream_changing = FALSE;
			emit_playing_stream_and_tags (mp, TRUE);
		}
		break;

	case GST_MESSAGE_ELEMENT:
		structure = gst_message_get_structure (message);
		if (gst_is_missing_plugin_message (message)) {
			handle_missing_plugin_message (mp, message);
		} else if (gst_structure_has_name (structure, "redirect")) {
			const char *uri = gst_structure_get_string (structure, "new-location");
			_rb_player_emit_redirect (RB_PLAYER (mp), mp->priv->stream_data, uri);
		}
		break;

	default:
		break;
	}

	/* emit message signals too, so plugins can process messages */
	gst_bus_async_signal_func (bus, message, NULL);

	return TRUE;
}