X-Git-Url: http://git.maemo.org/git/?p=mafwsubrenderer;a=blobdiff_plain;f=mafw-gst-subtitles-renderer%2Flibmafw-gst-renderer%2Fmafw-gst-renderer.c;fp=mafw-gst-subtitles-renderer%2Flibmafw-gst-renderer%2Fmafw-gst-renderer.c;h=e92ea6395f938e9b180c8b04156a09c2a1277261;hp=0000000000000000000000000000000000000000;hb=be2c98fb83895d10ac44af7b9a9c3e00ca54bf49;hpb=c2bbb2bb3bead80144e2dda3ccd40599e4a2b48d 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 index 0000000..e92ea63 --- /dev/null +++ b/mafw-gst-subtitles-renderer/libmafw-gst-renderer/mafw-gst-renderer.c @@ -0,0 +1,2320 @@ +/* + * This file is a part of MAFW + * + * Copyright (C) 2007, 2008, 2009 Nokia Corporation, all rights reserved. + * + * Contact: Visa Smolander + * + * 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 +#include +#include +#include +#include + +#include +#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 +#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: */