Source tree moved to mafw-gst-subtitles-renderer directory.
[mafwsubrenderer] / mafw-gst-subtitles-renderer / libmafw-gst-renderer / mafw-gst-renderer.c
diff --git a/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.c b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.c
new file mode 100644 (file)
index 0000000..e92ea63
--- /dev/null
@@ -0,0 +1,2320 @@
+/*
+ * This file is a part of MAFW
+ *
+ * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved.
+ *
+ * Contact: Visa Smolander <visa.smolander@nokia.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <dbus/dbus.h>
+#include <libgnomevfs/gnome-vfs.h>
+
+#include <libmafw/mafw.h>
+#include "mafw-gst-renderer.h"
+#include "mafw-gst-renderer-utils.h"
+#include "mafw-gst-renderer-worker.h"
+
+#include "mafw-gst-renderer-state-playing.h"
+#include "mafw-gst-renderer-state-stopped.h"
+#include "mafw-gst-renderer-state-paused.h"
+#include "mafw-gst-renderer-state-transitioning.h"
+
+#include "blanking.h"
+
+#ifdef HAVE_CONIC
+#include <conicconnectionevent.h>
+#endif
+
+#undef  G_LOG_DOMAIN
+#define G_LOG_DOMAIN "mafw-gst-renderer"
+
+#define is_current_uri_stream(self) \
+       (((self)->media != NULL) && ((self)->media->uri != NULL) &&     \
+        uri_is_stream((self)->media->uri))
+
+#define GCONF_OSSO_AF "/system/osso/af"
+#define GCONF_BATTERY_COVER_OPEN "/system/osso/af/mmc-cover-open"
+#define GCONF_MAFW_GST_SUBTITLES_RENDERER "/system/mafw/mafw-gst-subtitles-renderer"
+#define HAL_VIDEOOUT_UDI "/org/freedesktop/Hal/devices" \
+        "/platform_soc_audio_logicaldev_input"
+
+/*----------------------------------------------------------------------------
+  Static variable definitions
+  ----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+  Plugin initialization
+  ----------------------------------------------------------------------------*/
+
+static gboolean mafw_gst_renderer_initialize(MafwRegistry *registry,
+                                          GError **error);
+static void mafw_gst_renderer_deinitialize(GError **error);
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+static void mafw_gst_renderer_dispose(GObject *object);
+static void mafw_gst_renderer_finalize(GObject *object);
+
+/*----------------------------------------------------------------------------
+  Hal callbacks
+  ----------------------------------------------------------------------------*/
+static void _property_modified(LibHalContext *ctx, const char *udi,
+                               const char *key, dbus_bool_t is_removed,
+                               dbus_bool_t is_added);
+static gboolean _tv_out_is_connected(LibHalContext *ctx, const char *udi);
+
+/*----------------------------------------------------------------------------
+  GConf notifications
+  ----------------------------------------------------------------------------*/
+
+static void _battery_cover_open_cb(GConfClient *client,
+                                  guint cnxn_id,
+                                  GConfEntry *entry,
+                                  MafwGstRenderer *renderer);
+
+static void _autoload_subtitles_changed_cb(GConfClient *client,
+                                          guint cnxn_id,
+                                          GConfEntry *entry,
+                                          MafwGstRenderer *renderer);
+
+static void _subtitle_font_changed_cb(GConfClient *client,
+                                     guint cnxn_id,
+                                     GConfEntry *entry,
+                                     MafwGstRenderer *renderer);
+
+/*----------------------------------------------------------------------------
+  Gnome VFS notifications
+  ----------------------------------------------------------------------------*/
+
+static void _volume_pre_unmount_cb(GnomeVFSVolumeMonitor *monitor,
+                                  GnomeVFSVolume *volume,
+                                   MafwGstRenderer *renderer);
+
+/*----------------------------------------------------------------------------
+  Playback
+  ----------------------------------------------------------------------------*/
+
+static void _signal_state_changed(MafwGstRenderer * self);
+static void _signal_media_changed(MafwGstRenderer * self);
+static void _signal_playlist_changed(MafwGstRenderer * self);
+static void _signal_transport_actions_property_changed(MafwGstRenderer * self);
+
+/*----------------------------------------------------------------------------
+  Properties
+  ----------------------------------------------------------------------------*/
+
+static void _set_error_policy(MafwGstRenderer *renderer, MafwRendererErrorPolicy policy);
+static MafwRendererErrorPolicy _get_error_policy(MafwGstRenderer *renderer);
+
+static void mafw_gst_renderer_set_property(MafwExtension *self, const gchar *key,
+                                        const GValue *value);
+static void mafw_gst_renderer_get_property(MafwExtension *self, const gchar *key,
+                                        MafwExtensionPropertyCallback callback,
+                                        gpointer user_data);
+
+/*----------------------------------------------------------------------------
+  Metadata
+  ----------------------------------------------------------------------------*/
+
+static void _notify_metadata(MafwSource *cb_source,
+                            const gchar *cb_object_id,
+                            GHashTable *cb_metadata,
+                            gpointer cb_user_data,
+                            const GError *cb_error);
+
+/*----------------------------------------------------------------------------
+  Notification operations
+  ----------------------------------------------------------------------------*/
+
+static void _notify_play(MafwGstRendererWorker *worker, gpointer owner);
+static void _notify_pause(MafwGstRendererWorker *worker, gpointer owner);
+static void _notify_seek(MafwGstRendererWorker *worker, gpointer owner);
+static void _notify_buffer_status(MafwGstRendererWorker *worker, gpointer owner,
+                                 gdouble percent);
+static void _notify_eos(MafwGstRendererWorker *worker, gpointer owner);
+static void _error_handler(MafwGstRendererWorker *worker, gpointer owner,
+                          const GError *error);
+
+#ifdef HAVE_CONIC
+/*----------------------------------------------------------------------------
+  Connection
+  ----------------------------------------------------------------------------*/
+
+static void _connection_init(MafwGstRenderer *renderer);
+#endif
+
+/*----------------------------------------------------------------------------
+  Plugin initialization
+  ----------------------------------------------------------------------------*/
+
+/*
+ * Registers the plugin descriptor making this plugin available to the
+ * framework and applications
+ */
+G_MODULE_EXPORT MafwPluginDescriptor mafw_gst_renderer_plugin_description = {
+       { .name         = MAFW_GST_RENDERER_PLUGIN_NAME },
+       .initialize     = mafw_gst_renderer_initialize,
+       .deinitialize   = mafw_gst_renderer_deinitialize,
+};
+
+static gboolean mafw_gst_renderer_initialize(MafwRegistry *registry,
+                                          GError **error)
+{
+       MafwGstRenderer *self;
+
+       g_assert(registry != NULL);
+       self = MAFW_GST_RENDERER(mafw_gst_renderer_new(registry));
+       mafw_registry_add_extension(registry, MAFW_EXTENSION(self));
+
+       return TRUE;
+}
+
+static void mafw_gst_renderer_deinitialize(GError **error)
+{
+}
+
+/*----------------------------------------------------------------------------
+  GObject initialization
+  ----------------------------------------------------------------------------*/
+
+G_DEFINE_TYPE(MafwGstRenderer, mafw_gst_renderer, MAFW_TYPE_RENDERER);
+
+static void mafw_gst_renderer_class_init(MafwGstRendererClass *klass)
+{
+       GObjectClass *gclass = NULL;
+       MafwRendererClass *renderer_class = NULL;
+       const gchar *preloaded_plugins[] = {"playback", "uridecodebin",
+                               "coreelements", "typefindfunctions", "dsp",
+                               "pulseaudio", "xvimagesink", NULL};
+       gint i = 0;
+       GObject *plugin;
+
+       gclass = G_OBJECT_CLASS(klass);
+       g_return_if_fail(gclass != NULL);
+
+       renderer_class = MAFW_RENDERER_CLASS(klass);
+       g_return_if_fail(renderer_class != NULL);
+
+       /* GObject */
+
+       gclass->dispose = mafw_gst_renderer_dispose;
+       gclass->finalize = mafw_gst_renderer_finalize;
+
+       /* Playback */
+
+       renderer_class->play = mafw_gst_renderer_play;
+       renderer_class->play_object = mafw_gst_renderer_play_object;
+       renderer_class->stop = mafw_gst_renderer_stop;
+       renderer_class->pause = mafw_gst_renderer_pause;
+       renderer_class->resume = mafw_gst_renderer_resume;
+       renderer_class->get_status = mafw_gst_renderer_get_status;
+
+       /* Playlist operations */
+
+       renderer_class->assign_playlist = mafw_gst_renderer_assign_playlist;
+       renderer_class->next = mafw_gst_renderer_next;
+       renderer_class->previous = mafw_gst_renderer_previous;
+       renderer_class->goto_index = mafw_gst_renderer_goto_index;
+
+       /* Playback position */
+
+       renderer_class->set_position = mafw_gst_renderer_set_position;
+       renderer_class->get_position = mafw_gst_renderer_get_position;
+
+       /* Metadata */
+
+       renderer_class->get_current_metadata =
+               mafw_gst_renderer_get_current_metadata;
+
+       /* Properties */
+
+       MAFW_EXTENSION_CLASS(klass)->get_extension_property =
+               (gpointer) mafw_gst_renderer_get_property;
+       MAFW_EXTENSION_CLASS(klass)->set_extension_property =
+               (gpointer) mafw_gst_renderer_set_property;
+
+       gst_init(NULL, NULL);
+       gst_pb_utils_init();
+
+       /* Pre-load some common plugins */
+       while (preloaded_plugins[i])
+       {
+               plugin = G_OBJECT(gst_plugin_load_by_name(preloaded_plugins[i]));
+               if (plugin)
+                       g_object_unref(plugin);
+               else
+                       g_debug("Can not load plugin: %s", preloaded_plugins[i]);
+               i++;
+       }
+}
+
+static void mafw_gst_renderer_init(MafwGstRenderer *self)
+{
+       MafwGstRenderer *renderer = NULL;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       renderer = MAFW_GST_RENDERER(self);
+       g_return_if_fail(renderer != NULL);
+
+       mafw_extension_add_property(MAFW_EXTENSION(self), "volume", G_TYPE_UINT);
+#ifdef MAFW_GST_RENDERER_ENABLE_MUTE
+       mafw_extension_add_property(MAFW_EXTENSION(self), "mute", G_TYPE_BOOLEAN);
+#endif
+       mafw_extension_add_property(MAFW_EXTENSION(self), "xid", G_TYPE_UINT);
+       mafw_extension_add_property(MAFW_EXTENSION(self), "error-policy", G_TYPE_UINT);
+       MAFW_EXTENSION_SUPPORTS_AUTOPAINT(self);
+       MAFW_EXTENSION_SUPPORTS_COLORKEY(self);
+#ifdef HAVE_GDKPIXBUF
+       mafw_extension_add_property(MAFW_EXTENSION(self),
+                                    "current-frame-on-pause",
+                                    G_TYPE_BOOLEAN);
+#endif
+        mafw_extension_add_property(MAFW_EXTENSION(self),
+                                    MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED,
+                                    G_TYPE_BOOLEAN);
+       MAFW_EXTENSION_SUPPORTS_TRANSPORT_ACTIONS(self);
+       renderer->media = g_new0(MafwGstRendererMedia, 1);
+       renderer->media->seekability = SEEKABILITY_UNKNOWN;
+       renderer->current_state = Stopped;
+
+       renderer->playlist = NULL;
+       renderer->iterator = NULL;
+       renderer->seeking_to = -1;
+        renderer->update_playcount_id = 0;
+
+        self->worker = mafw_gst_renderer_worker_new(self);
+
+        /* Set notification handlers for worker */
+        renderer->worker->notify_play_handler = _notify_play;
+        renderer->worker->notify_pause_handler = _notify_pause;
+        renderer->worker->notify_seek_handler = _notify_seek;
+        renderer->worker->notify_error_handler = _error_handler;
+        renderer->worker->notify_eos_handler = _notify_eos;
+       renderer->worker->notify_buffer_status_handler = _notify_buffer_status;
+
+       renderer->states = g_new0 (MafwGstRendererState*, _LastMafwPlayState);
+       renderer->states[Stopped] =
+               MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_stopped_new(self));
+       renderer->states[Transitioning] =
+               MAFW_GST_RENDERER_STATE(
+                       mafw_gst_renderer_state_transitioning_new(self));
+       renderer->states[Playing] =
+               MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_playing_new(self));
+       renderer->states[Paused] =
+               MAFW_GST_RENDERER_STATE(mafw_gst_renderer_state_paused_new(self));
+
+       renderer->current_state = Stopped;
+       renderer->resume_playlist = FALSE;
+       renderer->playback_mode = MAFW_GST_RENDERER_MODE_PLAYLIST;
+
+#ifdef HAVE_CONIC
+       renderer->connected = FALSE;
+       renderer->connection = NULL;
+
+       _connection_init(renderer);
+#endif
+       renderer->gconf_client = gconf_client_get_default();
+       gconf_client_add_dir(renderer->gconf_client, GCONF_OSSO_AF,
+                            GCONF_CLIENT_PRELOAD_ONELEVEL, &error);
+       if (error) {
+               g_warning("%s", error->message);
+               g_error_free(error);
+               error = NULL;
+       }
+
+       gconf_client_notify_add(renderer->gconf_client,
+                               GCONF_BATTERY_COVER_OPEN,
+                               (GConfClientNotifyFunc) _battery_cover_open_cb,
+                               renderer,
+                               NULL, &error);
+
+       if (error) {
+               g_warning("%s", error->message);
+               g_error_free(error);
+       }
+
+       gconf_client_add_dir(renderer->gconf_client,
+                            GCONF_MAFW_GST_SUBTITLES_RENDERER,
+                            GCONF_CLIENT_PRELOAD_ONELEVEL,
+                            &error);
+       if (error) {
+               g_warning("%s", error->message);
+               g_error_free(error);
+               error = NULL;
+       }
+
+       gconf_client_notify_add(renderer->gconf_client,
+                               GCONF_MAFW_GST_SUBTITLES_RENDERER "/autoload_subtitles",
+                               (GConfClientNotifyFunc) _autoload_subtitles_changed_cb,
+                               renderer,
+                               NULL, &error);
+       if (error) {
+               g_warning("%s", error->message);
+               g_error_free(error);
+       }
+
+       gconf_client_notify_add(renderer->gconf_client,
+                               GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_encoding",
+                               (GConfClientNotifyFunc) _subtitle_font_changed_cb,
+                               renderer,
+                               NULL, &error);
+       if (error) {
+               g_warning("%s", error->message);
+               g_error_free(error);
+       }
+
+       gconf_client_notify_add(renderer->gconf_client,
+                               GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_font",
+                               (GConfClientNotifyFunc) _subtitle_font_changed_cb,
+                               renderer,
+                               NULL, &error);
+       if (error) {
+               g_warning("%s", error->message);
+               g_error_free(error);
+       }
+
+       if (self->worker->pipeline) {
+               gconf_client_notify(renderer->gconf_client,
+                                   GCONF_MAFW_GST_SUBTITLES_RENDERER "/autoload_subtitles");
+
+               gconf_client_notify(renderer->gconf_client,
+                                   GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_encoding");
+
+               gconf_client_notify(renderer->gconf_client,
+                                   GCONF_MAFW_GST_SUBTITLES_RENDERER "/subtitle_font");
+       }
+
+       if (gnome_vfs_init()) {
+               GnomeVFSVolumeMonitor *monitor = gnome_vfs_get_volume_monitor();
+               g_signal_connect(monitor, "volume-pre-unmount", 
+                                G_CALLBACK(_volume_pre_unmount_cb), renderer);
+       } else {
+               g_warning("Failed to initialize gnome-vfs");
+       }
+}
+
+static void mafw_gst_renderer_dispose(GObject *object)
+{
+       MafwGstRenderer *renderer;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(object));
+
+       renderer = MAFW_GST_RENDERER(object);
+
+       if (renderer->worker != NULL) {
+               mafw_gst_renderer_worker_exit(renderer->worker);
+               renderer->seek_pending = FALSE;
+               g_free(renderer->worker);
+               renderer->worker = NULL;
+       }
+
+       if (renderer->registry != NULL) {
+               g_object_unref(renderer->registry);
+               renderer->registry = NULL;
+       }
+
+       if (renderer->states != NULL) {
+               guint i = 0;
+
+               for (i = 0; i < _LastMafwPlayState; i++) {
+                       if (renderer->states[i] != NULL)
+                               g_object_unref(renderer->states[i]);
+               }
+               g_free(renderer->states);
+               renderer->states = NULL;
+       }
+
+       if (renderer->hal_ctx != NULL) {
+                libhal_device_remove_property_watch(renderer->hal_ctx,
+                                                    HAL_VIDEOOUT_UDI,
+                                                    NULL);
+               libhal_ctx_shutdown(renderer->hal_ctx, NULL);
+               libhal_ctx_free(renderer->hal_ctx);
+       }
+
+#ifdef HAVE_CONIC
+       if (renderer->connection != NULL) {
+               g_object_unref(renderer->connection);
+               renderer->connection = NULL;
+       }
+#endif
+
+       if (renderer->gconf_client != NULL) {
+               g_object_unref(renderer->gconf_client);
+               renderer->gconf_client = NULL;
+       }
+
+       G_OBJECT_CLASS(mafw_gst_renderer_parent_class)->dispose(object);
+}
+
+static void mafw_gst_renderer_finalize(GObject *object)
+{
+       MafwGstRenderer *self = (MafwGstRenderer*) object;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       mafw_gst_renderer_clear_media(self);
+
+       if (self->media)
+       {
+               g_free(self->media);
+               self->media = NULL;
+       }
+
+       G_OBJECT_CLASS(mafw_gst_renderer_parent_class)->finalize(object);
+}
+
+/**
+ * mafw_gst_renderer_new:
+ * @registry: The registry that owns this renderer.
+ *
+ * Creates a new MafwGstRenderer object
+ */
+GObject *mafw_gst_renderer_new(MafwRegistry* registry)
+{
+       GObject* object;
+       LibHalContext *ctx;
+       DBusConnection *conn;
+       DBusError err;
+        char **jackets;
+        char **jack;
+        gint num_jacks;
+
+       object = g_object_new(MAFW_TYPE_GST_RENDERER,
+                             "uuid", MAFW_GST_RENDERER_UUID,
+                             "name", MAFW_GST_RENDERER_NAME,
+                             "plugin", MAFW_GST_RENDERER_PLUGIN_NAME,
+                             NULL);
+       g_assert(object != NULL);
+       MAFW_GST_RENDERER(object)->registry = g_object_ref(registry);
+
+       /* Set default error policy */
+       MAFW_GST_RENDERER(object)->error_policy =
+               MAFW_RENDERER_ERROR_POLICY_CONTINUE;
+
+        MAFW_GST_RENDERER(object)->tv_connected = FALSE;
+
+       /* Setup hal connection for reacting usb cable connected event */
+       dbus_error_init(&err);
+       conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
+
+        if (dbus_error_is_set(&err)) {
+               g_warning("Couldn't setup HAL connection: %s", err.message);
+                dbus_error_free(&err);
+
+               goto err1;
+        }
+       ctx = libhal_ctx_new();
+       libhal_ctx_set_dbus_connection(ctx, conn);
+       libhal_ctx_set_user_data(ctx, object);
+
+       if (libhal_ctx_init(ctx, &err) == FALSE) {
+               if (dbus_error_is_set(&err)) {
+                       g_warning("Could not initialize hal: %s", err.message);
+                       dbus_error_free(&err);
+               } else {
+                       g_warning("Could not initialize hal");
+               }
+               goto err2;
+       }
+
+       libhal_device_add_property_watch(ctx, HAL_VIDEOOUT_UDI, &err);
+
+       if (dbus_error_is_set(&err)) {
+               g_warning("Could not start watching usb device: %s",
+                          err.message);
+               dbus_error_free(&err);
+
+               goto err3;
+       }
+        libhal_ctx_set_device_property_modified(ctx, _property_modified);
+
+        /* Initializes blanking policy */
+        jackets = libhal_find_device_by_capability(ctx,
+                                                   "input.jack.video-out",
+                                                   &num_jacks, NULL);
+       if (jackets != NULL) {
+                jack = jackets;
+                while (*jack) {
+                        if (_tv_out_is_connected(ctx, *jack)) {
+                                MAFW_GST_RENDERER(object)->tv_connected = TRUE;
+                                break;
+                        }
+                        jack++;
+                }
+
+                blanking_control(*jack == NULL);
+                libhal_free_string_array(jackets);
+        }
+
+       MAFW_GST_RENDERER(object)->hal_ctx = ctx;
+
+       return object;
+err3:
+       libhal_ctx_shutdown(ctx, NULL);
+err2:
+       libhal_ctx_free(ctx);
+err1:
+       return object;
+}
+
+/**
+ * mafw_gst_renderer_error_quark:
+ *
+ * Fetches the quark representing the domain of the errors in the
+ * gst renderer
+ *
+ * Return value: a quark identifying the error domain of the
+ * #MafwGstRenderer objects.
+ *
+ **/
+GQuark mafw_gst_renderer_error_quark(void)
+{
+       return g_quark_from_static_string("mafw-gst-renderer-error-quark");
+}
+
+void mafw_gst_renderer_set_playback_mode(MafwGstRenderer *self,
+                                      MafwGstRendererPlaybackMode mode)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       self->playback_mode = mode;
+}
+
+MafwGstRendererPlaybackMode mafw_gst_renderer_get_playback_mode(
+       MafwGstRenderer *self)
+{
+       g_return_val_if_fail(MAFW_IS_GST_RENDERER(self),
+                            MAFW_GST_RENDERER_MODE_STANDALONE);
+       return self->playback_mode;
+}
+
+/*----------------------------------------------------------------------------
+  Set Media
+  ----------------------------------------------------------------------------*/
+
+static MafwSource* _get_source(MafwGstRenderer *renderer,
+                              const gchar *object_id)
+{
+       MafwSource* source;
+       gchar* sourceid = NULL;
+
+       g_assert(object_id != NULL);
+
+       /* Attempt to find a source that provided the object ID */
+       mafw_source_split_objectid(object_id, &sourceid, NULL);
+       source = MAFW_SOURCE(mafw_registry_get_extension_by_uuid(
+                                    renderer->registry, sourceid));
+       g_free(sourceid);
+
+       return source;
+}
+
+void mafw_gst_renderer_get_metadata(MafwGstRenderer* self,
+                                 const gchar* objectid,
+                                 GError **error)
+{
+       MafwSource* source;
+
+       g_assert(self != NULL);
+
+       /*
+        * Any error here is an error when trying to Play, so
+        * it must be handled by error policy.
+        * Problem: if we get an error here and we are not in
+        * Transitioning yet (maybe we are still in Stopped state)
+        * then the policy may move to next and stay Stopped (instead of
+        * trying to play), so  errors need to be handled by the policy
+         * in an idle callback, so that any error that may happen here
+         * is not processed until we have moved to Transitioning state
+        */
+
+       source = _get_source(self, objectid);
+       if (source != NULL)
+       {
+               /* List of metadata keys that we are interested in when going to
+                  Transitioning state */
+               static const gchar * const keys[] =
+                       { MAFW_METADATA_KEY_URI,
+                         MAFW_METADATA_KEY_IS_SEEKABLE,
+                         MAFW_METADATA_KEY_DURATION,
+                               NULL };
+
+               /* Source found, get metadata */
+               mafw_source_get_metadata(source, objectid,
+                                        keys,
+                                        _notify_metadata,
+                                        self);
+
+       }
+       else
+       {
+               /* This is a playback error: execute error policy */
+               MafwGstRendererErrorClosure *error_closure;
+               error_closure = g_new0(MafwGstRendererErrorClosure, 1);
+               error_closure->renderer = self;
+               g_set_error (&(error_closure->error),
+                            MAFW_EXTENSION_ERROR,
+                            MAFW_EXTENSION_ERROR_EXTENSION_NOT_AVAILABLE,
+                            "Unable to find source for current object ID");
+               g_idle_add(mafw_gst_renderer_manage_error_idle, error_closure);
+       }
+}
+
+void mafw_gst_renderer_set_object(MafwGstRenderer *self, const gchar *object_id)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(object_id != NULL);
+
+       /* This is intended to be called only when using play_object(),
+        * as for playlists we use set_media_playlist()
+        */
+
+       /* Stop any ongoing playback */
+       mafw_gst_renderer_clear_media(renderer);
+
+       /* Set new object */
+       renderer->media->object_id = g_strdup(object_id);
+
+       /* Signal media changed */
+       _signal_media_changed(renderer);
+}
+
+
+/**
+ * mafw_gst_renderer_clear_media:
+ *
+ * @renderer A #MafwGstRenderer whose media to clear
+ *
+ * Clears & frees the renderer's current media details
+ **/
+void mafw_gst_renderer_clear_media(MafwGstRenderer *self)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(self->media != NULL);
+
+       g_free(self->media->object_id);
+       self->media->object_id = NULL;
+
+       g_free(self->media->uri);
+       self->media->uri = NULL;
+
+       g_free(self->media->title);
+       self->media->title = NULL;
+
+       g_free(self->media->artist);
+       self->media->artist = NULL;
+
+       g_free(self->media->album);
+       self->media->album = NULL;
+
+       self->media->duration = 0;
+       self->media->position = 0;
+}
+
+
+/**
+ * mafw_gst_renderer_set_media_playlist:
+ *
+ * @self A #MafwGstRenderer, whose media to set
+ *
+ * Set current media from the renderer's playlist, using the current playlist index.
+ **/
+void mafw_gst_renderer_set_media_playlist(MafwGstRenderer* self)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       /* Get rid of old media details */
+       mafw_gst_renderer_clear_media(self);
+
+        if (self->playlist != NULL &&
+            mafw_playlist_iterator_get_size(self->iterator, NULL) > 0) {
+                /* Get the current item from playlist */
+                self->media->object_id =
+                       g_strdup(mafw_playlist_iterator_get_current_objectid(self->iterator));
+        } else {
+                self->media->object_id = NULL;
+       }
+
+       _signal_media_changed(self);
+}
+
+#ifdef HAVE_CONIC
+/*----------------------------------------------------------------------------
+  Connection
+  ----------------------------------------------------------------------------*/
+
+static void
+_con_ic_status_handler(ConIcConnection *conn, ConIcConnectionEvent *event,
+                      gpointer data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) data;
+
+       g_assert(MAFW_IS_GST_RENDERER(renderer));
+
+       renderer->connected =
+               con_ic_connection_event_get_status(event) ==
+               CON_IC_STATUS_CONNECTED;
+}
+
+static void
+_connection_init(MafwGstRenderer *renderer)
+{
+       g_assert (MAFW_IS_GST_RENDERER(renderer));
+
+       if (renderer->connection == NULL) {
+               renderer->connection = con_ic_connection_new();
+               renderer->connected = FALSE;
+
+               g_assert(renderer->connection != NULL);
+       }
+
+       g_object_set(renderer->connection, "automatic-connection-events",
+                     TRUE, NULL);
+        g_signal_connect(renderer->connection, "connection-event",
+                        G_CALLBACK (_con_ic_status_handler), renderer);
+
+       con_ic_connection_connect(renderer->connection,
+                                 CON_IC_CONNECT_FLAG_AUTOMATICALLY_TRIGGERED);
+}
+#endif
+
+/*----------------------------------------------------------------------------
+  Hal callbacks
+  ----------------------------------------------------------------------------*/
+
+static gboolean _tv_out_is_connected(LibHalContext *ctx, const char *udi)
+{
+        gboolean is_tv_out_jack = FALSE;
+        char **jack_types;
+        char **jack;
+
+        if (udi == NULL) {
+                return FALSE;
+        }
+
+        jack_types = libhal_device_get_property_strlist(ctx, udi,
+                                                        "input.jack.type",
+                                                        NULL);
+        if (jack_types == NULL) {
+                return FALSE;
+        }
+
+        jack = jack_types;
+        while (*jack) {
+                if (strcmp(*jack, "video-out") == 0) {
+                        is_tv_out_jack = TRUE;
+                        break;
+                } else {
+                        jack++;
+                }
+        }
+
+        libhal_free_string_array(jack_types);
+
+        return is_tv_out_jack;
+}
+
+static void _property_modified(LibHalContext *ctx, const char *udi,
+                               const char *key, dbus_bool_t is_removed,
+                               dbus_bool_t is_added)
+{
+        MafwGstRenderer *renderer;
+        gboolean connected;
+        GValue value = { 0 };
+
+        g_debug("HAL property modified! jack changed\n");
+        connected = _tv_out_is_connected(ctx, udi);
+        renderer = MAFW_GST_RENDERER(libhal_ctx_get_user_data(ctx));
+        if (renderer->tv_connected != connected) {
+                /* Notify the change */
+                renderer->tv_connected = connected;
+                g_value_init(&value, G_TYPE_BOOLEAN);
+                g_value_set_boolean(&value, renderer->tv_connected);
+                mafw_extension_emit_property_changed(
+                        MAFW_EXTENSION(renderer),
+                        MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED,
+                        &value);
+                g_value_unset(&value);
+        }
+        blanking_control(connected == FALSE);
+}
+
+/*----------------------------------------------------------------------------
+  GConf notifications
+  ----------------------------------------------------------------------------*/
+
+static void _battery_cover_open_cb(GConfClient *client,
+                                  guint cnxn_id,
+                                  GConfEntry *entry,
+                                  MafwGstRenderer *renderer)
+{
+       GConfValue *value = NULL;
+       gboolean is_cover_open;
+
+       value = gconf_entry_get_value(entry);
+       is_cover_open = gconf_value_get_bool(value);
+
+       if (is_cover_open) {
+               /* External mmc could be removed!. */
+               const gchar *emmc_path = g_getenv("MMC_MOUNTPOINT");
+
+               mafw_gst_renderer_state_handle_pre_unmount(
+                       MAFW_GST_RENDERER_STATE(
+                               renderer->states[renderer->current_state]),
+                                emmc_path);
+       }
+}
+
+static void _autoload_subtitles_changed_cb(GConfClient *client,
+                                          guint cnxn_id,
+                                          GConfEntry *entry,
+                                          MafwGstRenderer *renderer)
+{
+       GConfValue *value = NULL;
+       gboolean enabled = FALSE;
+
+       value = gconf_entry_get_value(entry);
+       if (value == NULL)
+               return;
+
+       enabled = gconf_value_get_bool(value);
+
+       if (enabled)
+               renderer->worker->subtitles.enabled = TRUE;
+       else
+               renderer->worker->subtitles.enabled = FALSE;
+}
+
+static void _subtitle_font_changed_cb(GConfClient *client,
+                                     guint cnxn_id,
+                                     GConfEntry *entry,
+                                     MafwGstRenderer *renderer)
+{
+       const gchar *key = NULL;
+       GConfValue *value = NULL;
+       const gchar *str_value = NULL;
+
+       key = gconf_entry_get_key(entry);
+
+       /* Only key without absolute path is required */
+       key += strlen(GCONF_MAFW_GST_SUBTITLES_RENDERER) + 1;
+
+       value = gconf_entry_get_value(entry);
+       if (value)
+               str_value = gconf_value_get_string(value);
+       else
+               str_value = NULL;
+
+       if (strcmp(key, "subtitle_font") == 0) {
+               if (renderer->worker->subtitles.font)
+                       g_free(renderer->worker->subtitles.font);
+
+               if (str_value)
+                       renderer->worker->subtitles.font = g_strdup(str_value);
+               else
+                       renderer->worker->subtitles.font = NULL;
+       } else if (strcmp(key, "subtitle_encoding") == 0) {
+               if (renderer->worker->subtitles.encoding)
+                       g_free(renderer->worker->subtitles.encoding);
+
+               if (str_value)
+                       renderer->worker->subtitles.encoding = g_strdup(str_value);
+               else
+                       renderer->worker->subtitles.encoding = NULL;
+       } else {
+               g_warning("Wrong %s key, %s", GCONF_MAFW_GST_SUBTITLES_RENDERER, key);
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Gnome VFS notifications
+  ----------------------------------------------------------------------------*/
+
+static void _volume_pre_unmount_cb(GnomeVFSVolumeMonitor *monitor, 
+                                  GnomeVFSVolume *volume,
+                                  MafwGstRenderer *renderer)
+{
+       gchar *location = gnome_vfs_volume_get_activation_uri(volume);
+       if (!location) {
+               return;
+       }
+       
+       mafw_gst_renderer_state_handle_pre_unmount(
+               MAFW_GST_RENDERER_STATE(
+                       renderer->states[renderer->current_state]),
+               location);
+       
+       g_free(location);
+}
+
+/*----------------------------------------------------------------------------
+  Signals
+  ----------------------------------------------------------------------------*/
+
+
+/**
+ * _signal_state_changed:
+ * @self: A #MafwGstRenderer
+ *
+ * Signals state_changed to all UIs
+ **/
+static void _signal_state_changed(MafwGstRenderer * self)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_signal_emit_by_name(MAFW_RENDERER(self),
+                             "state-changed", self->current_state);
+}
+
+/**
+ * _signal_playlist_changed:
+ * @self: A #MafwGstRenderer
+ *
+ * Signals playlist update to all UIs
+ **/
+static void _signal_playlist_changed(MafwGstRenderer * self)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_signal_emit_by_name(MAFW_RENDERER(self),
+                             "playlist-changed", self->playlist);
+}
+
+/**
+ * _signal_media_changed:
+ * @self: A #MafwGstRenderer
+ *
+ * Signals media_changed to all UIs
+ **/
+static void _signal_media_changed(MafwGstRenderer *self)
+{
+
+       MafwGstRendererPlaybackMode mode;
+       gint index;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       mode = mafw_gst_renderer_get_playback_mode(MAFW_GST_RENDERER(self));
+       if ((mode == MAFW_GST_RENDERER_MODE_STANDALONE) ||
+            (self->iterator == NULL)) {
+               index = -1;
+       } else {
+               index = mafw_playlist_iterator_get_current_index(self->iterator);
+       }
+
+       g_signal_emit_by_name(MAFW_RENDERER(self),
+                             "media-changed",
+                             index,
+                             self->media->object_id);
+}
+
+/**
+ * _signal_transport_actions_property_changed:
+ * @self: A #MafwGstRenderer
+ *
+ * Signals transport_actions property_changed to all UIs
+ **/
+static void _signal_transport_actions_property_changed(MafwGstRenderer * self)
+{
+       GValue *value;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       value = mafw_gst_renderer_state_get_property_value(
+               MAFW_GST_RENDERER_STATE(
+                       self->states[self->current_state]),
+               MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS);
+
+        if (value) {
+                mafw_extension_emit_property_changed(
+                        MAFW_EXTENSION(self),
+                        MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS,
+                        value);
+                g_value_unset(value);
+                g_free(value);
+        }
+}
+
+
+/*----------------------------------------------------------------------------
+  State pattern support
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_set_state(MafwGstRenderer *self, MafwPlayState state)
+{
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       self->current_state = state;
+       _signal_state_changed(self);
+       _signal_transport_actions_property_changed(self);
+}
+
+void mafw_gst_renderer_play(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                         gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_play(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_play_object(MafwRenderer *self,
+                                const gchar *object_id,
+                                MafwRendererPlaybackCB callback,
+                                gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(object_id != NULL);
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_play_object(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               object_id,
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_stop(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                         gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       renderer->play_failed_count = 0;
+       mafw_gst_renderer_state_stop(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+
+void mafw_gst_renderer_pause(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                          gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_pause(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_resume(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                           gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_resume(
+               MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_next(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                         gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       renderer->play_failed_count = 0;
+       mafw_gst_renderer_state_next(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_previous(MafwRenderer *self, MafwRendererPlaybackCB callback,
+                             gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       renderer->play_failed_count = 0;
+       mafw_gst_renderer_state_previous(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_goto_index(MafwRenderer *self, guint index,
+                               MafwRendererPlaybackCB callback,
+                               gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       renderer->play_failed_count = 0;
+       mafw_gst_renderer_state_goto_index(
+               MAFW_GST_RENDERER_STATE(renderer->states[renderer->current_state]),
+               index,
+               &error);
+
+       if (callback != NULL)
+               callback(self, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_get_position(MafwRenderer *self, MafwRendererPositionCB callback,
+                                 gpointer user_data)
+{
+       MafwGstRenderer *renderer;
+       gint pos;
+       GError *error = NULL;
+
+       g_return_if_fail(callback != NULL);
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       renderer = MAFW_GST_RENDERER(self);
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_get_position(
+                MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]),
+               &pos,
+               &error);
+       
+       callback(self, pos, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+void mafw_gst_renderer_set_position(MafwRenderer *self, MafwRendererSeekMode mode,
+                                  gint seconds, MafwRendererPositionCB callback,
+                                  gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer *) self;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_set_position(
+               MAFW_GST_RENDERER_STATE (renderer->states[renderer->current_state]),
+               mode,
+               seconds,
+               &error);
+
+       if (callback != NULL)
+               callback(self, seconds, user_data, error);
+       if (error)
+               g_error_free(error);
+}
+
+gboolean mafw_gst_renderer_manage_error_idle(gpointer data)
+{
+        MafwGstRendererErrorClosure *mec = (MafwGstRendererErrorClosure *) data;
+
+        mafw_gst_renderer_manage_error(mec->renderer, mec->error);
+       if (mec->error)
+               g_error_free(mec->error);
+        g_free(mec);
+
+        return FALSE;
+}
+
+static void _run_error_policy(MafwGstRenderer *self, const GError *in_err,
+                             GError **out_err)
+{
+        g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+        gboolean play_next = FALSE;
+
+        /* Check what to do on error */
+       if (in_err->code == MAFW_EXTENSION_ERROR_OUT_OF_MEMORY) {
+                play_next = FALSE;
+       } else {
+               MafwGstRendererPlaybackMode mode;
+
+               mode = mafw_gst_renderer_get_playback_mode(self);
+
+               if (mode == MAFW_GST_RENDERER_MODE_PLAYLIST) {
+                       /* In playlist mode we try to play next if
+                          error policy suggests so */
+                       play_next =
+                               (_get_error_policy(self) ==
+                                MAFW_RENDERER_ERROR_POLICY_CONTINUE);
+               } else {
+                       /* In standalone mode, then switch back to playlist
+                          mode and resume if necessary or move to Stopped
+                          otherwise */
+                       mafw_gst_renderer_set_playback_mode(
+                               self, MAFW_GST_RENDERER_MODE_PLAYLIST);
+                       mafw_gst_renderer_set_media_playlist(self);
+                       if (self->resume_playlist) {
+                               mafw_gst_renderer_play(MAFW_RENDERER(self),
+                                                    NULL, NULL);
+                       } else {
+                               mafw_gst_renderer_worker_stop(self->worker);
+                               mafw_gst_renderer_set_state(self, Stopped);
+                       }
+                       if (out_err) *out_err = g_error_copy(in_err);
+
+                       /* Bail out, he have already managed the error
+                          for the case of standalone mode */
+                       return;
+               }
+       }
+
+       if (play_next) {
+               if (self->playlist){
+                       MafwPlaylistIteratorMovementResult result;
+
+                       result = mafw_playlist_iterator_move_to_next(self->iterator,
+                                                                     NULL);
+                       self->play_failed_count++;
+
+                       if (mafw_playlist_iterator_get_size(self->iterator,
+                               NULL) <=
+                               self->play_failed_count)
+                       {
+                               mafw_gst_renderer_state_stop(
+                                       MAFW_GST_RENDERER_STATE(self->states[self->current_state]),
+                                       NULL);
+                               self->play_failed_count = 0;
+                               mafw_gst_renderer_set_media_playlist(self);
+                       } else if (result !=
+                                  MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK) {
+                               mafw_playlist_iterator_reset(self->iterator, NULL);
+                               mafw_gst_renderer_set_media_playlist(self);
+                               mafw_gst_renderer_stop(MAFW_RENDERER(self), NULL, NULL);
+                       } else {
+                               mafw_gst_renderer_set_media_playlist(self);
+                               mafw_gst_renderer_play(MAFW_RENDERER(self), NULL, NULL);
+                       }
+
+                       if (out_err) *out_err = g_error_copy(in_err);
+               }
+       } else {
+               /* We cannot move to next in the playlist or decided
+                  we do not want to do it, just stop on error */
+                mafw_gst_renderer_stop(MAFW_RENDERER(self), NULL, NULL);
+                if (out_err) *out_err = g_error_copy(in_err);
+       }
+}
+
+static void _metadata_set_cb(MafwSource *self, const gchar *object_id,
+                               const gchar **failed_keys, gpointer user_data,
+                               const GError *error)
+{
+       if (error != NULL) {
+               g_debug("Ignoring error received when setting metadata: "
+                       "%s (%d): %s", g_quark_to_string(error->domain),
+                       error->code, error->message);
+       } else {
+               g_debug("Metadata set correctly");
+       }
+}
+
+/**
+ * _update_playcount_metadata_cb:
+ * @cb_source:   The #MafwSource that sent the metadata results
+ * @cb_object_id: The object ID, whose metadata results were received
+ * @cb_metadata: GHashTable containing metadata key-value pairs
+ * @cb_user_data: Optional user data pointer (self)
+ * @cb_error:    Set if any errors occurred during metadata browsing
+ *
+ * Receives the results of a metadata request about the playcount. It increases
+ * it, or sets to 1, and sets the metadata to that.
+ */
+static void _update_playcount_metadata_cb (MafwSource *cb_source,
+                                          const gchar *cb_object_id,
+                                          GHashTable *cb_metadata,
+                                          gpointer cb_user_data,
+                                          const GError *cb_error)
+{
+       GValue *curval = NULL;
+       gint curplaycount = -1;
+       GHashTable *mdata = cb_user_data;
+
+       if (cb_error == NULL) {
+               if (cb_metadata)
+                       curval = mafw_metadata_first(cb_metadata,
+                               MAFW_METADATA_KEY_PLAY_COUNT);
+               if (curval && !G_VALUE_HOLDS(curval, G_TYPE_INT))
+                       goto set_data;
+               if (curval)
+               {
+                       curplaycount = g_value_get_int(curval);
+                       curplaycount++;
+               }
+               else
+               { /* Playing at first time, or not supported... */
+                       curplaycount = 1;
+               }
+               if (!mdata)
+                       mdata = mafw_metadata_new();
+               mafw_metadata_add_int(mdata,
+                                       MAFW_METADATA_KEY_PLAY_COUNT,
+                                       curplaycount);
+
+       } else {
+               g_warning("_playcount_metadata received an error: "
+                         "%s (%d): %s", g_quark_to_string(cb_error->domain),
+                         cb_error->code, cb_error->message);
+               if (mdata)
+                       g_hash_table_unref(mdata);
+               return;
+       }
+set_data:
+       
+       if (mdata)
+       {
+               mafw_source_set_metadata(cb_source, cb_object_id, mdata,
+                                               _metadata_set_cb, NULL);
+               g_hash_table_unref(mdata);
+       }
+}
+
+/**
+ * mafw_gst_renderer_add_lastplayed:
+ * @mdata:   Exisiting mdata, or NULL
+ *
+ * Sets the MAFW_METADATA_KEY_LAST_PLAYED metadata in the given metadata-table,
+ * or creates a new metadata-table, and sets the current time there.
+ */
+static GHashTable *mafw_gst_renderer_add_lastplayed(GHashTable *mdata)
+{
+       GHashTable *metadata;
+       GTimeVal timeval;
+
+       
+       if (!mdata)
+               metadata = mafw_metadata_new();
+       else
+               metadata = mdata;
+       
+               
+
+       g_get_current_time(&timeval);
+               
+       mafw_metadata_add_long(metadata,
+                                       MAFW_METADATA_KEY_LAST_PLAYED,
+                                       timeval.tv_sec);
+       return metadata;
+}
+
+/**
+ * mafw_gst_renderer_increase_playcount:
+ * @self:   Gst renderer
+ * @object_id: The object ID of the touched object
+ * @mdat: Existing metadatas to add the playcount to, or NULL
+ *
+ * Increases the playcount of the given object.
+ */
+static void mafw_gst_renderer_increase_playcount(MafwGstRenderer* self,
+                                       const gchar *object_id, GHashTable *mdat)
+{
+       MafwSource* source;
+
+       g_assert(self != NULL);
+       source = _get_source(self, object_id);
+       if (source != NULL)
+       {
+               static const gchar * const keys[] =
+                       { MAFW_METADATA_KEY_PLAY_COUNT, NULL };
+
+               mafw_source_get_metadata(source, object_id,
+                                        keys,
+                                        _update_playcount_metadata_cb,
+                                        mdat);
+
+       }
+}
+
+/**
+ * mafw_gst_renderer_update_stats:
+ * @data: user data
+ *
+ * Updates both playcount and lastplayed after a while.
+ **/
+gboolean mafw_gst_renderer_update_stats(gpointer data)
+{
+        MafwGstRenderer *renderer = (MafwGstRenderer *) data;
+
+        /* Update stats only for audio content */
+        if (renderer->media->object_id &&
+            !renderer->worker->media.has_visual_content) {
+               GHashTable *mdata = mafw_gst_renderer_add_lastplayed(NULL);
+               mafw_gst_renderer_increase_playcount(renderer,
+                                                     renderer->media->object_id,
+                                                     mdata);
+       }
+        renderer->update_playcount_id = 0;
+        return FALSE;
+}
+
+void mafw_gst_renderer_update_source_duration(MafwGstRenderer *renderer,
+                                             gint duration)
+{
+       GHashTable *metadata;
+       MafwSource* source;
+
+       source = _get_source(renderer, renderer->media->object_id);
+       g_return_if_fail(source != NULL);
+
+       renderer->media->duration = duration;
+
+       g_debug("updated source duration to %d", duration);
+
+       metadata = mafw_metadata_new();
+       mafw_metadata_add_int(metadata, MAFW_METADATA_KEY_DURATION, duration);
+
+       mafw_source_set_metadata(source, renderer->media->object_id, metadata,
+                                _metadata_set_cb, NULL);
+       g_hash_table_unref(metadata);
+}
+
+/**
+ * _notify_metadata:
+ * @source:   The #MafwSource that sent the metadata results
+ * @objectid: The object ID, whose metadata results were received
+ * @metadata: GHashTable containing metadata key-value pairs
+ * @userdata: Optional user data pointer (self)
+ * @error:    Set if any errors occurred during metadata browsing
+ *
+ * Receives the results of a metadata request.
+ */
+static void _notify_metadata (MafwSource *cb_source,
+                             const gchar *cb_object_id,
+                             GHashTable *cb_metadata,
+                             gpointer cb_user_data,
+                             const GError *cb_error)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) cb_user_data;
+        GError *mafw_error = NULL;
+       GError *error = NULL;
+       GValue *mval;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       g_debug("running _notify_metadata...");
+
+       mval = mafw_metadata_first(cb_metadata, MAFW_METADATA_KEY_URI);
+
+       if (cb_error == NULL && mval != NULL) {
+               mafw_gst_renderer_state_notify_metadata(
+                       MAFW_GST_RENDERER_STATE(
+                               renderer->states[renderer->current_state]),
+                       cb_object_id,
+                       cb_metadata,
+                       &error);
+       }
+       else {
+                g_set_error(&mafw_error,
+                           MAFW_RENDERER_ERROR,
+                           MAFW_RENDERER_ERROR_URI_NOT_AVAILABLE, "%s",
+                           cb_error ? cb_error->message : "URI not available");
+               mafw_gst_renderer_manage_error(renderer, mafw_error);
+                g_error_free(mafw_error);
+       }
+}
+
+static void _notify_play(MafwGstRendererWorker *worker, gpointer owner)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       g_debug("running _notify_play...");
+
+       mafw_gst_renderer_state_notify_play(renderer->states[renderer->current_state],
+                                         &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error",
+                                     error->domain,
+                                     error->code,
+                                     error->message);
+               g_error_free (error);
+       }
+}
+
+static void _notify_pause(MafwGstRendererWorker *worker, gpointer owner)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER (renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_notify_pause(renderer->states[renderer->current_state],
+                                          &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error",
+                                     error->domain, error->code,
+                                     error->message);
+               g_error_free(error);
+       }
+}
+
+static void _notify_buffer_status (MafwGstRendererWorker *worker,
+                                  gpointer owner,
+                                  gdouble percent)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_notify_buffer_status(
+               renderer->states[renderer->current_state],
+               percent,
+               &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION (renderer), "error",
+                                     error->domain, error->code,
+                                     error->message);
+               g_error_free(error);
+       }
+}
+
+static void _notify_seek(MafwGstRendererWorker *worker, gpointer owner)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_notify_seek(renderer->states[renderer->current_state],
+                                         &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error",
+                                     error->domain, error->code,
+                                     error->message);
+               g_error_free(error);
+       }
+}
+
+static void _playlist_changed_handler(MafwPlaylistIterator *iterator,
+                                     gboolean clip_changed, GQuark domain,
+                                     gint code, const gchar *message,
+                                     gpointer user_data)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) user_data;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       /* We update the current index and media here,  for this is
+          the same for all the states. Then we delegate in the state
+          to finish the task (for example, start playback if needed) */
+
+       if (renderer->playlist == NULL) {
+               g_critical("Got iterator:contents-changed but renderer has no" \
+                          "playlist assigned!. Skipping...");
+               return;
+       }
+
+       if (domain != 0) {
+               g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error",
+                                     domain, code, message);
+       } else {
+               GError *error = NULL;
+               MafwGstRendererPlaybackMode mode;
+               
+               mode = mafw_gst_renderer_get_playback_mode(renderer);
+
+               /* Only in non-playobject mode */               
+               if (clip_changed && mode == MAFW_GST_RENDERER_MODE_PLAYLIST)
+                       mafw_gst_renderer_set_media_playlist(renderer);
+
+               /* We let the state know if the current clip has changed as
+                  result of this operation, so it can do its work */
+               mafw_gst_renderer_state_playlist_contents_changed_handler(
+                       renderer->states[renderer->current_state],
+                       clip_changed,
+                       &error);
+
+               if (error != NULL) {
+                       g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error",
+                                             error->domain, error->code,
+                                             error->message);
+                       g_error_free(error);
+               }
+       }
+}
+
+static void _error_handler(MafwGstRendererWorker *worker, gpointer owner,
+                          const GError *error)
+{
+        MafwGstRenderer *renderer = MAFW_GST_RENDERER(owner);
+
+        mafw_gst_renderer_manage_error(renderer, error);
+}
+
+void mafw_gst_renderer_manage_error(MafwGstRenderer *self, const GError *error)
+{
+        GError *new_err = NULL;
+       GError *raise_error = NULL;
+        GQuark new_err_domain = MAFW_RENDERER_ERROR;
+        gint new_err_code = 0;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+
+       g_return_if_fail((self->states != 0) &&
+                        (self->current_state != _LastMafwPlayState) &&
+                        (self->states[self->current_state] != NULL));
+
+       g_warning("Got error in renderer:\n\tdomain: %d, code: %d, message: %s",
+                 error->domain, error->code, error->message);
+
+        /* Get a MAFW error */
+       if (error->domain == GST_RESOURCE_ERROR) {
+               /* handle RESOURCE errors */
+               switch (error->code) {
+               case GST_RESOURCE_ERROR_READ:
+                       if (is_current_uri_stream(self)) {
+#ifdef HAVE_CONIC
+                               if (self->connected) {
+                                       new_err_code = MAFW_RENDERER_ERROR_STREAM_DISCONNECTED;
+                               } else {
+                                       new_err_domain = MAFW_EXTENSION_ERROR;
+                                       new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN;
+                               }
+#else
+                               /* Stream + cannot read resource ->
+                                  disconnected */
+                               new_err_code = MAFW_RENDERER_ERROR_STREAM_DISCONNECTED;
+#endif
+                       } else {
+                               /* This shouldn't happen */
+                               /* Unknown RESOURCE error */
+                               new_err_domain = MAFW_EXTENSION_ERROR;
+                               new_err_code = MAFW_EXTENSION_ERROR_FAILED;
+                       }
+                       break;
+               case GST_RESOURCE_ERROR_NOT_FOUND:
+#ifdef HAVE_CONIC
+                       if (!is_current_uri_stream(self) || self->connected) {
+                               new_err_code =
+                                       MAFW_RENDERER_ERROR_INVALID_URI;
+                       } else {
+                               new_err_domain = MAFW_EXTENSION_ERROR;
+                               new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN;
+                       }
+#else
+                       new_err_code =
+                               MAFW_RENDERER_ERROR_INVALID_URI;
+#endif
+                       break;
+               case GST_RESOURCE_ERROR_OPEN_READ_WRITE:
+               case GST_RESOURCE_ERROR_OPEN_READ:
+#ifdef HAVE_CONIC
+                       if (!is_current_uri_stream(self) || self->connected) {
+                               new_err_code =
+                                       MAFW_RENDERER_ERROR_MEDIA_NOT_FOUND;
+                       } else {
+                               new_err_domain = MAFW_EXTENSION_ERROR;
+                               new_err_code = MAFW_EXTENSION_ERROR_NETWORK_DOWN;
+                       }
+#else
+                       new_err_code =
+                               MAFW_RENDERER_ERROR_MEDIA_NOT_FOUND;
+#endif
+                       break;
+               case GST_RESOURCE_ERROR_NO_SPACE_LEFT:
+                       new_err_domain = MAFW_EXTENSION_ERROR;
+                       new_err_code = MAFW_EXTENSION_ERROR_OUT_OF_MEMORY;
+                       break;
+               case GST_RESOURCE_ERROR_WRITE:
+                       /* DSP renderers send ERROR_WRITE when they find
+                          corrupted data */
+                       new_err_code = MAFW_RENDERER_ERROR_CORRUPTED_FILE;
+                       break;
+               case GST_RESOURCE_ERROR_SEEK:
+                       new_err_code = MAFW_RENDERER_ERROR_CANNOT_SET_POSITION;
+                       break;
+               default:
+                       /* Unknown RESOURCE error */
+                       new_err_domain = MAFW_EXTENSION_ERROR;
+                       new_err_code = MAFW_EXTENSION_ERROR_FAILED;
+               }
+
+       } else if (error->domain == GST_STREAM_ERROR) {
+               /* handle STREAM errors */
+               switch (error->code) {
+               case GST_STREAM_ERROR_TYPE_NOT_FOUND:
+                       new_err_code = MAFW_RENDERER_ERROR_TYPE_NOT_AVAILABLE;
+                       break;
+               case GST_STREAM_ERROR_FORMAT:
+               case GST_STREAM_ERROR_WRONG_TYPE:
+               case GST_STREAM_ERROR_FAILED:
+                        new_err_code = MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE;
+                       break;
+               case GST_STREAM_ERROR_DECODE:
+               case GST_STREAM_ERROR_DEMUX:
+                        new_err_code = MAFW_RENDERER_ERROR_CORRUPTED_FILE;
+                       break;
+               case GST_STREAM_ERROR_CODEC_NOT_FOUND:
+                       new_err_code = MAFW_RENDERER_ERROR_CODEC_NOT_FOUND;
+                       break;
+               case GST_STREAM_ERROR_DECRYPT:
+               case GST_STREAM_ERROR_DECRYPT_NOKEY:
+                       new_err_code = MAFW_RENDERER_ERROR_DRM;
+                       break;
+               default:
+                       /* Unknown STREAM error */
+                        new_err_domain = MAFW_EXTENSION_ERROR;
+                        new_err_code = MAFW_EXTENSION_ERROR_FAILED;
+                }
+       } else if (error->domain == MAFW_GST_RENDERER_ERROR) {
+               /* Handle own errors. Errors that belong to this domain:
+                     - MAFW_GST_RENDERER_ERROR_PLUGIN_NOT_FOUND,
+                     - MAFW_GST_RENDERER_ERROR_VIDEO_CODEC_NOT_SUPPORTED,
+                     - MAFW_GST_RENDERER_ERROR_AUDIO_CODEC_NOT_SUPPORTED */
+               new_err_code = MAFW_RENDERER_ERROR_UNSUPPORTED_TYPE;
+       } else if (error->domain == MAFW_RENDERER_ERROR) {
+               /* Worker may have sent MAFW_RENDERER_ERROR as well.
+                  No processing needed */
+               new_err_code = error->code;
+       } else {
+                /* default */
+               /* Unknown error */
+               new_err_domain = MAFW_EXTENSION_ERROR;
+               new_err_code = MAFW_EXTENSION_ERROR_FAILED;
+       }
+
+       g_set_error(&new_err, new_err_domain, new_err_code, "%s", error->message);
+
+        _run_error_policy(self, new_err, &raise_error);
+        g_error_free(new_err);
+
+        if (raise_error) {
+                g_signal_emit_by_name(MAFW_EXTENSION (self), "error",
+                                     raise_error->domain,
+                                     raise_error->code,
+                                     raise_error->message);
+                g_error_free(raise_error);
+        }
+}
+
+static void _notify_eos(MafwGstRendererWorker *worker, gpointer owner)
+{
+       MafwGstRenderer *renderer = (MafwGstRenderer*) owner;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER (renderer));
+
+       g_return_if_fail((renderer->states != 0) &&
+                        (renderer->current_state != _LastMafwPlayState) &&
+                        (renderer->states[renderer->current_state] != NULL));
+
+       mafw_gst_renderer_state_notify_eos(renderer->states[renderer->current_state],
+                                        &error);
+
+       if (error != NULL) {
+               g_signal_emit_by_name(MAFW_EXTENSION(renderer), "error",
+                                     error->domain, error->code,
+                                     error->message);
+               g_error_free(error);
+       }
+}
+
+/*----------------------------------------------------------------------------
+  Status
+  ----------------------------------------------------------------------------*/
+
+void mafw_gst_renderer_get_status(MafwRenderer *self, MafwRendererStatusCB callback,
+                               gpointer user_data)
+{
+       MafwGstRenderer* renderer;
+       gint index;
+       MafwGstRendererPlaybackMode mode;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(callback != NULL);
+       renderer = MAFW_GST_RENDERER(self);
+
+       mode = mafw_gst_renderer_get_playback_mode(MAFW_GST_RENDERER(self));
+       if ((mode == MAFW_GST_RENDERER_MODE_STANDALONE) || (renderer->iterator == NULL)) {
+               index = -1;
+       } else {
+               index =
+                       mafw_playlist_iterator_get_current_index(renderer->iterator);
+       }
+
+       /* TODO: Set error parameter */
+       callback(self, renderer->playlist, index, renderer->current_state,
+                (const gchar*) renderer->media->object_id, user_data, NULL);
+}
+
+void mafw_gst_renderer_get_current_metadata(MafwRenderer *self,
+                                           MafwRendererMetadataResultCB callback,
+                                           gpointer user_data)
+{
+       MafwGstRenderer *renderer;
+       GHashTable *metadata;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       renderer = MAFW_GST_RENDERER(self);
+
+       metadata = mafw_gst_renderer_worker_get_current_metadata(
+                       renderer->worker);
+
+       callback(self,
+                (const gchar*) renderer->media->object_id,
+                metadata,
+                user_data,
+                NULL);
+}
+
+/*----------------------------------------------------------------------------
+  Playlist
+  ----------------------------------------------------------------------------*/
+
+static void
+_playlist_contents_changed_handler(MafwPlaylist *playlist,
+                                       guint from, guint nremove,
+                                       guint nreplace,
+                                       MafwGstRenderer *renderer)
+{
+       /* Item(s) added to playlist, so new playable items could come */
+       if (nreplace)
+               renderer->play_failed_count = 0;
+}
+
+gboolean mafw_gst_renderer_assign_playlist(MafwRenderer *self,
+                                          MafwPlaylist *playlist,
+                                          GError **error)
+{
+       MafwGstRenderer* renderer = (MafwGstRenderer*) self;
+
+       g_return_val_if_fail(MAFW_IS_GST_RENDERER(self), FALSE);
+
+       /* Get rid of previously assigned playlist  */
+       if (renderer->playlist != NULL) {
+               g_signal_handlers_disconnect_matched(renderer->iterator,
+                                                    (GSignalMatchType) G_SIGNAL_MATCH_FUNC,
+                                                    0, 0, NULL,
+                                                    _playlist_changed_handler,
+                                                    NULL);
+               g_signal_handlers_disconnect_matched(renderer->playlist,
+                                       (GSignalMatchType) G_SIGNAL_MATCH_FUNC,
+                                       0, 0, NULL,
+                                       G_CALLBACK(_playlist_contents_changed_handler),
+                                       NULL);
+               /* Decrement the use count of the previous playlist because the
+                  renderer isn't going to use it more */
+               mafw_playlist_decrement_use_count(renderer->playlist, NULL);
+
+               g_object_unref(renderer->iterator);
+               g_object_unref(renderer->playlist);
+       }
+
+       /* Assign the new playlist */
+        if (playlist == NULL) {
+               renderer->playlist = NULL;
+               renderer->iterator = NULL;
+       } else {
+               GError *new_error = NULL;
+               MafwPlaylistIterator *iterator = NULL;
+
+               iterator = mafw_playlist_iterator_new();
+               mafw_playlist_iterator_initialize(iterator, playlist,
+                                                  &new_error);
+               
+               g_object_ref(playlist);
+
+               if (new_error == NULL) {
+
+                       renderer->playlist = playlist;
+                       renderer->iterator = iterator;
+
+                       /* Increment the use_count to avoid the playlist destruction
+                          while the playlist is assigned to some renderer */
+                       mafw_playlist_increment_use_count(renderer->playlist, NULL);
+
+                       g_signal_connect(iterator,
+                                        "playlist-changed",
+                                        G_CALLBACK(_playlist_changed_handler),
+                                        renderer);
+                       g_signal_connect(renderer->playlist,
+                                        "contents-changed",
+                                        G_CALLBACK(_playlist_contents_changed_handler),
+                                        renderer);
+               }
+               else {
+                       g_propagate_error (error, new_error);
+               }
+       }
+
+       /* Set the new media and signal playlist changed signal */
+       _signal_playlist_changed(renderer);
+       mafw_gst_renderer_set_media_playlist(renderer);
+
+
+       /* Stop playback */
+       mafw_gst_renderer_stop(MAFW_RENDERER(renderer), NULL , NULL);
+
+       return TRUE;
+}
+
+MafwGstRendererMovementResult mafw_gst_renderer_move(MafwGstRenderer *renderer,
+                                                  MafwGstRendererMovementType type,
+                                                  guint index,
+                                                  GError **error)
+{
+       MafwGstRendererMovementResult value = MAFW_GST_RENDERER_MOVE_RESULT_OK;
+
+        if (renderer->playlist == NULL) {
+               value = MAFW_GST_RENDERER_MOVE_RESULT_NO_PLAYLIST;
+        } else {
+               MafwPlaylistIteratorMovementResult result;
+
+               switch (type) {
+               case MAFW_GST_RENDERER_MOVE_TYPE_INDEX:
+                       result =
+                               mafw_playlist_iterator_move_to_index(renderer->iterator,
+                                                                     index,
+                                                                     error);
+                       break;
+               case MAFW_GST_RENDERER_MOVE_TYPE_PREV:
+                       result =
+                               mafw_playlist_iterator_move_to_prev(renderer->iterator,
+                                                                    error);
+                       break;
+               case MAFW_GST_RENDERER_MOVE_TYPE_NEXT:
+                       result =
+                               mafw_playlist_iterator_move_to_next(renderer->iterator,
+                                                                    error);
+                       break;
+               }
+
+               switch (result) {
+               case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_OK:
+                       value = MAFW_GST_RENDERER_MOVE_RESULT_OK;
+                       mafw_gst_renderer_set_media_playlist(renderer);
+                       break;
+               case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_INVALID:
+                       g_critical("Iterator is invalid!");
+                       value = MAFW_GST_RENDERER_MOVE_RESULT_ERROR;
+                       break;
+               case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_ERROR:
+                       value = MAFW_GST_RENDERER_MOVE_RESULT_ERROR;
+                       break;
+               case MAFW_PLAYLIST_ITERATOR_MOVE_RESULT_LIMIT:
+                       value = MAFW_GST_RENDERER_MOVE_RESULT_PLAYLIST_LIMIT;
+                       break;
+               }
+       }
+
+       return value;
+}
+
+/*----------------------------------------------------------------------------
+  Properties
+  ----------------------------------------------------------------------------*/
+
+static void _set_error_policy(MafwGstRenderer *renderer, MafwRendererErrorPolicy policy)
+{
+       renderer->error_policy = policy;
+}
+
+static MafwRendererErrorPolicy _get_error_policy(MafwGstRenderer *renderer)
+{
+       return renderer->error_policy;
+}
+
+static void mafw_gst_renderer_get_property(MafwExtension *self,
+                                        const gchar *key,
+                                        MafwExtensionPropertyCallback callback,
+                                        gpointer user_data)
+{
+       MafwGstRenderer *renderer;
+       GValue *value = NULL;
+       GError *error = NULL;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(callback != NULL);
+       g_return_if_fail(key != NULL);
+
+       renderer = MAFW_GST_RENDERER(self);
+       if (!strcmp(key, MAFW_PROPERTY_RENDERER_VOLUME)) {
+               guint volume;
+
+               volume = mafw_gst_renderer_worker_get_volume(
+                       renderer->worker);
+
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_UINT);
+               g_value_set_uint(value, volume);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_MUTE)) {
+               gboolean mute;
+               mute = mafw_gst_renderer_worker_get_mute(renderer->worker);
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_BOOLEAN);
+               g_value_set_boolean(value, mute);
+       }
+       else if (!strcmp (key, MAFW_PROPERTY_RENDERER_XID)) {
+               guint xid;
+               xid = mafw_gst_renderer_worker_get_xid(renderer->worker);
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_UINT);
+               g_value_set_uint(value, xid);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_ERROR_POLICY)) {
+               guint policy;
+               policy = _get_error_policy(renderer);
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_UINT);
+               g_value_set_uint(value, policy);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_AUTOPAINT)) {
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_BOOLEAN);
+               g_value_set_boolean(
+                       value,
+                       mafw_gst_renderer_worker_get_autopaint(
+                               renderer->worker));
+       } else if (!strcmp(key, MAFW_PROPERTY_RENDERER_COLORKEY)) {
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_INT);
+               g_value_set_int(
+                       value,
+                       mafw_gst_renderer_worker_get_colorkey(
+                               renderer->worker));
+       }
+#ifdef HAVE_GDKPIXBUF
+       else if (!strcmp(key,
+                        MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE)) {
+               gboolean current_frame_on_pause;
+               current_frame_on_pause =
+                       mafw_gst_renderer_worker_get_current_frame_on_pause(renderer->worker);
+               value = g_new0(GValue, 1);
+               g_value_init(value, G_TYPE_BOOLEAN);
+               g_value_set_boolean(value, current_frame_on_pause);
+       }
+#endif
+        else if (!strcmp(key,
+                         MAFW_PROPERTY_GST_RENDERER_TV_CONNECTED)) {
+                value = g_new0(GValue, 1);
+                g_value_init(value, G_TYPE_BOOLEAN);
+                g_value_set_boolean(value, renderer->tv_connected);
+        }
+       else if (!strcmp(key,
+                        MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS)){
+               /* Delegate in the state. */
+               value = mafw_gst_renderer_state_get_property_value(
+                       MAFW_GST_RENDERER_STATE(
+                               renderer->states[renderer->current_state]),
+                       MAFW_PROPERTY_RENDERER_TRANSPORT_ACTIONS);
+
+               if (!value) {
+                       /* Something goes wrong. */
+                       error = g_error_new(
+                               MAFW_GST_RENDERER_ERROR,
+                               MAFW_EXTENSION_ERROR_GET_PROPERTY,
+                               "Error while getting the property value");
+               }
+       }
+       else {
+               /* Unsupported property */
+               error = g_error_new(MAFW_GST_RENDERER_ERROR,
+                                   MAFW_EXTENSION_ERROR_GET_PROPERTY,
+                                   "Unsupported property");
+       }
+
+       callback(self, key, value, user_data, error);
+}
+
+static void mafw_gst_renderer_set_property(MafwExtension *self,
+                                        const gchar *key,
+                                        const GValue *value)
+{
+       MafwGstRenderer *renderer;
+
+       g_return_if_fail(MAFW_IS_GST_RENDERER(self));
+       g_return_if_fail(key != NULL);
+
+       renderer = MAFW_GST_RENDERER(self);
+
+       if (!strcmp(key, MAFW_PROPERTY_RENDERER_VOLUME)) {
+               guint volume = g_value_get_uint(value);
+               if (volume > 100)
+                       volume = 100;
+                mafw_gst_renderer_worker_set_volume(renderer->worker,
+                                                          volume);
+                /* Property-changed emision is done by worker */
+                return;
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_MUTE)) {
+               gboolean mute = g_value_get_boolean(value);
+                mafw_gst_renderer_worker_set_mute(renderer->worker, mute);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_XID)) {
+               XID xid = g_value_get_uint(value);
+               mafw_gst_renderer_worker_set_xid(renderer->worker, xid);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_ERROR_POLICY)) {
+               MafwRendererErrorPolicy policy = g_value_get_uint(value);
+               _set_error_policy(renderer, policy);
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_AUTOPAINT)) {
+               mafw_gst_renderer_worker_set_autopaint(
+                       renderer->worker,
+                       g_value_get_boolean(value));
+       }
+       else if (!strcmp(key, MAFW_PROPERTY_RENDERER_COLORKEY)) {
+               mafw_gst_renderer_worker_set_colorkey(
+                       renderer->worker,
+                       g_value_get_int(value));
+       }
+#ifdef HAVE_GDKPIXBUF
+       else if (!strcmp(key,
+                        MAFW_PROPERTY_GST_RENDERER_CURRENT_FRAME_ON_PAUSE)) {
+               gboolean current_frame_on_pause = g_value_get_boolean(value);
+               mafw_gst_renderer_worker_set_current_frame_on_pause(renderer->worker,
+                                                                          current_frame_on_pause);
+       }
+#endif
+       else return;
+
+       /* FIXME I'm not sure when to emit property-changed signals.
+        * Maybe we should let the worker do it, when the change
+        * reached the hardware... */
+       mafw_extension_emit_property_changed(self, key, value);
+}
+
+/* vi: set noexpandtab ts=8 sw=8 cino=t0,(0: */