[rhythmbox] player: rewrite non-crossfading backend using playbin2 (bug #542922)
- From: Jonathan Matthew <jmatthew src gnome org>
- To: svn-commits-list gnome org
- Subject: [rhythmbox] player: rewrite non-crossfading backend using playbin2 (bug #542922)
- Date: Sat, 23 May 2009 07:41:48 -0400 (EDT)
commit 11b1567a6462bfb333a227d8bb571f3777e0144f
Author: Jonathan Matthew <jonathan d14n org>
Date: Sat May 23 21:33:34 2009 +1000
player: rewrite non-crossfading backend using playbin2 (bug #542922)
Amongst other things, playbin2 does chained oggs correctly (bug #396409)
and doesn't require us to have a video sink running at all times in case
the user enables visualization (bug #406807).
The fake visualizer element and associated hackery can now be removed
from the visualization plugin, since we can just enable and disable vis
as required.
playbin2 exposes a network buffer size property, so we don't need to
disable the slider controlling that any more.
This increases the GStreamer version requirement to 0.10.20.
---
backends/gstreamer/rb-player-gst.c | 1162 +++++++++++++++--------------
configure.ac | 2 +-
plugins/visualizer/Makefile.am | 3 +-
plugins/visualizer/rb-fake-visualizer.c | 609 ---------------
plugins/visualizer/rb-visualizer-plugin.c | 62 +-
shell/rb-shell-preferences.c | 1 -
6 files changed, 640 insertions(+), 1199 deletions(-)
diff --git a/backends/gstreamer/rb-player-gst.c b/backends/gstreamer/rb-player-gst.c
index 7a35e41..bcae010 100644
--- a/backends/gstreamer/rb-player-gst.c
+++ b/backends/gstreamer/rb-player-gst.c
@@ -1,9 +1,8 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
- * arch-tag: Implementation of GStreamer backends, with workarounds for bugs
- *
* Copyright (C) 2003 Jorn Baayen <jorn nl linux org>
* Copyright (C) 2003,2004 Colin Walters <walters debian org>
+ * Copyright (C) 2009 Jonathan Matthew <jonathan d14n org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -44,59 +43,32 @@
#include "rb-util.h"
#include "rb-player.h"
-#include "rb-player-gst-filter.h"
-#include "rb-player-gst-tee.h"
-/*#include "rb-player-gst-data-tee.h"*/
#include "rb-player-gst.h"
#include "rb-player-gst-helper.h"
+#include "rb-player-gst-filter.h"
+#include "rb-player-gst-tee.h"
static void rb_player_init (RBPlayerIface *iface);
static void rb_player_gst_filter_init (RBPlayerGstFilterIface *iface);
static void rb_player_gst_tee_init (RBPlayerGstTeeIface *iface);
-/*tatic void rb_player_gst_data_tee_init (RBPlayerGstDataTeeIface *iface);*/
-static void rb_player_gst_finalize (GObject *object);
-static void rb_player_gst_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec);
-
-static gboolean rb_player_gst_open (RBPlayer *player,
- const char *uri,
- gpointer stream_data,
- GDestroyNotify stream_data_destroy,
- GError **error);
-static gboolean rb_player_gst_opened (RBPlayer *player);
-static gboolean rb_player_gst_close (RBPlayer *player,
- const char *uri,
- GError **error);
-static gboolean rb_player_gst_play (RBPlayer *player, gint crossfade, GError **error);
-static void rb_player_gst_pause (RBPlayer *player);
-static gboolean rb_player_gst_playing (RBPlayer *player);
-static void rb_player_gst_set_volume (RBPlayer *player, float volume);
-static float rb_player_gst_get_volume (RBPlayer *player);
-static void rb_player_gst_set_replaygain (RBPlayer *player,
- const char *uri,
- double track_gain, double track_peak,
- double album_gain, double album_peak);
-static gboolean rb_player_gst_seekable (RBPlayer *player);
-static void rb_player_gst_set_time (RBPlayer *player, long time);
-static long rb_player_gst_get_time (RBPlayer *player);
G_DEFINE_TYPE_WITH_CODE(RBPlayerGst, rb_player_gst, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER, rb_player_init)
G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER_GST_FILTER, rb_player_gst_filter_init)
G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER_GST_TEE, rb_player_gst_tee_init)
- /*G_IMPLEMENT_INTERFACE(RB_TYPE_PLAYER_GST_DATA_TEE, rb_player_gst_data_tee_init)*/
)
-#define RB_PLAYER_GST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), RB_TYPE_PLAYER_GST, RBPlayerGstPrivate))
+
+#define MAX_NETWORK_BUFFER_SIZE (2048)
#define RB_PLAYER_GST_TICK_HZ 5
+#define STATE_CHANGE_MESSAGE_TIMEOUT 5
enum
{
PROP_0,
PROP_PLAYBIN,
- PROP_BUS
+ PROP_BUS,
+ PROP_BUFFER_SIZE
};
enum
@@ -109,170 +81,85 @@ static guint signals[LAST_SIGNAL] = { 0 };
struct _RBPlayerGstPrivate
{
+ char *prev_uri;
char *uri;
gpointer stream_data;
GDestroyNotify stream_data_destroy;
GstElement *playbin;
- GstElement *volume_handler;
-
- gboolean can_signal_direct_error;
- GError *error;
- gboolean emitted_error;
+ GstElement *audio_sink;
+ guint buffer_size;
gboolean playing;
gboolean buffering;
- GList *waiting_tees;
- GstElement *sinkbin;
- GstElement *tee;
+ gboolean stream_change_pending;
+ gboolean current_track_finishing;
- GList *waiting_filters; /* in reverse order */
- GstElement *filterbin;
+ gboolean emitted_error;
float cur_volume;
+ float replaygain_scale;
guint tick_timeout_id;
-};
-
-static gboolean rb_player_gst_sync_pipeline (RBPlayerGst *mp);
-static void rb_player_gst_gst_free_playbin (RBPlayerGst *player);
-
-static void
-rb_player_gst_class_init (RBPlayerGstClass *klass)
-{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- object_class->finalize = rb_player_gst_finalize;
- object_class->get_property = rb_player_gst_get_property;
-
- g_object_class_install_property (object_class,
- PROP_PLAYBIN,
- g_param_spec_object ("playbin",
- "playbin",
- "playbin element",
- GST_TYPE_ELEMENT,
- G_PARAM_READABLE));
- g_object_class_install_property (object_class,
- PROP_BUS,
- g_param_spec_object ("bus",
- "bus",
- "GStreamer message bus",
- GST_TYPE_BUS,
- G_PARAM_READABLE));
-
- signals[MISSING_PLUGINS] =
- g_signal_new ("missing-plugins",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- 0, /* no point handling this internally */
- NULL, NULL,
- rb_marshal_VOID__POINTER_POINTER_POINTER,
- G_TYPE_NONE,
- 3,
- G_TYPE_POINTER, G_TYPE_STRV, G_TYPE_STRV);
- g_type_class_add_private (klass, sizeof (RBPlayerGstPrivate));
-}
+ GList *waiting_tees;
+ GstElement *sinkbin;
+ GstElement *tee;
-static void
-rb_player_init (RBPlayerIface *iface)
-{
- iface->open = rb_player_gst_open;
- iface->opened = rb_player_gst_opened;
- iface->close = rb_player_gst_close;
- iface->play = rb_player_gst_play;
- iface->pause = rb_player_gst_pause;
- iface->playing = rb_player_gst_playing;
- iface->set_volume = rb_player_gst_set_volume;
- iface->get_volume = rb_player_gst_get_volume;
- iface->set_replaygain = rb_player_gst_set_replaygain;
- iface->seekable = rb_player_gst_seekable;
- iface->set_time = rb_player_gst_set_time;
- iface->get_time = rb_player_gst_get_time;
- iface->multiple_open = (RBPlayerFeatureFunc) rb_false_function;
-}
+ GList *waiting_filters; /* in reverse order */
+ GstElement *filterbin;
+};
static gboolean
tick_timeout (RBPlayerGst *mp)
{
- if (mp->priv->playing == FALSE)
- return TRUE;
-
- _rb_player_emit_tick (RB_PLAYER (mp), mp->priv->stream_data, rb_player_get_time (RB_PLAYER (mp)), -1);
-
+ 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
-rb_player_gst_init (RBPlayerGst *mp)
-{
- mp->priv = RB_PLAYER_GST_GET_PRIVATE (mp);
-}
static void
-rb_player_gst_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
+about_to_finish_cb (GstElement *playbin, RBPlayerGst *player)
{
- RBPlayerGst *mp = RB_PLAYER_GST (object);
-
- switch (prop_id) {
- case PROP_PLAYBIN:
- g_value_set_object (value, mp->priv->playbin);
- break;
- case PROP_BUS:
- if (mp->priv->playbin) {
- GstBus *bus;
- bus = gst_element_get_bus (mp->priv->playbin);
- g_value_set_object (value, bus);
- gst_object_unref (bus);
- }
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
+ 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;
}
-}
-static void
-rb_player_gst_finalize (GObject *object)
-{
- RBPlayerGst *mp;
-
- mp = RB_PLAYER_GST (object);
-
- if (mp->priv->tick_timeout_id != 0)
- g_source_remove (mp->priv->tick_timeout_id);
-
- if (mp->priv->playbin) {
- gst_element_set_state (mp->priv->playbin,
- GST_STATE_NULL);
-
- rb_player_gst_gst_free_playbin (mp);
- }
- if (mp->priv->volume_handler) {
- g_object_unref (mp->priv->volume_handler);
- mp->priv->volume_handler = NULL;
+ /* 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;
}
- if (mp->priv->waiting_tees) {
- g_list_foreach (mp->priv->waiting_tees, (GFunc)gst_object_sink, NULL);
- }
- g_list_free (mp->priv->waiting_tees);
+ /* 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);
+}
- G_OBJECT_CLASS (rb_player_gst_parent_class)->finalize (object);
+static gboolean
+emit_volume_changed_idle (RBPlayerGst *player)
+{
+ _rb_player_emit_volume_changed (RB_PLAYER (player), player->priv->cur_volume);
+ return FALSE;
}
static void
-rb_player_gst_gst_free_playbin (RBPlayerGst *player)
+volume_notify_cb (GObject *element, GParamSpec *pspec, RBPlayerGst *player)
{
- if (player->priv->playbin == NULL)
- return;
+ gdouble v;
+ g_object_get (element, "volume", &v, NULL);
+ player->priv->cur_volume = v;
- gst_object_unref (GST_OBJECT (player->priv->playbin));
- player->priv->playbin = NULL;
+ g_idle_add ((GSourceFunc) emit_volume_changed_idle, player);
}
static void
@@ -302,7 +189,7 @@ process_tag (const GstTagList *list, const gchar *tag, RBPlayerGst *player)
}
static void
-rb_player_gst_handle_missing_plugin_message (RBPlayerGst *player, GstMessage *message)
+handle_missing_plugin_message (RBPlayerGst *player, GstMessage *message)
{
char **details;
char **descriptions;
@@ -334,8 +221,9 @@ rb_player_gst_handle_missing_plugin_message (RBPlayerGst *player, GstMessage *me
}
static gboolean
-rb_player_gst_bus_cb (GstBus * bus, GstMessage * message, RBPlayerGst *mp)
+bus_cb (GstBus *bus, GstMessage *message, RBPlayerGst *mp)
{
+ const GstStructure *structure;
g_return_val_if_fail (mp != NULL, FALSE);
switch (GST_MESSAGE_TYPE (message)) {
@@ -381,9 +269,12 @@ rb_player_gst_bus_cb (GstBus * bus, GstMessage * message, RBPlayerGst *mp)
g_free (debug);
break;
}
+
case GST_MESSAGE_EOS:
+ rb_debug ("got EOS.. why haven't you told me what to do yet?");
_rb_player_emit_eos (RB_PLAYER (mp), mp->priv->stream_data);
break;
+
case GST_MESSAGE_TAG: {
GstTagList *tags;
gst_message_parse_tag (message, &tags);
@@ -392,12 +283,12 @@ rb_player_gst_bus_cb (GstBus * bus, GstMessage * message, RBPlayerGst *mp)
gst_tag_list_free (tags);
break;
}
+
case GST_MESSAGE_BUFFERING: {
- const GstStructure *s;
gint progress;
- s = gst_message_get_structure (message);
- if (!gst_structure_get_int (s, "buffer-percent", &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;
}
@@ -422,26 +313,21 @@ rb_player_gst_bus_cb (GstBus * bus, GstMessage * message, RBPlayerGst *mp)
mp->priv->buffering = TRUE;
}
- _rb_player_emit_buffering (RB_PLAYER (mp),
- mp->priv->stream_data,
- progress);
+ _rb_player_emit_buffering (RB_PLAYER (mp), mp->priv->stream_data, progress);
break;
}
- case GST_MESSAGE_APPLICATION: {
- const GstStructure *structure;
+ 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);
- }
- case GST_MESSAGE_ELEMENT: {
+ _rb_player_emit_event (RB_PLAYER (mp), mp->priv->stream_data, gst_structure_get_name (structure), NULL);
+ break;
+
+ case GST_MESSAGE_ELEMENT:
if (gst_is_missing_plugin_message (message)) {
- rb_player_gst_handle_missing_plugin_message (mp, message);
+ handle_missing_plugin_message (mp, message);
}
break;
- }
+
default:
break;
}
@@ -452,41 +338,84 @@ rb_player_gst_bus_cb (GstBus * bus, GstMessage * message, RBPlayerGst *mp)
return TRUE;
}
+static void
+cdda_got_source_cb (GObject *object, GParamSpec *pspec, char *device)
+{
+ GstElement *source;
+
+ g_object_get (object, "source", &source, NULL);
+ rb_debug ("got source %p", source);
+ if (source) {
+ g_signal_handlers_disconnect_by_func (object, cdda_got_source_cb, device);
+
+ g_object_set (G_OBJECT (source), "device", device, NULL);
+ g_free (device);
+
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
+ g_object_set (G_OBJECT (source), "paranoia-mode", 0, NULL);
+
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "read-speed"))
+ g_object_set (G_OBJECT (source), "read-speed", 1, NULL);
+ }
+}
+
static gboolean
-rb_player_gst_construct (RBPlayerGst *mp, GError **error)
+construct_pipeline (RBPlayerGst *mp, GError **error)
{
- char *element_name = NULL;
GstElement *sink;
- GstElement *fakesink;
- /* playbin */
- rb_debug ("constructing element \"playbin\"");
- mp->priv->playbin = gst_element_factory_make ("playbin", "playbin");
+ mp->priv->playbin = gst_element_factory_make ("playbin2", NULL);
if (mp->priv->playbin == NULL) {
- goto missing_element;
+ g_set_error (error,
+ RB_PLAYER_ERROR,
+ RB_PLAYER_ERROR_GENERAL,
+ _("Failed to create playbin2 element; check your GStreamer installation"));
+ return FALSE;
+ }
+ g_signal_connect_object (G_OBJECT (mp->priv->playbin),
+ "about-to-finish",
+ G_CALLBACK (about_to_finish_cb),
+ mp, 0);
+ g_signal_connect_object (G_OBJECT (mp->priv->playbin),
+ "notify::volume",
+ G_CALLBACK (volume_notify_cb),
+ mp, 0);
+ if (mp->priv->buffer_size != 0) {
+ g_object_set (mp->priv->playbin, "buffer-size", mp->priv->buffer_size * 1024, NULL);
}
- gst_bus_add_watch (gst_element_get_bus (GST_ELEMENT (mp->priv->playbin)),
- (GstBusFunc) rb_player_gst_bus_cb, mp);
+ gst_bus_add_watch (gst_element_get_bus (mp->priv->playbin),
+ (GstBusFunc) bus_cb,
+ mp);
/* let plugins add bits to playbin */
g_object_notify (G_OBJECT (mp), "playbin");
g_object_notify (G_OBJECT (mp), "bus");
/* Use gconfaudiosink for audio if there's no audio sink yet */
- g_object_get (G_OBJECT (mp->priv->playbin), "audio-sink", &sink, NULL);
- if (sink == NULL) {
- sink = rb_player_gst_try_audio_sink ("gconfaudiosink", "audiosink");
- if (sink == NULL) {
+ g_object_get (mp->priv->playbin, "audio-sink", &mp->priv->audio_sink, NULL);
+ if (mp->priv->audio_sink == NULL) {
+ mp->priv->audio_sink = gst_element_factory_make ("gconfaudiosink", NULL);
+ if (mp->priv->audio_sink == NULL) {
/* fall back to autoaudiosink */
- sink = rb_player_gst_try_audio_sink ("autoaudiosink", "audiosink");
+ rb_debug ("falling back to autoaudiosink");
+ mp->priv->audio_sink = gst_element_factory_make ("autoaudiosink", NULL);
+ } else {
+ rb_debug ("using gconfaudiosink");
}
- if (sink != NULL) {
- g_object_set (G_OBJECT (mp->priv->playbin), "audio-sink", sink, NULL);
+ if (mp->priv->audio_sink != NULL) {
+ /* set the profile property on the gconfaudiosink to "music and movies" */
+ if (g_object_class_find_property (G_OBJECT_GET_CLASS (mp->priv->audio_sink), "profile")) {
+ rb_debug ("setting profile property on audio sink");
+ g_object_set (mp->priv->audio_sink, "profile", 1, NULL);
+ }
+
+ g_object_set (mp->priv->playbin, "audio-sink", mp->priv->audio_sink, NULL);
}
} else {
- g_object_unref (sink);
+ rb_debug ("existing audio sink found");
+ g_object_unref (mp->priv->audio_sink);
}
{
@@ -502,27 +431,26 @@ rb_player_gst_construct (RBPlayerGst *mp, GError **error)
gst_bin_add (GST_BIN (mp->priv->filterbin), audioconvert);
/* ghost it to the bin */
- pad = gst_element_get_static_pad (audioconvert, "sink");
+ pad = gst_element_get_pad (audioconvert, "sink");
ghostpad = gst_ghost_pad_new ("sink", pad);
gst_element_add_pad (mp->priv->filterbin, ghostpad);
gst_object_unref (pad);
- pad = gst_element_get_static_pad (audioconvert, "src");
+ pad = gst_element_get_pad (audioconvert, "src");
ghostpad = gst_ghost_pad_new ("src", pad);
gst_element_add_pad (mp->priv->filterbin, ghostpad);
gst_object_unref (pad);
-
- /* set up the sinkbin with it's tee element */
+ /* set up the sinkbin with its tee element */
mp->priv->sinkbin = gst_bin_new (NULL);
mp->priv->tee = gst_element_factory_make ("tee", NULL);
queue = gst_element_factory_make ("queue", NULL);
/* link it all together and insert */
- gst_bin_add_many (GST_BIN (mp->priv->sinkbin), mp->priv->filterbin, mp->priv->tee, queue, sink, NULL);
- gst_element_link_many (mp->priv->filterbin, mp->priv->tee, queue, sink, NULL);
+ gst_bin_add_many (GST_BIN (mp->priv->sinkbin), mp->priv->filterbin, mp->priv->tee, queue, mp->priv->audio_sink, NULL);
+ gst_element_link_many (mp->priv->filterbin, mp->priv->tee, queue, mp->priv->audio_sink, NULL);
- pad = gst_element_get_static_pad (mp->priv->filterbin, "sink");
+ pad = gst_element_get_pad (mp->priv->filterbin, "sink");
ghostpad = gst_ghost_pad_new ("sink", pad);
gst_element_add_pad (mp->priv->sinkbin, ghostpad);
gst_object_unref (pad);
@@ -545,10 +473,10 @@ rb_player_gst_construct (RBPlayerGst *mp, GError **error)
}
/* Use fakesink for video if there's no video sink yet */
- g_object_get (G_OBJECT (mp->priv->playbin), "video-sink", &sink, NULL);
+ g_object_get (mp->priv->playbin, "video-sink", &sink, NULL);
if (sink == NULL) {
- fakesink = gst_element_factory_make ("fakesink", "fakesink");
- g_object_set (G_OBJECT (mp->priv->playbin), "video-sink", fakesink, NULL);
+ sink = gst_element_factory_make ("fakesink", NULL);
+ g_object_set (mp->priv->playbin, "video-sink", sink, NULL);
} else {
g_object_unref (sink);
}
@@ -557,215 +485,166 @@ rb_player_gst_construct (RBPlayerGst *mp, GError **error)
mp->priv->cur_volume = 1.0;
if (mp->priv->cur_volume < 0.0)
mp->priv->cur_volume = 0;
+ mp->priv->replaygain_scale = 1.0f;
+
rb_player_set_volume (RB_PLAYER (mp), mp->priv->cur_volume);
rb_debug ("pipeline construction complete");
return TRUE;
-missing_element:
- {
- char *err = g_strdup_printf (_("Failed to create %s element; check your installation"),
- element_name);
- g_set_error (error,
- RB_PLAYER_ERROR,
- RB_PLAYER_ERROR_GENERAL,
- "%s", err);
- g_free (err);
- rb_player_gst_gst_free_playbin (mp);
- return FALSE;
- }
}
-RBPlayer *
-rb_player_gst_new (GError **error)
+static gboolean
+message_from_sink (GstElement *sink, GstMessage *message)
{
- RBPlayerGst *mp;
-
- mp = RB_PLAYER_GST (g_object_new (RB_TYPE_PLAYER_GST, NULL, NULL));
+ GstElement *src;
+ GstElement *match;
+ char *name;
- return RB_PLAYER (mp);
-}
+ src = GST_ELEMENT (GST_MESSAGE_SRC (message));
-static gboolean
-rb_player_gst_sync_pipeline (RBPlayerGst *mp)
-{
- rb_debug ("syncing pipeline");
- if (mp->priv->playing) {
- rb_debug ("PLAYING pipeline");
- if (gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
- return FALSE;
- }
- } else {
- rb_debug ("PAUSING pipeline");
- if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) {
- return FALSE;
- }
+ if (GST_IS_BIN (sink) == FALSE) {
+ return (src == sink);
}
- /* FIXME: Set up a timeout to watch if the pipeline doesn't
- * go to PAUSED/PLAYING within some time (5 secs maybe?)
- */
- return TRUE;
-}
+ name = gst_element_get_name (src);
+ match = gst_bin_get_by_name (GST_BIN (sink), name);
+ g_free (name);
-/* Start a sequence of synchronous GStreamer operations in which we
- * can receive an error signal.
- */
-static void
-begin_gstreamer_operation (RBPlayerGst *mp)
-{
- g_assert (mp->priv->error == NULL);
- mp->priv->can_signal_direct_error = TRUE;
-}
-
-/* End a sequence of synchronous operations and propagate any
- * error from the sequence into error.
- */
-static void
-end_gstreamer_operation (RBPlayerGst *mp, gboolean op_failed, GError **error)
-{
- mp->priv->can_signal_direct_error = FALSE;
- if (mp->priv->error) {
- g_propagate_error (error, mp->priv->error);
- mp->priv->error = NULL;
- } else if (op_failed) {
- g_set_error (error,
- RB_PLAYER_ERROR,
- RB_PLAYER_ERROR_GENERAL,
- _("Unknown playback error"));
+ if (match != NULL) {
+ g_object_unref (match);
+ return (match == src);
}
+ return FALSE;
}
-static void
-cdda_got_source_cb (GObject *object, GParamSpec *pspec, char *device)
+static gboolean
+set_state_and_wait (RBPlayerGst *player, GstState target, GError **error)
{
- GstElement *source;
+ GstBus *bus;
+ gboolean waiting;
+ gboolean result;
- g_object_get (object, "source", &source, NULL);
- rb_debug ("got source %p", source);
- if (source) {
- g_signal_handlers_disconnect_by_func (object, cdda_got_source_cb, device);
+ g_assert (player->priv->playbin != NULL);
+ /* XXX probably need to remove bus watch here if we're not on the main thread */
+ /* .. probably shouldn't be doing this much anyway .. */
- g_object_set (G_OBJECT (source), "device", device, NULL);
- g_free (device);
+ rb_debug ("setting playbin state to %s", gst_element_state_get_name (target));
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "paranoia-mode"))
- g_object_set (G_OBJECT (source), "paranoia-mode", 0, NULL);
+ switch (gst_element_set_state (player->priv->playbin, target)) {
+ case GST_STATE_CHANGE_SUCCESS:
+ rb_debug ("state change was successful");
+ return TRUE;
- if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), "read-speed"))
- g_object_set (G_OBJECT (source), "read-speed", 1, NULL);
- }
-}
+ case GST_STATE_CHANGE_NO_PREROLL:
+ rb_debug ("state change was successful (no preroll)");
+ return TRUE;
-static void
-_destroy_stream_data (RBPlayerGst *player)
-{
- if (player->priv->stream_data && player->priv->stream_data_destroy) {
- player->priv->stream_data_destroy (player->priv->stream_data);
- }
- player->priv->stream_data = NULL;
- player->priv->stream_data_destroy = NULL;
-}
+ case GST_STATE_CHANGE_ASYNC:
+ rb_debug ("state is changing asynchronously");
+ result = TRUE;
+ break;
-static gboolean
-rb_player_gst_open (RBPlayer *player,
- const char *uri,
- gpointer stream_data,
- GDestroyNotify stream_data_destroy,
- GError **error)
-{
- RBPlayerGst *mp = RB_PLAYER_GST (player);
- gboolean cdda_seek = FALSE;
+ case GST_STATE_CHANGE_FAILURE:
+ rb_debug ("state change failed");
+ result = FALSE;
+ break;
- if (mp->priv->playbin == NULL) {
- if (!rb_player_gst_construct (mp, error))
- return FALSE;
- } else {
- if (!rb_player_close (player, NULL, error))
- return FALSE;
+ default:
+ rb_debug ("unknown state change return..");
+ result = TRUE;
+ break;
}
- g_assert (mp->priv->playbin != NULL);
-
- if (uri == NULL) {
- _destroy_stream_data (mp);
- g_free (mp->priv->uri);
- mp->priv->uri = NULL;
- mp->priv->playing = FALSE;
- mp->priv->buffering = FALSE;
- return TRUE;
- }
+ bus = gst_element_get_bus (player->priv->playbin);
+ waiting = TRUE;
+ while (waiting) {
+ GstMessage *message;
- /* check if we are switching tracks on a cd, so we don't have to close the device */
- if (g_str_has_prefix (uri, "cdda://")) {
- const char *old_device = NULL;
- const char *new_device;
-
- if (mp->priv->uri && g_str_has_prefix (mp->priv->uri, "cdda://"))
- old_device = g_utf8_strchr (mp->priv->uri, -1, '#');
- new_device = g_utf8_strchr (uri, -1, '#');
-
- if (old_device && strcmp (old_device, new_device) == 0) {
- /* just seek, instead of having playbin close the device */
- GstFormat track_format = gst_format_get_by_nick ("track");
- char *track_str;
- guint track;
- guint cdda_len;
-
- cdda_len = strlen ("cdda://");
- track_str = g_strndup (uri + cdda_len, new_device - (uri + cdda_len));
- track = atoi (track_str);
- g_free (track_str);
-
- rb_debug ("seeking to track %d on CD device %s", track, new_device);
-
- if (gst_element_seek (mp->priv->playbin, 1.0,
- track_format, GST_SEEK_FLAG_FLUSH,
- GST_SEEK_TYPE_SET, track,
- GST_SEEK_TYPE_NONE, -1))
- cdda_seek = TRUE;
- } else {
- /* +1 to skip the '#' */
- char *device = g_strdup (new_device + 1);
-
- rb_debug ("waiting for source element for CD device %s", device);
- g_signal_connect (G_OBJECT (mp->priv->playbin),
- "notify::source",
- G_CALLBACK (cdda_got_source_cb),
- device);
+ message = gst_bus_timed_pop (bus, GST_SECOND * STATE_CHANGE_MESSAGE_TIMEOUT);
+ if (message == NULL) {
+ rb_debug ("state change is taking too long..");
+ break;
}
- }
- begin_gstreamer_operation (mp);
- _destroy_stream_data (mp);
- g_free (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->emitted_error = FALSE;
+ switch (GST_MESSAGE_TYPE (message)) {
+ case GST_MESSAGE_ERROR:
+ {
+ char *debug;
+ GError *gst_error = NULL;
+
+ gst_message_parse_error (message, &gst_error, &debug);
+
+ if (message_from_sink (player->priv->audio_sink, message)) {
+ rb_debug ("got error from sink: %s (%s)", gst_error->message, debug);
+ g_set_error (error,
+ RB_PLAYER_ERROR,
+ RB_PLAYER_ERROR_INTERNAL,
+ _("Failed to open output device: %s"),
+ gst_error->message);
+ } else {
+ rb_debug ("got error from stream: %s (%s)", gst_error->message, debug);
+ g_set_error (error,
+ RB_PLAYER_ERROR,
+ RB_PLAYER_ERROR_GENERAL,
+ "%s",
+ gst_error->message);
+ }
+
+ g_error_free (gst_error);
+ g_free (debug);
+
+ waiting = FALSE;
+ result = 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 (player->priv->playbin)) {
+ rb_debug ("playbin reached state %s", gst_element_state_get_name (newstate));
+ if (pending == GST_STATE_VOID_PENDING && newstate == target) {
+ waiting = FALSE;
+ }
+ }
+ break;
+ }
- if (!cdda_seek) {
- g_object_set (G_OBJECT (mp->priv->playbin), "uri", uri, NULL);
+ default:
+ /* pass back to regular message handler */
+ bus_cb (bus, message, player);
+ break;
+ }
}
- if (!rb_player_gst_sync_pipeline (mp)) {
- end_gstreamer_operation (mp, TRUE, error);
- rb_player_gst_close (player, uri, NULL);
- return FALSE;
+ if (result == FALSE && *error == NULL) {
+ g_set_error (error,
+ RB_PLAYER_ERROR,
+ RB_PLAYER_ERROR_GENERAL,
+ _("Unable to start playback pipeline"));
}
- 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);
+ return result;
+}
- end_gstreamer_operation (mp, FALSE, error);
- return TRUE;
+static void
+_destroy_stream_data (RBPlayerGst *player)
+{
+ if (player->priv->stream_data && player->priv->stream_data_destroy) {
+ player->priv->stream_data_destroy (player->priv->stream_data);
+ }
+ player->priv->stream_data = NULL;
+ player->priv->stream_data_destroy = NULL;
}
static gboolean
-rb_player_gst_close (RBPlayer *player, const char *uri, GError **error)
+impl_close (RBPlayer *player, const char *uri, GError **error)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
- gboolean ret;
mp->priv->playing = FALSE;
mp->priv->buffering = FALSE;
@@ -777,7 +656,11 @@ rb_player_gst_close (RBPlayer *player, const char *uri, GError **error)
_destroy_stream_data (mp);
g_free (mp->priv->uri);
+ g_free (mp->priv->prev_uri);
mp->priv->uri = NULL;
+ mp->priv->prev_uri = NULL;
+
+ mp->priv->replaygain_scale = 1.0f;
if (mp->priv->tick_timeout_id != 0) {
g_source_remove (mp->priv->tick_timeout_id);
@@ -787,20 +670,44 @@ rb_player_gst_close (RBPlayer *player, const char *uri, GError **error)
if (mp->priv->playbin == NULL)
return TRUE;
- begin_gstreamer_operation (mp);
- ret = gst_element_set_state (mp->priv->playbin, GST_STATE_READY) == GST_STATE_CHANGE_SUCCESS;
- end_gstreamer_operation (mp, !ret, error);
+ return set_state_and_wait (mp, GST_STATE_READY, error);
+}
+
+static gboolean
+impl_open (RBPlayer *player,
+ const char *uri,
+ gpointer stream_data,
+ GDestroyNotify stream_data_destroy,
+ GError **error)
+{
+ RBPlayerGst *mp = RB_PLAYER_GST (player);
- if (mp->priv->volume_handler) {
- g_object_unref (mp->priv->volume_handler);
- mp->priv->volume_handler = NULL;
+ if (mp->priv->playbin == NULL) {
+ if (!construct_pipeline (mp, error))
+ return FALSE;
}
- return ret;
+ g_assert (mp->priv->playbin != NULL);
+
+ if (uri == NULL) {
+ return impl_close (player, NULL, error);
+ }
+
+ 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->emitted_error = FALSE;
+ mp->priv->stream_change_pending = TRUE;
+
+ return TRUE;
}
static gboolean
-rb_player_gst_opened (RBPlayer *player)
+impl_opened (RBPlayer *player)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
@@ -808,32 +715,105 @@ rb_player_gst_opened (RBPlayer *player)
}
static gboolean
-rb_player_gst_play (RBPlayer *player, gint crossfade, GError **error)
+impl_play (RBPlayer *player, gint crossfade, GError **error)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
- gboolean ret;
-
- mp->priv->playing = TRUE;
- mp->priv->buffering = FALSE;
+ gboolean result;
g_return_val_if_fail (mp->priv->playbin != NULL, FALSE);
- begin_gstreamer_operation (mp);
- ret = rb_player_gst_sync_pipeline (mp);
- end_gstreamer_operation (mp, !ret, error);
+ if (mp->priv->stream_change_pending == FALSE) {
+ rb_debug ("no stream change pending, just restarting playback");
+ result = set_state_and_wait (mp, GST_STATE_PLAYING, error);
- _rb_player_emit_playing_stream (RB_PLAYER (mp), mp->priv->stream_data);
+ } else if (mp->priv->current_track_finishing) {
+ rb_debug ("current track finishing -> just setting URI on playbin");
+ g_object_set (mp->priv->playbin, "uri", mp->priv->uri, NULL);
- 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);
+ _rb_player_emit_playing_stream (RB_PLAYER (mp), mp->priv->stream_data);
- return ret;
+ result = TRUE;
+ } else {
+ gboolean cdda_seek = FALSE;
+
+ rb_debug ("not in transition, stopping current track to start the new one");
+
+ /* check if we are switching tracks on a cd, so we don't have to close the device */
+ if (g_str_has_prefix (mp->priv->uri, "cdda://")) {
+ const char *old_device = NULL;
+ const char *new_device;
+
+ if (mp->priv->prev_uri && g_str_has_prefix (mp->priv->prev_uri, "cdda://"))
+ old_device = g_utf8_strchr (mp->priv->prev_uri, -1, '#');
+
+ new_device = g_utf8_strchr (mp->priv->uri, -1, '#');
+
+ if (old_device && strcmp (old_device, new_device) == 0) {
+ GstFormat track_format = gst_format_get_by_nick ("track");
+ char *track_str;
+ guint track;
+ guint cdda_len;
+
+ cdda_len = strlen ("cdda://");
+ track_str = g_strndup (mp->priv->uri + cdda_len, new_device - (mp->priv->uri + cdda_len));
+ track = atoi (track_str);
+ g_free (track_str);
+
+ rb_debug ("seeking to track %d on CD device %s", track, new_device);
+ if (gst_element_seek (mp->priv->playbin, 1.0,
+ track_format, GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, track - 1,
+ GST_SEEK_TYPE_NONE, -1)) {
+ cdda_seek = TRUE;
+ result = TRUE;
+ _rb_player_emit_playing_stream (RB_PLAYER (mp), mp->priv->stream_data);
+ }
+ } else {
+ /* +1 to skip the '#' */
+ char *device = g_strdup (new_device + 1);
+
+ rb_debug ("waiting for source element for CD device %s", device);
+ g_signal_connect (G_OBJECT (mp->priv->playbin),
+ "notify::source",
+ G_CALLBACK (cdda_got_source_cb),
+ device);
+ }
+ }
+
+ if (cdda_seek == FALSE) {
+ result = set_state_and_wait (mp, GST_STATE_READY, error);
+ if (result == TRUE) {
+ g_object_set (mp->priv->playbin, "uri", mp->priv->uri, NULL);
+
+ _rb_player_emit_playing_stream (RB_PLAYER (mp), mp->priv->stream_data);
+ result = set_state_and_wait (mp, GST_STATE_PLAYING, error);
+ }
+ }
+ }
+
+ mp->priv->stream_change_pending = FALSE;
+
+ if (result) {
+ mp->priv->current_track_finishing = FALSE;
+ mp->priv->buffering = FALSE;
+ mp->priv->playing = TRUE;
+
+ 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);
+ }
+ }
+
+ return result;
}
static void
-rb_player_gst_pause (RBPlayer *player)
+impl_pause (RBPlayer *player)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
+ GError *error = NULL;
if (!mp->priv->playing)
return;
@@ -842,7 +822,10 @@ rb_player_gst_pause (RBPlayer *player)
g_return_if_fail (mp->priv->playbin != NULL);
- rb_player_gst_sync_pipeline (mp);
+ if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+ g_warning ("unable to pause playback: %s\n", error->message);
+ g_error_free (error);
+ }
if (mp->priv->tick_timeout_id != 0) {
g_source_remove (mp->priv->tick_timeout_id);
@@ -851,59 +834,20 @@ rb_player_gst_pause (RBPlayer *player)
}
static gboolean
-rb_player_gst_playing (RBPlayer *player)
+impl_playing (RBPlayer *player)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
return mp->priv->playing;
}
-static gboolean
-emit_volume_changed_idle (RBPlayerGst *mp)
-{
- _rb_player_emit_volume_changed (RB_PLAYER (mp), mp->priv->cur_volume);
- return FALSE;
-}
-
-static void
-stream_volume_changed (GObject *element, GParamSpec *pspec, RBPlayerGst *mp)
-{
- gdouble v;
- g_object_get (element, "volume", &v, NULL);
- mp->priv->cur_volume = v;
-
- g_idle_add ((GSourceFunc) emit_volume_changed_idle, mp);
-}
-
static void
-find_volume_handler (RBPlayerGst *mp)
-{
- /* look for a 'volume' property provided by the sink */
- if (mp->priv->volume_handler == NULL && mp->priv->playbin != NULL) {
- GstElement *sink;
-
- g_object_get (mp->priv->playbin, "audio-sink", &sink, NULL);
- if (sink != NULL) {
- mp->priv->volume_handler = rb_player_gst_find_element_with_property (sink, "volume");
- g_object_unref (sink);
- }
-
- if (mp->priv->volume_handler == NULL) {
- mp->priv->volume_handler = g_object_ref (mp->priv->playbin);
- }
-
- g_signal_connect_object (mp->priv->volume_handler,
- "notify::volume",
- G_CALLBACK (stream_volume_changed),
- mp, 0);
- }
-}
-
-static void
-rb_player_gst_set_replaygain (RBPlayer *player,
- const char *uri,
- double track_gain, double track_peak,
- double album_gain, double album_peak)
+impl_set_replaygain (RBPlayer *player,
+ const char *uri,
+ double track_gain,
+ double track_peak,
+ double album_gain,
+ double album_peak)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
double scale;
@@ -911,9 +855,9 @@ rb_player_gst_set_replaygain (RBPlayer *player,
double peak = 0;
if (album_gain != 0)
- gain = album_gain;
+ gain = album_gain;
else
- gain = track_gain;
+ gain = track_gain;
if (gain == 0)
return;
@@ -922,9 +866,9 @@ rb_player_gst_set_replaygain (RBPlayer *player,
/* anti clip */
if (album_peak != 0)
- peak = album_peak;
+ peak = album_peak;
else
- peak = track_peak;
+ peak = track_peak;
if (peak != 0 && (scale * peak) > 1)
scale = 1.0 / peak;
@@ -934,42 +878,29 @@ rb_player_gst_set_replaygain (RBPlayer *player,
scale = 15;
rb_debug ("Scale : %f New volume : %f", scale, mp->priv->cur_volume * scale);
+ mp->priv->replaygain_scale = scale;
- find_volume_handler (mp);
- if (mp->priv->volume_handler != NULL) {
- GParamSpec *volume_pspec;
- GValue val = {0,};
-
- volume_pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (mp->priv->volume_handler),
- "volume");
- g_value_init (&val, G_TYPE_DOUBLE);
-
- g_value_set_double (&val, mp->priv->cur_volume * scale);
- if (g_param_value_validate (volume_pspec, &val))
- rb_debug ("replay gain too high, reducing value to %f", g_value_get_double (&val));
-
- g_object_set_property (G_OBJECT (mp->priv->volume_handler), "volume", &val);
- g_value_unset (&val);
+ if (mp->priv->playbin != NULL) {
+ g_object_set (mp->priv->playbin, "volume", mp->priv->cur_volume * scale, NULL);
}
}
static void
-rb_player_gst_set_volume (RBPlayer *player,
- float volume)
+impl_set_volume (RBPlayer *player,
+ float volume)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
g_return_if_fail (volume >= 0.0 && volume <= 1.0);
- find_volume_handler (mp);
- if (mp->priv->volume_handler != NULL) {
- g_object_set (mp->priv->volume_handler, "volume", volume, NULL);
+ if (mp->priv->playbin != NULL) {
+ g_object_set (mp->priv->playbin, "volume", volume * mp->priv->replaygain_scale, NULL);
}
mp->priv->cur_volume = volume;
}
static float
-rb_player_gst_get_volume (RBPlayer *player)
+impl_get_volume (RBPlayer *player)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
@@ -977,10 +908,9 @@ rb_player_gst_get_volume (RBPlayer *player)
}
static gboolean
-rb_player_gst_seekable (RBPlayer *player)
+impl_seekable (RBPlayer *player)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
- /* Need to send a seekable query on playbin */
gboolean can_seek = TRUE;
GstQuery *query;
@@ -1002,18 +932,20 @@ rb_player_gst_seekable (RBPlayer *player)
}
static void
-rb_player_gst_set_time (RBPlayer *player, long time)
+impl_set_time (RBPlayer *player, long time)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
+ GError *error = NULL;
+
g_return_if_fail (time >= 0);
g_return_if_fail (mp->priv->playbin != NULL);
- if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
- /* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
- if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
- g_warning ("Failed to pause pipeline before seek");
- }
+ if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+ g_warning ("got error while pausing the pipelink for seeking: %s\n", error->message);
+ g_clear_error (&error);
+
+ /* keep going anyway? */
}
gst_element_seek (mp->priv->playbin, 1.0,
@@ -1022,12 +954,15 @@ rb_player_gst_set_time (RBPlayer *player, long time)
GST_SEEK_TYPE_NONE, -1);
if (mp->priv->playing) {
- gst_element_set_state (mp->priv->playbin, GST_STATE_PLAYING);
+ if (set_state_and_wait (mp, GST_STATE_PLAYING, &error) == FALSE) {
+ g_warning ("unable to resume playback after seeking: %s\n", error->message);
+ g_clear_error (&error);
+ }
}
}
static long
-rb_player_gst_get_time (RBPlayer *player)
+impl_get_time (RBPlayer *player)
{
RBPlayerGst *mp = RB_PLAYER_GST (player);
@@ -1040,13 +975,13 @@ rb_player_gst_get_time (RBPlayer *player)
position /= GST_SECOND;
return (long) position;
- } else
+ } else {
return -1;
+ }
}
-
static gboolean
-rb_player_gst_add_tee (RBPlayerGstTee *player, GstElement *element)
+impl_add_tee (RBPlayerGstTee *player, GstElement *element)
{
RBPlayerGst *mp;
GstElement *queue, *audioconvert, *bin;
@@ -1060,12 +995,11 @@ rb_player_gst_add_tee (RBPlayerGstTee *player, GstElement *element)
}
if (mp->priv->playing) {
- if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
- /* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
- if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
- g_warning ("Failed to pause pipeline before tee insertion");
- return FALSE;
- }
+ GError *error = NULL;
+ if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+ g_warning ("Failed to pause pipeline before tee insertion: %s", error->message);
+ g_error_free (error);
+ return FALSE;
}
}
@@ -1079,7 +1013,7 @@ rb_player_gst_add_tee (RBPlayerGstTee *player, GstElement *element)
gst_element_link_many (queue, audioconvert, element, NULL);
/* link it to the tee */
- pad = gst_element_get_static_pad (queue, "sink");
+ pad = gst_element_get_pad (queue, "sink");
ghostpad = gst_ghost_pad_new ("sink", pad);
gst_element_add_pad (bin, ghostpad);
gst_object_unref (pad);
@@ -1095,7 +1029,7 @@ rb_player_gst_add_tee (RBPlayerGstTee *player, GstElement *element)
}
static gboolean
-rb_player_gst_remove_tee (RBPlayerGstTee *player, GstElement *element)
+impl_remove_tee (RBPlayerGstTee *player, GstElement *element)
{
RBPlayerGst *mp;
GstElement *bin;
@@ -1111,25 +1045,18 @@ rb_player_gst_remove_tee (RBPlayerGstTee *player, GstElement *element)
_rb_player_gst_tee_emit_tee_pre_remove (player, element);
if (mp->priv->playing) {
- if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
- /* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
- if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
- g_warning ("Failed to pause pipeline before eee insertion");
- return FALSE;
- }
+ GError *error = NULL;
+ if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+ g_warning ("Failed to pause pipeline before tee removal: %s", error->message);
+ g_error_free (error);
+ return FALSE;
}
}
/* get the containing bin and unlink it */
bin = GST_ELEMENT (gst_element_get_parent (element));
- if (gst_element_set_state (bin, GST_STATE_NULL) == GST_STATE_CHANGE_ASYNC) {
- /* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
- if (gst_element_get_state (bin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
- g_warning ("Failed to pause pipeline before tee insertion");
- return FALSE;
- }
- }
+ gst_element_set_state (bin, GST_STATE_NULL);
gst_bin_remove (GST_BIN (mp->priv->sinkbin), bin);
gst_object_unref (bin);
@@ -1141,7 +1068,7 @@ rb_player_gst_remove_tee (RBPlayerGstTee *player, GstElement *element)
}
static gboolean
-rb_player_gst_add_filter (RBPlayerGstFilter *player, GstElement *element)
+impl_add_filter (RBPlayerGstFilter *player, GstElement *element)
{
RBPlayerGst *mp;
GstElement *audioconvert, *bin;
@@ -1160,12 +1087,11 @@ rb_player_gst_add_filter (RBPlayerGstFilter *player, GstElement *element)
}
if (mp->priv->playing) {
- if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
- /* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
- if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
- g_warning ("Failed to pause pipeline before filter insertion");
- return FALSE;
- }
+ GError *error = NULL;
+ if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+ g_warning ("Failed to pause pipeline before filter insertion: %s", error->message);
+ g_error_free (error);
+ return FALSE;
}
}
@@ -1207,7 +1133,7 @@ rb_player_gst_add_filter (RBPlayerGstFilter *player, GstElement *element)
binsinkpad = gst_ghost_pad_new ("sink", GST_PAD (element_sink_pad));
gst_element_add_pad (bin, binsinkpad);
- realpad = gst_element_get_static_pad (audioconvert, "src");
+ realpad = gst_element_get_pad (audioconvert, "src");
binsrcpad = gst_ghost_pad_new ("src", realpad);
gst_element_add_pad (bin, binsrcpad);
gst_object_unref (realpad);
@@ -1215,7 +1141,7 @@ rb_player_gst_add_filter (RBPlayerGstFilter *player, GstElement *element)
/* replace the filter chain ghost with the new bin */
gst_bin_add (GST_BIN (mp->priv->filterbin), bin);
- ghostpad = gst_element_get_static_pad (mp->priv->filterbin, "src");
+ ghostpad = gst_element_get_pad (mp->priv->filterbin, "src");
realpad = gst_ghost_pad_get_target (GST_GHOST_PAD (ghostpad));
gst_ghost_pad_set_target (GST_GHOST_PAD (ghostpad), binsrcpad);
gst_object_unref (ghostpad);
@@ -1236,7 +1162,7 @@ rb_player_gst_add_filter (RBPlayerGstFilter *player, GstElement *element)
}
static gboolean
-rb_player_gst_remove_filter (RBPlayerGstFilter *player, GstElement *element)
+impl_remove_filter (RBPlayerGstFilter *player, GstElement *element)
{
RBPlayerGst *mp;
GstPad *mypad;
@@ -1257,35 +1183,26 @@ rb_player_gst_remove_filter (RBPlayerGstFilter *player, GstElement *element)
_rb_player_gst_filter_emit_filter_pre_remove (player, element);
if (mp->priv->playing) {
- /* it'd be more fun to do this by blocking a pad.. */
-
- if (gst_element_set_state (mp->priv->playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_ASYNC) {
- /* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
- if (gst_element_get_state (mp->priv->playbin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
- g_warning ("Failed to pause pipeline before filter insertion");
- return FALSE;
- }
+ GError *error = NULL;
+ if (set_state_and_wait (mp, GST_STATE_PAUSED, &error) == FALSE) {
+ g_warning ("Failed to pause pipeline before filter removal: %s", error->message);
+ g_error_free (error);
+ return FALSE;
}
}
/* get the containing bin and unlink it */
bin = GST_ELEMENT (gst_element_get_parent (element));
- if (gst_element_set_state (bin, GST_STATE_NULL) == GST_STATE_CHANGE_ASYNC) {
- /* FIXME: Use a timeout on get_state. Post a GError somewhere on failed? */
- if (gst_element_get_state (bin, NULL, NULL, 3 * GST_SECOND) != GST_STATE_CHANGE_SUCCESS) {
- g_warning ("Failed to pause pipeline before filter insertion");
- return FALSE;
- }
- }
+ gst_element_set_state (bin, GST_STATE_NULL);
- mypad = gst_element_get_static_pad (bin, "sink");
+ mypad = gst_element_get_pad (bin, "sink");
prevpad = gst_pad_get_peer (mypad);
gst_pad_unlink (prevpad, mypad);
gst_object_unref (mypad);
- ghostpad = gst_element_get_static_pad (bin, "src");
- nextpad = gst_element_get_static_pad (mp->priv->filterbin, "src");
+ ghostpad = gst_element_get_pad (bin, "src");
+ nextpad = gst_element_get_pad (mp->priv->filterbin, "src");
targetpad = gst_ghost_pad_get_target (GST_GHOST_PAD (nextpad));
if (targetpad == ghostpad) {
@@ -1293,7 +1210,7 @@ rb_player_gst_remove_filter (RBPlayerGstFilter *player, GstElement *element)
gst_ghost_pad_set_target (GST_GHOST_PAD (nextpad), prevpad);
} else {
/* we are in the middle, so link the previous and next elements */
- mypad = gst_element_get_static_pad (bin, "src");
+ mypad = gst_element_get_pad (bin, "src");
gst_object_unref (nextpad);
nextpad = gst_pad_get_peer (mypad);
gst_pad_unlink (mypad, nextpad);
@@ -1317,37 +1234,182 @@ rb_player_gst_remove_filter (RBPlayerGstFilter *player, GstElement *element)
return result;
}
-/*static gboolean
-rb_player_gst_add_data_tee (RBPlayerGstDataTee *player, GstElement *element)
+static void
+rb_player_gst_filter_init (RBPlayerGstFilterIface *iface)
{
+ iface->add_filter = impl_add_filter;
+ iface->remove_filter = impl_remove_filter;
+}
+static void
+rb_player_gst_tee_init (RBPlayerGstTeeIface *iface)
+{
+ iface->add_tee = impl_add_tee;
+ iface->remove_tee = impl_remove_tee;
}
-static gboolean
-rb_player_gst_remove_data_tee (RBPlayerGstDataTee *player, GstElement *element)
+
+
+RBPlayer *
+rb_player_gst_new (GError **error)
{
+ return RB_PLAYER (g_object_new (RB_TYPE_PLAYER_GST, NULL, NULL));
+}
-}*/
static void
-rb_player_gst_filter_init (RBPlayerGstFilterIface *iface)
+rb_player_gst_init (RBPlayerGst *mp)
{
- iface->add_filter = rb_player_gst_add_filter;
- iface->remove_filter = rb_player_gst_remove_filter;
+ mp->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((mp),
+ RB_TYPE_PLAYER_GST,
+ RBPlayerGstPrivate));
}
static void
-rb_player_gst_tee_init (RBPlayerGstTeeIface *iface)
+impl_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ RBPlayerGst *mp = RB_PLAYER_GST (object);
+
+ switch (prop_id) {
+ case PROP_PLAYBIN:
+ g_value_set_object (value, mp->priv->playbin);
+ break;
+ case PROP_BUS:
+ if (mp->priv->playbin) {
+ GstBus *bus;
+ bus = gst_element_get_bus (mp->priv->playbin);
+ g_value_set_object (value, bus);
+ gst_object_unref (bus);
+ }
+ break;
+ case PROP_BUFFER_SIZE:
+ g_value_set_uint (value, mp->priv->buffer_size);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+impl_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ RBPlayerGst *mp = RB_PLAYER_GST (object);
+
+ switch (prop_id) {
+ case PROP_BUFFER_SIZE:
+ mp->priv->buffer_size = g_value_get_uint (value);
+ if (mp->priv->playbin != NULL) {
+ rb_debug ("setting buffer size on playbin: %d", mp->priv->buffer_size * 1024);
+ g_object_set (mp->priv->playbin, "buffer-size", mp->priv->buffer_size * 1024, NULL);
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+impl_dispose (GObject *object)
+{
+ RBPlayerGst *mp;
+
+ mp = RB_PLAYER_GST (object);
+
+ 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) {
+ gst_element_set_state (mp->priv->playbin, GST_STATE_NULL);
+ g_object_unref (mp->priv->playbin);
+ mp->priv->playbin = NULL;
+ mp->priv->audio_sink = NULL;
+ }
+
+ if (mp->priv->waiting_tees != NULL) {
+ g_list_foreach (mp->priv->waiting_tees, (GFunc)gst_object_sink, NULL);
+ g_list_free (mp->priv->waiting_tees);
+ mp->priv->waiting_tees = NULL;
+ }
+
+ if (mp->priv->waiting_filters != NULL) {
+ g_list_foreach (mp->priv->waiting_filters, (GFunc)gst_object_sink, NULL);
+ g_list_free (mp->priv->waiting_filters);
+ mp->priv->waiting_filters = NULL;
+ }
+
+ G_OBJECT_CLASS (rb_player_gst_parent_class)->dispose (object);
+}
+
+static void
+rb_player_init (RBPlayerIface *iface)
{
- iface->add_tee = rb_player_gst_add_tee;
- iface->remove_tee = rb_player_gst_remove_tee;
+ iface->open = impl_open;
+ iface->opened = impl_opened;
+ iface->close = impl_close;
+ iface->play = impl_play;
+ iface->pause = impl_pause;
+ iface->playing = impl_playing;
+ iface->set_volume = impl_set_volume;
+ iface->get_volume = impl_get_volume;
+ iface->set_replaygain = impl_set_replaygain;
+ iface->seekable = impl_seekable;
+ iface->set_time = impl_set_time;
+ iface->get_time = impl_get_time;
+ iface->multiple_open = (RBPlayerFeatureFunc) rb_false_function;
}
-/*static void
-rb_player_gst_data_tee_init (RBPlayerGstDataTeeIface *iface)
+static void
+rb_player_gst_class_init (RBPlayerGstClass *klass)
{
- iface->add_data_tee = rb_player_gst_add_data_tee;
- iface->remove_data_tee = rb_player_gst_remove_data_tee;
-}*/
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = impl_dispose;
+ object_class->get_property = impl_get_property;
+ object_class->set_property = impl_set_property;
+ g_object_class_install_property (object_class,
+ PROP_PLAYBIN,
+ g_param_spec_object ("playbin",
+ "playbin",
+ "playbin element",
+ GST_TYPE_ELEMENT,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_BUS,
+ g_param_spec_object ("bus",
+ "bus",
+ "GStreamer message bus",
+ GST_TYPE_BUS,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_BUFFER_SIZE,
+ g_param_spec_uint ("buffer-size",
+ "buffer size",
+ "Buffer size for network streams, in kB",
+ 64, MAX_NETWORK_BUFFER_SIZE, 128,
+ G_PARAM_READWRITE));
+
+ signals[MISSING_PLUGINS] =
+ g_signal_new ("missing-plugins",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0, /* no point handling this internally */
+ NULL, NULL,
+ rb_marshal_VOID__POINTER_POINTER_POINTER,
+ G_TYPE_NONE,
+ 3,
+ G_TYPE_POINTER, G_TYPE_STRV, G_TYPE_STRV);
+
+ g_type_class_add_private (klass, sizeof (RBPlayerGstPrivate));
+}
diff --git a/configure.ac b/configure.ac
index 1eb8f89..985ea34 100644
--- a/configure.ac
+++ b/configure.ac
@@ -29,7 +29,7 @@ AC_C_BIGENDIAN
AC_CHECK_SIZEOF(long)
DBUS_MIN_REQS=0.35
-GST_0_10_REQS=0.10.15
+GST_0_10_REQS=0.10.20
GTK_REQS=2.12.0
GLIB_REQS=2.16.0
GNOME_MEDIA_PROFILES_REQS=2.8
diff --git a/plugins/visualizer/Makefile.am b/plugins/visualizer/Makefile.am
index eba7975..7c8c73b 100644
--- a/plugins/visualizer/Makefile.am
+++ b/plugins/visualizer/Makefile.am
@@ -6,8 +6,7 @@ plugin_LTLIBRARIES = libvisualizer.la
libvisualizer_la_SOURCES = \
rb-visualizer-plugin.c \
rb-vis-widget.c \
- rb-vis-widget.h \
- rb-fake-visualizer.c
+ rb-vis-widget.h
libvisualizer_la_LDFLAGS = $(PLUGIN_LIBTOOL_FLAGS)
diff --git a/plugins/visualizer/rb-fake-visualizer.c b/plugins/visualizer/rb-fake-visualizer.c
deleted file mode 100644
index 2b81232..0000000
--- a/plugins/visualizer/rb-fake-visualizer.c
+++ /dev/null
@@ -1,609 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2004 Benjamin Otte <otte gnome org>
- * Copyright (C) 2006 Jonathan Matthew
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * The Rhythmbox authors hereby grant permission for non-GPL compatible
- * GStreamer plugins to be used and distributed together with GStreamer
- * and Rhythmbox. This permission is above and beyond the permissions granted
- * by the GPL license by which Rhythmbox is covered. If you modify this code
- * you may extend this exception to your version of the code, but you are not
- * obligated to do so. If you do not wish to do so, delete this exception
- * statement from your version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-/* fake visualizer element for evil rhythmbox purposes */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <string.h>
-
-#include <gst/gst.h>
-#include <gst/base/gstadapter.h>
-#include <gst/video/video.h>
-#include <gst/audio/audio.h>
-
-#define RB_TYPE_FAKE_VIS (rb_fake_vis_get_type())
-#define RB_IS_FAKE_VIS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),RB_TYPE_FAKE_VIS))
-#define RB_FAKE_VIS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),RB_TYPE_FAKE_VIS,RBFakeVis))
-#define RB_IS_FAKE_VIS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),RB_TYPE_FAKE_VIS))
-#define RB_FAKE_VIS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),RB_TYPE_FAKE_VIS,RBFakeVisClass))
-#define RB_FAKE_VIS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), RB_TYPE_FAKE_VIS, RBFakeVisClass))
-
-typedef struct _RBFakeVis RBFakeVis;
-typedef struct _RBFakeVisClass RBFakeVisClass;
-
-GST_DEBUG_CATEGORY_STATIC (rb_fake_vis_debug);
-#define GST_CAT_DEFAULT (rb_fake_vis_debug)
-
-struct _RBFakeVis
-{
- GstElement element;
-
- /* pads */
- GstPad *sinkpad;
- GstPad *srcpad;
- GstClockTime next_ts;
- GstSegment segment;
-
- /* audio/video state */
- gint channels;
- gint rate; /* Input samplerate */
- gint bps;
-
- /* framerate numerator & denominator */
- gint fps_n;
- gint fps_d;
- gint width;
- gint height;
- gint depth;
- GstClockTime duration;
-
- /* samples per frame based on caps */
- guint spf;
-
- /* state stuff */
- /*GstAdapter *adapter;*/
- guint avail;
- gboolean first_frame;
-
- /* QoS stuff *//* with LOCK */
- gdouble proportion;
- GstClockTime earliest_time;
-};
-
-struct _RBFakeVisClass
-{
- GstElementClass parent_class;
-};
-
-GType rb_fake_vis_get_type (void);
-
-
-static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
- GST_PAD_SRC,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS (GST_VIDEO_CAPS_xRGB_HOST_ENDIAN)
- );
-
-static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
- GST_PAD_SINK,
- GST_PAD_ALWAYS,
- GST_STATIC_CAPS ("audio/x-raw-int, "
- "width = (int) 16, "
- "depth = (int) 16, "
- "endianness = (int) BYTE_ORDER, "
- "signed = (boolean) TRUE, "
- "channels = (int) { 1, 2 }, " "rate = (int) [ 1000, MAX ]")
- );
-
-static GstStateChangeReturn rb_fake_vis_change_state (GstElement * element,
- GstStateChange transition);
-static GstFlowReturn rb_fake_vis_chain (GstPad * pad, GstBuffer * buffer);
-static gboolean rb_fake_vis_sink_event (GstPad * pad, GstEvent * event);
-static gboolean rb_fake_vis_src_event (GstPad * pad, GstEvent * event);
-
-static gboolean rb_fake_vis_sink_setcaps (GstPad * pad, GstCaps * caps);
-static gboolean rb_fake_vis_src_setcaps (GstPad * pad, GstCaps * caps);
-static GstCaps *rb_fake_vis_getcaps (GstPad * pad);
-
-static GstElementDetails rb_fake_vis_details =
- GST_ELEMENT_DETAILS ("RB fake visualizer",
- "Visualization",
- "pretend to generate visualization from audio input",
- "Benjamin Otte <otte gnome org>, Jonathan Matthew <jonathan kaolin wh9 net>");
-
-static void
-_do_init (GType fake_vis_type)
-{
- GST_DEBUG_CATEGORY_INIT (rb_fake_vis_debug,
- "fakevis", GST_DEBUG_FG_WHITE,
- "Rhythmbox built-in fake visualizer");
-}
-
-GST_BOILERPLATE_FULL (RBFakeVis, rb_fake_vis, GstElement, GST_TYPE_ELEMENT, _do_init);
-
-static void
-rb_fake_vis_base_init (gpointer g_class)
-{
- GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
- gst_element_class_set_details (element_class, &rb_fake_vis_details);
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&src_template));
- gst_element_class_add_pad_template (element_class,
- gst_static_pad_template_get (&sink_template));
-}
-
-static void
-rb_fake_vis_class_init (RBFakeVisClass *klass)
-{
- GstElementClass *element = GST_ELEMENT_CLASS (klass);
- element->change_state = rb_fake_vis_change_state;
-}
-
-static void
-rb_fake_vis_init (RBFakeVis * visual, RBFakeVisClass *klass)
-{
- /* create the sink and src pads */
- visual->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink");
- gst_pad_set_setcaps_function (visual->sinkpad, rb_fake_vis_sink_setcaps);
- gst_pad_set_chain_function (visual->sinkpad, rb_fake_vis_chain);
- gst_pad_set_event_function (visual->sinkpad, rb_fake_vis_sink_event);
- gst_element_add_pad (GST_ELEMENT (visual), visual->sinkpad);
-
- visual->srcpad = gst_pad_new_from_static_template (&src_template, "src");
- gst_pad_set_setcaps_function (visual->srcpad, rb_fake_vis_src_setcaps);
- gst_pad_set_getcaps_function (visual->srcpad, rb_fake_vis_getcaps);
- gst_pad_set_event_function (visual->srcpad, rb_fake_vis_src_event);
- gst_element_add_pad (GST_ELEMENT (visual), visual->srcpad);
-}
-
-static void
-rb_fake_vis_reset (RBFakeVis * visual)
-{
- visual->next_ts = -1;
- visual->avail = 0;
- gst_segment_init (&visual->segment, GST_FORMAT_UNDEFINED);
-
- GST_OBJECT_LOCK (visual);
- visual->proportion = 1.0;
- visual->earliest_time = -1;
- visual->first_frame = FALSE;
- GST_OBJECT_UNLOCK (visual);
-}
-
-static gboolean
-rb_fake_vis_src_setcaps (GstPad * pad, GstCaps * caps)
-{
- RBFakeVis *visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
- GstStructure *structure;
-
- structure = gst_caps_get_structure (caps, 0);
-
- GST_DEBUG_OBJECT (visual, "src pad got caps %" GST_PTR_FORMAT, caps);
-
- if (!gst_structure_get_int (structure, "width", &visual->width))
- goto error;
- if (!gst_structure_get_int (structure, "height", &visual->height))
- goto error;
- if (!gst_structure_get_int (structure, "bpp", &visual->depth))
- goto error;
- if (!gst_structure_get_fraction (structure, "framerate", &visual->fps_n,
- &visual->fps_d))
- goto error;
-
- visual->spf =
- gst_util_uint64_scale_int (visual->rate, visual->fps_d, visual->fps_n);
- visual->duration =
- gst_util_uint64_scale_int (GST_SECOND, visual->fps_d, visual->fps_n);
-
- gst_object_unref (visual);
- return TRUE;
-
- /* ERRORS */
-error:
- {
- GST_DEBUG_OBJECT (visual, "error parsing caps");
- gst_object_unref (visual);
- return FALSE;
- }
-}
-
-
-static gboolean
-rb_fake_vis_sink_setcaps (GstPad * pad, GstCaps * caps)
-{
- RBFakeVis *visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
- GstStructure *structure;
-
- structure = gst_caps_get_structure (caps, 0);
-
- gst_structure_get_int (structure, "channels", &visual->channels);
- gst_structure_get_int (structure, "rate", &visual->rate);
-
- if (visual->fps_n != 0) {
- visual->spf =
- gst_util_uint64_scale_int (visual->rate, visual->fps_d, visual->fps_n);
- }
- visual->bps = visual->channels * sizeof (gint16);
-
- gst_object_unref (visual);
- return TRUE;
-}
-
-
-static GstCaps *
-rb_fake_vis_getcaps (GstPad * pad)
-{
- GstCaps *ret;
- RBFakeVis *visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-
- ret = gst_caps_copy (gst_pad_get_pad_template_caps (visual->srcpad));
-
- GST_DEBUG_OBJECT (visual, "returning caps %" GST_PTR_FORMAT, ret);
- gst_object_unref (visual);
- return ret;
-}
-
-
-static gboolean
-rb_fake_vis_src_negotiate (RBFakeVis * visual)
-{
- GstCaps *othercaps, *target, *intersect;
- GstStructure *structure;
- const GstCaps *templ;
-
- templ = gst_pad_get_pad_template_caps (visual->srcpad);
-
- /* see what the peer can do */
- othercaps = gst_pad_peer_get_caps (visual->srcpad);
- if (othercaps) {
- intersect = gst_caps_intersect (othercaps, templ);
- gst_caps_unref (othercaps);
-
- if (gst_caps_is_empty (intersect))
- goto no_format;
-
- target = gst_caps_copy_nth (intersect, 0);
- gst_caps_unref (intersect);
- } else {
- target = gst_caps_ref ((GstCaps *) templ);
- }
-
- structure = gst_caps_get_structure (target, 0);
- gst_structure_fixate_field_nearest_int (structure, "width", 1);
- gst_structure_fixate_field_nearest_int (structure, "height", 1);
- gst_structure_fixate_field_nearest_fraction (structure, "framerate", 1, 1);
-
- gst_pad_set_caps (visual->srcpad, target);
- gst_caps_unref (target);
-
- return TRUE;
-
- /* ERRORS */
-no_format:
- {
- GST_ELEMENT_ERROR (visual, STREAM, FORMAT, (NULL),
- ("could not negotiate output format"));
- gst_caps_unref (intersect);
- return FALSE;
- }
-}
-
-static gboolean
-rb_fake_vis_sink_event (GstPad * pad, GstEvent * event)
-{
- RBFakeVis *visual;
- gboolean res;
-
- visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_FLUSH_START:
- res = gst_pad_push_event (visual->srcpad, event);
- break;
- case GST_EVENT_FLUSH_STOP:
- rb_fake_vis_reset (visual);
- res = gst_pad_push_event (visual->srcpad, event);
- break;
- case GST_EVENT_NEWSEGMENT:
- {
- GstFormat format;
- gdouble rate, arate;
- gint64 start, stop, time;
- gboolean update;
-
- /* the newsegment values are used to clip the input samples
- * and to convert the incoming timestamps to running time so
- * we can do QoS */
- gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format,
- &start, &stop, &time);
-
- /* now configure the values */
- gst_segment_set_newsegment_full (&visual->segment, update,
- rate, arate, format, start, stop, time);
-
- /* and forward */
- res = gst_pad_push_event (visual->srcpad, event);
- break;
- }
- default:
- res = gst_pad_push_event (visual->srcpad, event);
- break;
- }
-
- gst_object_unref (visual);
- return res;
-}
-
-static gboolean
-rb_fake_vis_src_event (GstPad * pad, GstEvent * event)
-{
- RBFakeVis *visual;
- gboolean res;
-
- visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
-
- switch (GST_EVENT_TYPE (event)) {
- case GST_EVENT_QOS:
- {
- gdouble proportion;
- GstClockTimeDiff diff;
- GstClockTime timestamp;
-
- gst_event_parse_qos (event, &proportion, &diff, ×tamp);
-
- /* save stuff for the _chain function */
- GST_OBJECT_LOCK (visual);
- visual->proportion = proportion;
- if (diff >= 0)
- /* we're late, this is a good estimate for next displayable
- * frame (see part-qos.txt) */
- visual->earliest_time = timestamp + 2 * diff + visual->duration;
- else
- visual->earliest_time = timestamp + diff;
-
- GST_OBJECT_UNLOCK (visual);
-
- res = gst_pad_push_event (visual->sinkpad, event);
- break;
- }
- default:
- res = gst_pad_push_event (visual->sinkpad, event);
- break;
- }
-
- gst_object_unref (visual);
- return res;
-}
-
-/* allocate and output buffer, if no format was negotiated, this
- * function will negotiate one. After calling this function, a
- * reverse negotiation could have happened. */
-static GstFlowReturn
-get_buffer (RBFakeVis * visual, GstBuffer ** outbuf)
-{
- GstFlowReturn ret;
- guint outsize;
-
- /* we don't know an output format yet, pick one */
- if (GST_PAD_CAPS (visual->srcpad) == NULL) {
- if (!rb_fake_vis_src_negotiate (visual))
- return GST_FLOW_NOT_NEGOTIATED;
- }
-
- outsize = visual->height * visual->width * (visual->depth / 8);
-
- GST_DEBUG_OBJECT (visual, "allocating output buffer with caps %"
- GST_PTR_FORMAT, GST_PAD_CAPS (visual->srcpad));
-
- /* now allocate a buffer with the last negotiated format.
- * Downstream could renegotiate a new format, which will trigger
- * our setcaps function on the source pad. */
- ret =
- gst_pad_alloc_buffer_and_set_caps (visual->srcpad,
- GST_BUFFER_OFFSET_NONE, outsize,
- GST_PAD_CAPS (visual->srcpad), outbuf);
-
- /* no buffer allocated, we don't care why. */
- if (ret != GST_FLOW_OK)
- return ret;
-
- /* this is bad and should not happen. When the alloc function
- * returns _OK, core ensures we have a valid buffer. */
- if (*outbuf == NULL)
- return GST_FLOW_ERROR;
-
- memset (GST_BUFFER_DATA (*outbuf), 0, outsize);
-
- return GST_FLOW_OK;
-}
-
-static GstFlowReturn
-rb_fake_vis_chain (GstPad * pad, GstBuffer * buffer)
-{
- GstBuffer *outbuf = NULL;
- RBFakeVis *visual = RB_FAKE_VIS (gst_pad_get_parent (pad));
- GstFlowReturn ret = GST_FLOW_OK;
-
- GST_DEBUG_OBJECT (visual, "chain function called");
-
- /* If we don't have an output format yet, preallocate a buffer to try and
- * set one */
- if (GST_PAD_CAPS (visual->srcpad) == NULL) {
- GST_DEBUG_OBJECT (visual, "calling buffer alloc to set caps");
- ret = get_buffer (visual, &outbuf);
- if (ret != GST_FLOW_OK) {
- GST_DEBUG_OBJECT (visual, "couldn't allocate buffer: %s", gst_flow_get_name (ret));
- gst_buffer_unref (buffer);
- goto beach;
- }
- }
-
- /* resync on DISCONT */
- if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
- visual->avail = 0;
- visual->next_ts = -1;
- }
-
- /* Try to push a frame as soon as possible to avoid stalling the pipeline */
- if (visual->first_frame == FALSE) {
- if (outbuf == NULL) {
- ret = get_buffer (visual, &outbuf);
- if (ret != GST_FLOW_OK) {
- goto beach;
- }
- }
- GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer);
- ret = gst_pad_push (visual->srcpad, outbuf);
- if (ret != GST_FLOW_OK) {
- goto beach;
- }
- outbuf = NULL;
- visual->first_frame = TRUE;
- }
-
- /* Match timestamps from the incoming audio */
- if (GST_BUFFER_TIMESTAMP (buffer) != GST_CLOCK_TIME_NONE)
- visual->next_ts = GST_BUFFER_TIMESTAMP (buffer);
-
- GST_DEBUG_OBJECT (visual,
- "Input buffer has %d samples, time=%" G_GUINT64_FORMAT,
- GST_BUFFER_SIZE (buffer) / visual->bps, GST_BUFFER_TIMESTAMP (buffer));
-
- visual->avail += GST_BUFFER_SIZE (buffer);
- gst_buffer_unref (buffer);
-
- while (visual->avail > MAX (512, visual->spf) * visual->bps) {
- gboolean need_skip;
-
- GST_DEBUG_OBJECT (visual, "processing buffer (%u avail)", visual->avail);
-
- if (visual->next_ts != -1) {
- gint64 qostime;
-
- /* QoS is done on running time */
- qostime = gst_segment_to_running_time (&visual->segment, GST_FORMAT_TIME,
- visual->next_ts);
-
- GST_OBJECT_LOCK (visual);
- /* check for QoS, don't compute buffers that are known to be late */
- need_skip = visual->earliest_time != -1 &&
- qostime <= visual->earliest_time;
- GST_OBJECT_UNLOCK (visual);
-
- if (need_skip) {
- GST_WARNING_OBJECT (visual,
- "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
- GST_TIME_ARGS (qostime), GST_TIME_ARGS (visual->earliest_time));
- goto skip;
- }
- }
- /* alloc a buffer if we don't have one yet, this happens
- * when we pushed a buffer in this while loop before */
- if (outbuf == NULL) {
- ret = get_buffer (visual, &outbuf);
- if (ret != GST_FLOW_OK) {
- goto beach;
- }
- }
-
- GST_BUFFER_TIMESTAMP (outbuf) = visual->next_ts;
- GST_BUFFER_DURATION (outbuf) = visual->duration;
-
- ret = gst_pad_push (visual->srcpad, outbuf);
- outbuf = NULL;
-
- GST_DEBUG_OBJECT (visual, "finished frame, flushing %u samples from input",
- visual->spf);
- skip:
- /* interpolate next timestamp */
- if (visual->next_ts != -1)
- visual->next_ts += visual->duration;
-
- /* Flush out the number of samples per frame * channels * sizeof (gint16) */
- if (visual->avail < visual->spf * visual->bps)
- visual->avail = 0;
- else
- visual->avail -= visual->spf * visual->bps;
-
- /* quit the loop if something was wrong */
- if (ret != GST_FLOW_OK)
- break;
- }
-
- if (outbuf != NULL)
- gst_buffer_unref (outbuf);
-
-beach:
- gst_object_unref (visual);
-
- GST_DEBUG_OBJECT (visual, "leaving chain function");
- return ret;
-}
-
-static GstStateChangeReturn
-rb_fake_vis_change_state (GstElement * element, GstStateChange transition)
-{
- RBFakeVis *visual = RB_FAKE_VIS (element);
- GstStateChangeReturn ret;
-
- switch (transition) {
- case GST_STATE_CHANGE_NULL_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_PAUSED:
- rb_fake_vis_reset (visual);
- break;
- case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
- break;
- default:
- break;
- }
-
- ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
-
- switch (transition) {
- case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
- break;
- case GST_STATE_CHANGE_PAUSED_TO_READY:
- break;
- case GST_STATE_CHANGE_READY_TO_NULL:
- break;
- default:
- break;
- }
-
- return ret;
-}
-
-static gboolean
-plugin_init (GstPlugin * plugin)
-{
- return gst_element_register (plugin, "rbfakevis", GST_RANK_NONE, RB_TYPE_FAKE_VIS);
-}
-
-GST_PLUGIN_DEFINE_STATIC (GST_VERSION_MAJOR, GST_VERSION_MINOR,
- "rbfakevis",
- "fake visualizer",
- plugin_init,
- VERSION,
- "GPL",
- PACKAGE,
- "");
-
diff --git a/plugins/visualizer/rb-visualizer-plugin.c b/plugins/visualizer/rb-visualizer-plugin.c
index 5982f47..53d1b74 100644
--- a/plugins/visualizer/rb-visualizer-plugin.c
+++ b/plugins/visualizer/rb-visualizer-plugin.c
@@ -82,9 +82,6 @@
#include "rb-vis-widget.h"
-/* not going to create a new header just for this */
-extern GType rb_fake_vis_get_type (void);
-
/* preferences */
#define CONF_VIS_PREFIX CONF_PREFIX "/plugins/visualizer"
#define CONF_VIS_ELEMENT CONF_VIS_PREFIX "/element"
@@ -108,6 +105,8 @@ extern GType rb_fake_vis_get_type (void);
#define VISUALIZER_DBUS_PATH "/org/gnome/Rhythmbox/Visualizer"
+/* playbin2 flag(s) */
+#define PLAYBIN2_FLAG_VIS 0x08
typedef struct {
char *name;
@@ -240,13 +239,6 @@ static const VisualizerQuality vis_quality[] = {
{ N_("Extra Large"), 800, 600, 30, 1 },
};
-/* "quality" to use for fake visualization; should be small and
- * low framerate, but not so slow it makes a noticeable difference
- * to playback start time. the video sink needs to preroll too,
- * so if the framerate is 1/10, that will take 10 seconds.
- */
-static const VisualizerQuality fake_vis_quality = { "", 60, 60, 1, 1 };
-
static const VisualizerModeName vis_mode_name[] = {
{ N_("Embedded"), EMBEDDED },
{ N_("Fullscreen"), FULLSCREEN },
@@ -443,12 +435,7 @@ bus_sync_message_cb (GstBus *bus, GstMessage *msg, RBVisualizerPlugin *plugin)
case EXTERNAL_WINDOW:
if (plugin->vis_widget != NULL) {
g_object_get (plugin->vis_widget, "window-xid", &window, NULL);
- if (window == 0) {
- window = GDK_WINDOW_XWINDOW (plugin->fake_window);
- rb_debug ("setting fake window id %lu", window);
- } else {
- rb_debug ("setting window id %lu in prepare-xwindow-id handler", window);
- }
+ rb_debug ("setting window id %lu in prepare-xwindow-id handler", window);
}
break;
case DESKTOP_WINDOW:
@@ -457,7 +444,7 @@ bus_sync_message_cb (GstBus *bus, GstMessage *msg, RBVisualizerPlugin *plugin)
break;
}
- if (plugin->xoverlay != NULL)
+ if (plugin->xoverlay != NULL && window != 0)
gst_x_overlay_set_xwindow_id (plugin->xoverlay, window);
plugin->window_id_set = TRUE;
}
@@ -477,10 +464,7 @@ fixate_vis_caps (RBVisualizerPlugin *pi, GstElement *vis_element, GstElement *ca
if (quality < 0 || quality > G_N_ELEMENTS (vis_quality))
quality = DEFAULT_VIS_QUALITY;
- if (pi->active)
- q = &vis_quality[quality];
- else
- q = &fake_vis_quality;
+ q = &vis_quality[quality];
pad = gst_element_get_static_pad (vis_element, "src");
template_caps = gst_pad_get_pad_template_caps (pad);
@@ -554,12 +538,24 @@ update_playbin_visualizer (RBVisualizerPlugin *plugin,
{
GstPad *pad;
GstElement *vis_plugin;
+ int playbin_flags;
if (plugin->playbin == NULL)
return;
- if (plugin->visualizer)
+ if (plugin->visualizer) {
g_object_unref (plugin->visualizer);
+ plugin->visualizer = NULL;
+ }
+
+ g_object_get (plugin->playbin, "flags", &playbin_flags, NULL);
+
+ if (plugin->active == FALSE) {
+ playbin_flags &= ~PLAYBIN2_FLAG_VIS;
+ rb_debug ("disabling vis; new playbin2 flags %d", playbin_flags);
+ g_object_set (plugin->playbin, "flags", playbin_flags, NULL);
+ return;
+ }
plugin->visualizer = gst_bin_new (NULL);
@@ -572,13 +568,8 @@ update_playbin_visualizer (RBVisualizerPlugin *plugin,
gst_object_unref (pad);
/* set up visualizer */
- if (plugin->active) {
- vis_plugin = create_visualizer_element (vis_override);
- gst_bin_add (GST_BIN (plugin->visualizer), vis_plugin);
- } else {
- vis_plugin = g_object_new (rb_fake_vis_get_type (), NULL);
- gst_bin_add (GST_BIN (plugin->visualizer), vis_plugin);
- }
+ vis_plugin = create_visualizer_element (vis_override);
+ gst_bin_add (GST_BIN (plugin->visualizer), vis_plugin);
pad = gst_element_get_static_pad (vis_plugin, "sink");
gst_element_add_pad (plugin->visualizer, gst_ghost_pad_new ("sink", pad));
@@ -589,7 +580,12 @@ update_playbin_visualizer (RBVisualizerPlugin *plugin,
g_object_ref (plugin->visualizer);
- g_object_set (plugin->playbin, "vis-plugin", plugin->visualizer, NULL);
+ playbin_flags |= PLAYBIN2_FLAG_VIS;
+ rb_debug ("enabling vis; new playbin2 flags %d", playbin_flags);
+ g_object_set (plugin->playbin,
+ "vis-plugin", plugin->visualizer,
+ "flags", playbin_flags,
+ NULL);
}
static void
@@ -1618,8 +1614,6 @@ impl_activate (RBPlugin *plugin,
GtkAction *action;
char *ui_file;
- rb_fake_vis_get_type ();
-
pi->shell = shell;
/* find the player backend and connect to its pipeline mutation signal */
@@ -1913,10 +1907,6 @@ vis_plugin_filter (GstPluginFeature *feature, gpointer data)
{
GstElementFactory *f;
- /* skip our fake visualizer */
- if (strcmp (gst_plugin_feature_get_name (feature), "rbfakevis") == 0)
- return FALSE;
-
if (!GST_IS_ELEMENT_FACTORY (feature))
return FALSE;
f = GST_ELEMENT_FACTORY (feature);
diff --git a/shell/rb-shell-preferences.c b/shell/rb-shell-preferences.c
index e0302b4..372302c 100644
--- a/shell/rb-shell-preferences.c
+++ b/shell/rb-shell-preferences.c
@@ -666,7 +666,6 @@ update_playback_prefs_sensitivity (RBShellPreferences *preferences)
backend = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preferences->priv->xfade_backend_check));
gtk_widget_set_sensitive (preferences->priv->album_crossfade_check, backend);
- gtk_widget_set_sensitive (preferences->priv->network_buffer_size, backend);
gtk_widget_set_sensitive (preferences->priv->transition_duration, backend);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]