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; }
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; }
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; }
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; } }
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; }
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); }
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); }
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; } }
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; }