static void process_tag (const GstTagList *list, const gchar *tag, RBPlayerGst *player) { RBMetaDataField field; GValue value = {0,}; /* process embedded images */ if (!g_strcmp0 (tag, GST_TAG_IMAGE) || !g_strcmp0 (tag, GST_TAG_PREVIEW_IMAGE)) { GdkPixbuf *pixbuf; pixbuf = rb_gst_process_embedded_image (list, tag); if (pixbuf != NULL) { _rb_player_emit_image (RB_PLAYER (player), player->priv->stream_data, pixbuf); g_object_unref (pixbuf); } } else if (rb_gst_process_tag_string (list, tag, &field, &value)) { rb_debug ("emitting info field %d", field); _rb_player_emit_info (RB_PLAYER (player), player->priv->stream_data, field, &value); g_value_unset (&value); } }
static gboolean tick_timeout (RBPlayerGst *mp) { if (mp->priv->playing) { _rb_player_emit_tick (RB_PLAYER (mp), mp->priv->stream_data, rb_player_get_time (RB_PLAYER (mp)), -1); } return TRUE; }
static void emit_playing_stream_and_tags (RBPlayerGst *player, gboolean track_change) { GList *t; if (track_change) { /* swap stream data */ _destroy_stream_data (player); player->priv->stream_data = player->priv->next_stream_data; player->priv->stream_data_destroy = player->priv->next_stream_data_destroy; player->priv->next_stream_data = NULL; player->priv->next_stream_data_destroy = NULL; } _rb_player_emit_playing_stream (RB_PLAYER (player), player->priv->stream_data); /* process any tag lists we received while starting the stream */ for (t = player->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, player); gst_tag_list_free (tags); } g_list_free (player->priv->stream_tags); player->priv->stream_tags = NULL; }
static gboolean about_to_finish_idle (RBPlayerGst *player) { _rb_player_emit_eos (RB_PLAYER (player), player->priv->stream_data, TRUE); g_mutex_lock (&player->priv->eos_lock); g_cond_signal (&player->priv->eos_cond); g_mutex_unlock (&player->priv->eos_lock); return FALSE; }
static gboolean impl_add_filter (RBPlayerGstFilter *player, GstElement *element) { RBPlayerGst *mp = RB_PLAYER_GST (player); if (mp->priv->filterbin == NULL) { mp->priv->waiting_filters = g_list_prepend (mp->priv->waiting_filters, element); return TRUE; } return rb_gst_add_filter (RB_PLAYER (mp), mp->priv->filterbin, element, need_pad_blocking (mp)); }
static gboolean impl_add_tee (RBPlayerGstTee *player, GstElement *element) { RBPlayerGst *mp = RB_PLAYER_GST (player); if (mp->priv->tee == NULL) { mp->priv->waiting_tees = g_list_prepend (mp->priv->waiting_tees, element); return TRUE; } return rb_gst_add_tee (RB_PLAYER (player), mp->priv->tee, element, need_pad_blocking (mp)); }
static gboolean impl_remove_tee (RBPlayerGstTee *player, GstElement *element) { RBPlayerGst *mp = RB_PLAYER_GST (player); if (mp->priv->tee == NULL) { gst_object_sink (element); mp->priv->waiting_tees = g_list_remove (mp->priv->waiting_tees, element); return TRUE; } return rb_gst_remove_tee (RB_PLAYER (mp), mp->priv->tee, element, need_pad_blocking (mp)); }
static gboolean impl_remove_filter (RBPlayerGstFilter *player, GstElement *element) { RBPlayerGst *mp = RB_PLAYER_GST (player); if (mp->priv->filterbin == NULL) { gst_object_sink (element); mp->priv->waiting_filters = g_list_remove (mp->priv->waiting_filters, element); return TRUE; } return rb_gst_remove_filter (RB_PLAYER (mp), mp->priv->filterbin, element, need_pad_blocking (mp)); }
static gboolean tick_timeout (RBPlayerGst *mp) { if (mp->priv->playing) { gint64 position; position = rb_player_get_time (RB_PLAYER (mp)); /* if we don't have stream-changed messages, do the track change when * the playback position is less than one second into the current track, * which pretty much has to be the new one. */ if (mp->priv->playbin_stream_changing && (position < GST_SECOND)) { emit_playing_stream_and_tags (mp, TRUE); mp->priv->playbin_stream_changing = FALSE; } _rb_player_emit_tick (RB_PLAYER (mp), mp->priv->stream_data, position, -1); } return TRUE; }
static gboolean emit_volume_changed_idle (RBPlayerGst *player) { double vol; if (gst_element_implements_interface (player->priv->playbin, GST_TYPE_STREAM_VOLUME)) { vol = gst_stream_volume_get_volume (GST_STREAM_VOLUME (player->priv->playbin), GST_STREAM_VOLUME_FORMAT_CUBIC); } else { vol = player->priv->cur_volume; } _rb_player_emit_volume_changed (RB_PLAYER (player), vol); return FALSE; }
static void about_to_finish_cb (GstElement *playbin, RBPlayerGst *player) { if (player->priv->stream_change_pending == TRUE) { /* this probably shouldn't happen, but it's OK if it does, I think */ rb_debug ("got about-to-finish, but we already have a stream change pending."); return; } /* don't handle about-to-finish for cdda */ if (g_str_has_prefix (player->priv->uri, "cdda://")) { rb_debug ("ignoring about-to-finish for %s", player->priv->uri); return; } /* emit EOS now and hope we get something to play */ player->priv->current_track_finishing = TRUE; _rb_player_emit_eos (RB_PLAYER (player), player->priv->stream_data, TRUE); }
static gboolean actually_emit_stream_and_tags (RBPlayerGst *player) { GList *t; _rb_player_emit_playing_stream (RB_PLAYER (player), player->priv->stream_data); /* process any tag lists we received while starting the stream */ for (t = player->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, player); gst_tag_list_free (tags); } g_list_free (player->priv->stream_tags); player->priv->stream_tags = NULL; player->priv->emit_stream_idle_id = 0; return FALSE; }
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; 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; _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: _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)) { rb_debug ("playbin reached state %s", gst_element_state_get_name (newstate)); if (pending == GST_STATE_VOID_PENDING) { state_change_finished (mp, NULL); } } break; } case GST_MESSAGE_TAG: { GstTagList *tags; gst_message_parse_tag (message, &tags); if (mp->priv->stream_change_pending || mp->priv->playbin_stream_changing) { 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) { GstState cur_state; gst_element_get_state (mp->priv->playbin, &cur_state, NULL, 0); if (cur_state == GST_STATE_PLAYING) { rb_debug ("buffering - temporarily pausing playback"); gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED); } else { rb_debug ("buffering - during preroll; doing nothing"); } 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_ELEMENT: structure = gst_message_get_structure (message); if (gst_is_missing_plugin_message (message)) { handle_missing_plugin_message (mp, message); } else if (mp->priv->playbin_stream_changing && gst_structure_has_name (structure, "playbin2-stream-changed")) { rb_debug ("got playbin2-stream-changed message"); mp->priv->playbin_stream_changing = FALSE; emit_playing_stream_and_tags (mp, TRUE); } 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; }
RBPlayer * rb_player_gst_new (GError **error) { return RB_PLAYER (g_object_new (RB_TYPE_PLAYER_GST, NULL, NULL)); }