* dbus.c : D-Bus control interface
*****************************************************************************
* Copyright © 2006-2008 Rafaël Carré
- * Copyright © 2007-2010 Mirsal Ennaime
- * Copyright © 2009-2010 The VideoLAN team
+ * Copyright © 2007-2012 Mirsal Ennaime
+ * Copyright © 2009-2012 The VideoLAN team
+ * Copyright © 2013 Alex Merry
* $Id$
*
* Authors: Rafaël Carré <funman at videolanorg>
* Mirsal Ennaime <mirsal at mirsal fr>
+ * Alex Merry <dev at randomguy3 me uk>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#endif
#include <dbus/dbus.h>
-#include "dbus.h"
#include "dbus_common.h"
#include "dbus_root.h"
#include "dbus_player.h"
#include "dbus_tracklist.h"
#include "dbus_introspect.h"
+#define VLC_MODULE_LICENSE VLC_LICENSE_GPL_2_PLUS
#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_interface.h>
#include <vlc_playlist.h>
+#include <vlc_input.h>
#include <vlc_meta.h>
#include <vlc_mtime.h>
#include <vlc_fs.h>
-#include <vlc_aout.h>
#include <assert.h>
+#include <limits.h>
#include <string.h>
#include <poll.h>
#include <errno.h>
#include <unistd.h>
+#define DBUS_MPRIS_BUS_NAME "org.mpris.MediaPlayer2.vlc"
+#define DBUS_INSTANCE_ID_PREFIX "instance"
+
+#define SEEK_THRESHOLD 1000 /* µsec */
+
/*****************************************************************************
* Local prototypes.
*****************************************************************************/
+static DBusHandlerResult
+MPRISEntryPoint ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this );
+
+static const DBusObjectPathVTable dbus_mpris_vtable = {
+ NULL, MPRISEntryPoint, /* handler function */
+ NULL, NULL, NULL, NULL
+};
+
typedef struct
{
int signal;
int i_item;
} callback_info_t;
-typedef struct
-{
- mtime_t i_remaining;
- DBusTimeout *p_timeout;
-} timeout_info_t;
-
enum
{
PIPE_OUT = 0,
static int Open ( vlc_object_t * );
static void Close ( vlc_object_t * );
-static void Run ( intf_thread_t * );
+static void *Run ( void * );
static int TrackChange( intf_thread_t * );
static int AllCallback( vlc_object_t*, const char*, vlc_value_t, vlc_value_t, void* );
+static int InputCallback( vlc_object_t*, const char*, vlc_value_t, vlc_value_t, void* );
-static void dispatch_status_cb( DBusConnection *p_conn,
- DBusDispatchStatus i_status,
- void *p_data);
+static dbus_bool_t add_timeout(DBusTimeout *, void *);
+static void remove_timeout(DBusTimeout *, void *);
+static void toggle_timeout(DBusTimeout *, void *);
-static dbus_bool_t add_timeout ( DBusTimeout *p_timeout, void *p_data );
static dbus_bool_t add_watch ( DBusWatch *p_watch, void *p_data );
-
-static void remove_timeout ( DBusTimeout *p_timeout, void *p_data );
static void remove_watch ( DBusWatch *p_watch, void *p_data );
-
-static void timeout_toggled ( DBusTimeout *p_timeout, void *p_data );
static void watch_toggled ( DBusWatch *p_watch, void *p_data );
static void wakeup_main_loop( void *p_data );
-static int GetPollFds( intf_thread_t *p_intf, struct pollfd *p_fds );
-static int UpdateTimeouts( intf_thread_t *p_intf, mtime_t i_lastrun );
-
static void ProcessEvents ( intf_thread_t *p_intf,
callback_info_t **p_events,
int i_events );
struct pollfd *p_fds,
int i_fds );
-static void ProcessTimeouts( intf_thread_t *p_intf,
- DBusTimeout **p_timeouts,
- int i_timeouts );
-
static void DispatchDBusMessages( intf_thread_t *p_intf );
/*****************************************************************************
* Module descriptor
*****************************************************************************/
-#define DBUS_UNIQUE_TEXT N_("Unique DBUS service id (org.mpris.vlc-<pid>)")
-#define DBUS_UNIQUE_LONGTEXT N_( \
- "Use a unique dbus service id to identify this VLC instance on the DBUS bus. " \
- "The process identifier (PID) is added to the service name: org.mpris.vlc-<pid>" )
-
vlc_module_begin ()
- set_shortname( N_("dbus"))
+ set_shortname( N_("DBus"))
set_category( CAT_INTERFACE )
- set_subcategory( SUBCAT_INTERFACE_CONTROL )
set_description( N_("D-Bus control interface") )
set_capability( "interface", 0 )
set_callbacks( Open, Close )
- add_bool( "dbus-unique-service-id", false,
- DBUS_UNIQUE_TEXT, DBUS_UNIQUE_LONGTEXT, true )
vlc_module_end ()
/*****************************************************************************
*****************************************************************************/
static int Open( vlc_object_t *p_this )
-{ /* initialisation of the connection */
+{
intf_thread_t *p_intf = (intf_thread_t*)p_this;
- intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
- playlist_t *p_playlist;
- DBusConnection *p_conn;
- DBusError error;
- char *psz_service_name = NULL;
- if( !p_sys || !dbus_threads_init_default())
+ /* initialisation of the connection */
+ if( !dbus_threads_init_default() )
+ return VLC_EGENERIC;
+
+ intf_sys_t *p_sys = calloc( 1, sizeof( intf_sys_t ) );
+ if( unlikely(!p_sys) )
return VLC_ENOMEM;
- p_sys->b_meta_read = false;
- p_sys->b_dead = false;
- p_sys->p_input = NULL;
+ playlist_t *p_playlist;
+ DBusConnection *p_conn;
p_sys->i_player_caps = PLAYER_CAPS_NONE;
- p_sys->i_playing_state = -1;
+ p_sys->i_playing_state = PLAYBACK_STATE_INVALID;
if( vlc_pipe( p_sys->p_pipe_fds ) )
{
return VLC_EGENERIC;
}
- p_sys->b_unique = var_CreateGetBool( p_intf, "dbus-unique-service-id" );
- if( p_sys->b_unique )
- {
- if( asprintf( &psz_service_name, "%s-%d",
- DBUS_MPRIS_BUS_NAME, getpid() ) < 0 )
- {
- free( p_sys );
- return VLC_ENOMEM;
- }
- }
- else
- {
- psz_service_name = strdup(DBUS_MPRIS_BUS_NAME);
- }
-
+ DBusError error;
dbus_error_init( &error );
/* connect privately to the session bus
msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
error.message );
dbus_error_free( &error );
- free( psz_service_name );
+ close( p_sys->p_pipe_fds[1] );
+ close( p_sys->p_pipe_fds[0] );
free( p_sys );
return VLC_EGENERIC;
}
dbus_connection_set_exit_on_disconnect( p_conn, FALSE );
- /* register a well-known name on the bus */
- dbus_bus_request_name( p_conn, psz_service_name, 0, &error );
+ /* Register the entry point object path */
+ dbus_connection_register_object_path( p_conn, DBUS_MPRIS_OBJECT_PATH,
+ &dbus_mpris_vtable, p_this );
+
+ /* Try to register org.mpris.MediaPlayer2.vlc */
+ dbus_bus_request_name( p_conn, DBUS_MPRIS_BUS_NAME, 0, &error );
if( dbus_error_is_set( &error ) )
{
- msg_Err( p_this, "Error requesting service %s: %s",
- psz_service_name, error.message );
+ msg_Dbg( p_this, "Failed to get service name %s: %s",
+ DBUS_MPRIS_BUS_NAME, error.message );
dbus_error_free( &error );
- free( psz_service_name );
- free( p_sys );
- return VLC_EGENERIC;
- }
- msg_Info( p_intf, "listening on dbus as: %s", psz_service_name );
- free( psz_service_name );
- /* Register the entry point object path */
- dbus_connection_register_object_path( p_conn, DBUS_MPRIS_OBJECT_PATH,
- &dbus_mpris_vtable, p_this );
+ /* Register an instance-specific well known name of the form
+ * org.mpris.MediaPlayer2.vlc.instanceXXXX where XXXX is the
+ * current Process ID */
+ char unique_service[sizeof( DBUS_MPRIS_BUS_NAME ) +
+ sizeof( DBUS_INSTANCE_ID_PREFIX ) + 10];
+
+ snprintf( unique_service, sizeof (unique_service),
+ DBUS_MPRIS_BUS_NAME"."DBUS_INSTANCE_ID_PREFIX"%"PRIu32,
+ (uint32_t)getpid() );
+
+ dbus_bus_request_name( p_conn, unique_service, 0, &error );
+ if( dbus_error_is_set( &error ) )
+ {
+ msg_Err( p_this, "Failed to get service name %s: %s",
+ DBUS_MPRIS_BUS_NAME, error.message );
+ dbus_error_free( &error );
+ }
+ else
+ msg_Dbg( p_intf, "listening on dbus as: %s", unique_service );
+ }
+ else
+ msg_Dbg( p_intf, "listening on dbus as: %s", DBUS_MPRIS_BUS_NAME );
dbus_connection_flush( p_conn );
- p_intf->pf_run = Run;
p_intf->p_sys = p_sys;
p_sys->p_conn = p_conn;
p_sys->p_events = vlc_array_new();
p_playlist = pl_Get( p_intf );
p_sys->p_playlist = p_playlist;
- var_AddCallback( p_playlist, "item-current", AllCallback, p_intf );
+ var_AddCallback( p_playlist, "input-current", AllCallback, p_intf );
var_AddCallback( p_playlist, "intf-change", AllCallback, p_intf );
- var_AddCallback( p_playlist, "volume-change", AllCallback, p_intf );
- var_AddCallback( p_playlist, "volume-muted", AllCallback, p_intf );
+ var_AddCallback( p_playlist, "volume", AllCallback, p_intf );
+ var_AddCallback( p_playlist, "mute", AllCallback, p_intf );
var_AddCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
var_AddCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
var_AddCallback( p_playlist, "random", AllCallback, p_intf );
var_AddCallback( p_playlist, "repeat", AllCallback, p_intf );
var_AddCallback( p_playlist, "loop", AllCallback, p_intf );
-
- dbus_connection_set_dispatch_status_function( p_conn,
- dispatch_status_cb,
- p_intf, NULL );
+ var_AddCallback( p_playlist, "fullscreen", AllCallback, p_intf );
if( !dbus_connection_set_timeout_functions( p_conn,
add_timeout,
remove_timeout,
- timeout_toggled,
+ toggle_timeout,
p_intf, NULL ) )
- {
- dbus_connection_unref( p_conn );
- free( psz_service_name );
- free( p_sys );
- return VLC_ENOMEM;
- }
+ goto error;
if( !dbus_connection_set_watch_functions( p_conn,
add_watch,
remove_watch,
watch_toggled,
p_intf, NULL ) )
- {
- dbus_connection_unref( p_conn );
- free( psz_service_name );
- free( p_sys );
- return VLC_ENOMEM;
- }
+ goto error;
-/* dbus_connection_set_wakeup_main_function( p_conn,
- wakeup_main_loop,
- p_intf, NULL); */
+ if( vlc_clone( &p_sys->thread, Run, p_intf, VLC_THREAD_PRIORITY_LOW ) )
+ goto error;
return VLC_SUCCESS;
+
+error:
+ /* The dbus connection is private,
+ * so we are responsible for closing it
+ * XXX: Does this make sense when OOM ? */
+ dbus_connection_close( p_sys->p_conn );
+ dbus_connection_unref( p_conn );
+
+ vlc_array_destroy( p_sys->p_events );
+ vlc_array_destroy( p_sys->p_timeouts );
+ vlc_array_destroy( p_sys->p_watches );
+
+ vlc_mutex_destroy( &p_sys->lock );
+
+ close( p_sys->p_pipe_fds[1] );
+ close( p_sys->p_pipe_fds[0] );
+ free( p_sys );
+ return VLC_ENOMEM;
}
/*****************************************************************************
intf_sys_t *p_sys = p_intf->p_sys;
playlist_t *p_playlist = p_sys->p_playlist;
- var_DelCallback( p_playlist, "item-current", AllCallback, p_intf );
+ vlc_cancel( p_sys->thread );
+ vlc_join( p_sys->thread, NULL );
+
+ var_DelCallback( p_playlist, "input-current", AllCallback, p_intf );
var_DelCallback( p_playlist, "intf-change", AllCallback, p_intf );
- var_DelCallback( p_playlist, "volume-change", AllCallback, p_intf );
- var_DelCallback( p_playlist, "volume-muted", AllCallback, p_intf );
+ var_DelCallback( p_playlist, "volume", AllCallback, p_intf );
+ var_DelCallback( p_playlist, "mute", AllCallback, p_intf );
var_DelCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
var_DelCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
var_DelCallback( p_playlist, "random", AllCallback, p_intf );
var_DelCallback( p_playlist, "repeat", AllCallback, p_intf );
var_DelCallback( p_playlist, "loop", AllCallback, p_intf );
+ var_DelCallback( p_playlist, "fullscreen", AllCallback, p_intf );
if( p_sys->p_input )
{
- var_DelCallback( p_sys->p_input, "intf-event", AllCallback, p_intf );
+ var_DelCallback( p_sys->p_input, "intf-event", InputCallback, p_intf );
var_DelCallback( p_sys->p_input, "can-pause", AllCallback, p_intf );
var_DelCallback( p_sys->p_input, "can-seek", AllCallback, p_intf );
vlc_object_release( p_sys->p_input );
vlc_array_destroy( p_sys->p_events );
vlc_array_destroy( p_sys->p_timeouts );
vlc_array_destroy( p_sys->p_watches );
+ close( p_sys->p_pipe_fds[1] );
+ close( p_sys->p_pipe_fds[0] );
free( p_sys );
}
-static void dispatch_status_cb( DBusConnection *p_conn,
- DBusDispatchStatus i_status,
- void *p_data)
+static dbus_bool_t add_timeout(DBusTimeout *to, void *data)
{
- (void) p_conn;
- intf_thread_t *p_intf = (intf_thread_t*) p_data;
+ intf_thread_t *intf = data;
+ intf_sys_t *sys = intf->p_sys;
+
+ mtime_t *expiry = malloc(sizeof (*expiry));
+ if (unlikely(expiry == NULL))
+ return FALSE;
+
+ dbus_timeout_set_data(to, expiry, free);
- static const char *p_statuses[] = { "DATA_REMAINS",
- "COMPLETE",
- "NEED_MEMORY" };
+ vlc_mutex_lock(&sys->lock);
+ vlc_array_append(sys->p_timeouts, to);
+ vlc_mutex_unlock(&sys->lock);
- msg_Dbg( p_intf,
- "DBus dispatch status changed to %s.",
- p_statuses[i_status]);
+ return TRUE;
}
-static dbus_bool_t add_timeout( DBusTimeout *p_timeout, void *p_data )
+static void remove_timeout(DBusTimeout *to, void *data)
{
- intf_thread_t *p_intf = (intf_thread_t*) p_data;
- intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
+ intf_thread_t *intf = data;
+ intf_sys_t *sys = intf->p_sys;
+ unsigned idx = vlc_array_index_of_item(sys->p_timeouts, to);
- timeout_info_t *p_info = calloc( 1, sizeof( timeout_info_t ) );
- p_info->i_remaining = dbus_timeout_get_interval( p_timeout ) * 1000;/* µs */
- p_info->p_timeout = p_timeout;
+ vlc_mutex_lock(&sys->lock);
+ vlc_array_remove(sys->p_timeouts, idx);
+ vlc_mutex_unlock(&sys->lock);
+}
- dbus_timeout_set_data( p_timeout, p_info, free );
+static void toggle_timeout(DBusTimeout *to, void *data)
+{
+ intf_thread_t *intf = data;
+ intf_sys_t *sys = intf->p_sys;
+ mtime_t *expiry = dbus_timeout_get_data(to);
- vlc_mutex_lock( &p_sys->lock );
- vlc_array_append( p_sys->p_timeouts, p_timeout );
- vlc_mutex_unlock( &p_sys->lock );
+ vlc_mutex_lock(&sys->lock);
+ if (dbus_timeout_get_enabled(to))
+ *expiry = mdate() + UINT64_C(1000) * dbus_timeout_get_interval(to);
+ vlc_mutex_unlock(&sys->lock);
- return TRUE;
+ wakeup_main_loop(intf);
}
-static void remove_timeout( DBusTimeout *p_timeout, void *p_data )
+/**
+ * Computes the time until the next timeout expiration.
+ * @note Interface lock must be held.
+ * @return The time in milliseconds until the next expiration,
+ * or -1 if there are no pending timeouts.
+ */
+static int next_timeout(intf_thread_t *intf)
{
- intf_thread_t *p_intf = (intf_thread_t*) p_data;
- intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
+ intf_sys_t *sys = intf->p_sys;
+ mtime_t next_timeout = LAST_MDATE;
+ unsigned count = vlc_array_count(sys->p_timeouts);
- vlc_mutex_lock( &p_sys->lock );
+ for (unsigned i = 0; i < count; i++)
+ {
+ DBusTimeout *to = vlc_array_item_at_index(sys->p_timeouts, i);
- vlc_array_remove( p_sys->p_timeouts,
- vlc_array_index_of_item( p_sys->p_timeouts, p_timeout ) );
+ if (!dbus_timeout_get_enabled(to))
+ continue;
- vlc_mutex_unlock( &p_sys->lock );
+ mtime_t *expiry = dbus_timeout_get_data(to);
+
+ if (next_timeout > *expiry)
+ next_timeout = *expiry;
+ }
+
+ if (next_timeout >= LAST_MDATE)
+ return -1;
+
+ next_timeout /= 1000;
+
+ if (next_timeout > INT_MAX)
+ return INT_MAX;
+
+ return (int)next_timeout;
}
-static void timeout_toggled( DBusTimeout *p_timeout, void *p_data )
+/**
+ * Process pending D-Bus timeouts.
+ *
+ * @note Interface lock must be held.
+ */
+static void process_timeouts(intf_thread_t *intf)
{
- intf_thread_t *p_intf = (intf_thread_t*) p_data;
+ intf_sys_t *sys = intf->p_sys;
- msg_Dbg( p_intf, "Toggling dbus timeout" );
-
- if( dbus_timeout_get_enabled( p_timeout ) )
+ for (int i = 0; i < vlc_array_count(sys->p_timeouts); i++)
{
- msg_Dbg( p_intf, "Timeout is enabled, main loop needs to wake up" );
- wakeup_main_loop( p_intf );
+ DBusTimeout *to = vlc_array_item_at_index(sys->p_timeouts, i);
+
+ if (!dbus_timeout_get_enabled(to))
+ continue;
+
+ mtime_t *expiry = dbus_timeout_get_data(to);
+ if (*expiry > mdate())
+ continue;
+
+ expiry += UINT64_C(1000) * dbus_timeout_get_interval(to);
+ vlc_mutex_unlock(&sys->lock);
+
+ dbus_timeout_handle(to);
+
+ vlc_mutex_lock(&sys->lock);
+ i = -1; /* lost track of state, restart from beginning */
}
}
+
static dbus_bool_t add_watch( DBusWatch *p_watch, void *p_data )
{
intf_thread_t *p_intf = (intf_thread_t*) p_data;
intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
- int i_fd = dbus_watch_get_unix_fd( p_watch );
-
- msg_Dbg( p_intf, "Adding dbus watch on fd %d", i_fd );
-
- if( dbus_watch_get_flags( p_watch ) & DBUS_WATCH_READABLE )
- msg_Dbg( p_intf, "Watching fd %d for readability", i_fd );
-
- if( dbus_watch_get_flags( p_watch ) & DBUS_WATCH_WRITABLE )
- msg_Dbg( p_intf, "Watching fd %d for writeability", i_fd );
vlc_mutex_lock( &p_sys->lock );
vlc_array_append( p_sys->p_watches, p_watch );
intf_thread_t *p_intf = (intf_thread_t*) p_data;
intf_sys_t *p_sys = (intf_sys_t*) p_intf->p_sys;
- msg_Dbg( p_intf, "Removing dbus watch on fd %d",
- dbus_watch_get_unix_fd( p_watch ) );
-
vlc_mutex_lock( &p_sys->lock );
vlc_array_remove( p_sys->p_watches,
{
intf_thread_t *p_intf = (intf_thread_t*) p_data;
- msg_Dbg( p_intf, "Toggling dbus watch on fd %d",
- dbus_watch_get_unix_fd( p_watch ) );
-
if( dbus_watch_get_enabled( p_watch ) )
- {
- msg_Dbg( p_intf,
- "Watch on fd %d has been enabled, "
- "the main loops needs to wake up",
- dbus_watch_get_unix_fd( p_watch ) );
-
wakeup_main_loop( p_intf );
- }
}
/**
return i_fds;
}
-/**
- * UpdateTimeouts() updates the remaining time for each timeout and
- * returns how much time is left until the next timeout.
- *
- * This function must be called with p_sys->lock locked
- *
- * @return int The time remaining until the next timeout, in milliseconds
- * or -1 if there are no timeouts
- *
- * @param intf_thread_t *p_intf This interface thread's state
- * @param mtime_t i_loop_interval The time which has elapsed since the last
- * call to this function
- */
-static int UpdateTimeouts( intf_thread_t *p_intf, mtime_t i_loop_interval )
-{
- intf_sys_t *p_sys = p_intf->p_sys;
- mtime_t i_next_timeout = LAST_MDATE;
- unsigned int i_timeouts = vlc_array_count( p_sys->p_timeouts );
-
- if( 0 == i_timeouts )
- return -1;
-
- for( unsigned int i = 0; i < i_timeouts; i++ )
- {
- timeout_info_t *p_info = NULL;
- DBusTimeout *p_timeout = NULL;
- mtime_t i_interval = 0;
-
- p_timeout = vlc_array_item_at_index( p_sys->p_timeouts, i );
- i_interval = dbus_timeout_get_interval( p_timeout ) * 1000; /* µs */
- p_info = (timeout_info_t*) dbus_timeout_get_data( p_timeout );
-
- p_info->i_remaining -= __MAX( 0, i_loop_interval ) % i_interval;
-
- if( !dbus_timeout_get_enabled( p_timeout ) )
- continue;
-
- /* The correct poll timeout value is the shortest one
- * in the dbus timeouts list */
- i_next_timeout = __MIN( i_next_timeout,
- __MAX( 0, p_info->i_remaining ) );
- }
-
- /* next timeout in milliseconds */
- return i_next_timeout / 1000;
-}
-
/**
* ProcessEvents() reacts to a list of events originating from other VLC threads
*
static void ProcessEvents( intf_thread_t *p_intf,
callback_info_t **p_events, int i_events )
{
- playlist_t *p_playlist = p_intf->p_sys->p_playlist;
- bool b_can_play = p_intf->p_sys->b_can_play;
+ bool b_can_play = p_intf->p_sys->b_can_play;
- vlc_dictionary_t player_properties;
- vlc_dictionary_init( &player_properties, 0 );
+ vlc_dictionary_t player_properties, tracklist_properties, root_properties;
+ vlc_dictionary_init( &player_properties, 0 );
+ vlc_dictionary_init( &tracklist_properties, 0 );
+ vlc_dictionary_init( &root_properties, 0 );
for( int i = 0; i < i_events; i++ )
{
{
case SIGNAL_ITEM_CURRENT:
TrackChange( p_intf );
+
+ // rate depends on current item
+ if( !vlc_dictionary_has_key( &player_properties, "Rate" ) )
+ vlc_dictionary_insert( &player_properties, "Rate", NULL );
+
vlc_dictionary_insert( &player_properties, "Metadata", NULL );
break;
case SIGNAL_INTF_CHANGE:
case SIGNAL_PLAYLIST_ITEM_APPEND:
case SIGNAL_PLAYLIST_ITEM_DELETED:
+ {
+ playlist_t *p_playlist = p_intf->p_sys->p_playlist;
PL_LOCK;
b_can_play = playlist_CurrentSize( p_playlist ) > 0;
PL_UNLOCK;
+
if( b_can_play != p_intf->p_sys->b_can_play )
{
p_intf->p_sys->b_can_play = b_can_play;
vlc_dictionary_insert( &player_properties, "CanPlay", NULL );
}
+
+ if( !vlc_dictionary_has_key( &tracklist_properties, "Tracks" ) )
+ vlc_dictionary_insert( &tracklist_properties, "Tracks", NULL );
break;
+ }
case SIGNAL_VOLUME_MUTED:
case SIGNAL_VOLUME_CHANGE:
vlc_dictionary_insert( &player_properties, "Volume", NULL );
case SIGNAL_RANDOM:
vlc_dictionary_insert( &player_properties, "Shuffle", NULL );
break;
+ case SIGNAL_FULLSCREEN:
+ vlc_dictionary_insert( &root_properties, "Fullscreen", NULL );
+ break;
case SIGNAL_REPEAT:
case SIGNAL_LOOP:
vlc_dictionary_insert( &player_properties, "LoopStatus", NULL );
break;
case SIGNAL_INPUT_METADATA:
{
- input_thread_t *p_input = playlist_CurrentInput( p_playlist );
+ input_thread_t *p_input = pl_CurrentInput( p_intf );
input_item_t *p_item;
if( p_input )
{
{
input_thread_t *p_input;
input_item_t *p_item;
- p_input = playlist_CurrentInput( p_intf->p_sys->p_playlist );
+ p_input = pl_CurrentInput( p_intf );
if( p_input )
{
p_item = input_GetItem( p_input );
break;
}
default:
- assert(0);
+ vlc_assert_unreachable();
}
free( p_events[i] );
}
if( vlc_dictionary_keys_count( &player_properties ) )
PlayerPropertiesChangedEmit( p_intf, &player_properties );
- vlc_dictionary_clear( &player_properties, NULL, NULL );
+ if( vlc_dictionary_keys_count( &tracklist_properties ) )
+ TrackListPropertiesChangedEmit( p_intf, &tracklist_properties );
+
+ if( vlc_dictionary_keys_count( &root_properties ) )
+ RootPropertiesChangedEmit( p_intf, &root_properties );
+
+ vlc_dictionary_clear( &player_properties, NULL, NULL );
+ vlc_dictionary_clear( &tracklist_properties, NULL, NULL );
+ vlc_dictionary_clear( &root_properties, NULL, NULL );
}
/**
DBusWatch **p_watches, int i_watches,
struct pollfd *p_fds, int i_fds )
{
+ VLC_UNUSED(p_intf);
+
/* Process watches */
for( int i = 0; i < i_watches; i++ )
{
int i_flags = 0;
int i_revents = p_fds[j].revents;
- int i_fd = p_fds[j].fd;
if( i_revents & POLLIN )
- {
- msg_Dbg( p_intf, "fd %d is ready for reading", i_fd );
i_flags |= DBUS_WATCH_READABLE;
- }
if( i_revents & POLLOUT )
- {
- msg_Dbg( p_intf, "fd %d is ready for writing", i_fd );
i_flags |= DBUS_WATCH_WRITABLE;
- }
if( i_revents & POLLERR )
- {
- msg_Dbg( p_intf, "error when polling fd %d", i_fd );
i_flags |= DBUS_WATCH_ERROR;
- }
if( i_revents & POLLHUP )
- {
- msg_Dbg( p_intf, "Hangup signal on fd %d", i_fd );
i_flags |= DBUS_WATCH_HANGUP;
- }
if( i_flags )
- {
- msg_Dbg( p_intf, "Handling dbus watch on fd %d", i_fd );
dbus_watch_handle( p_watch, i_flags );
- }
- else
- msg_Dbg( p_intf, "Nothing happened on fd %d", i_fd );
}
}
}
-/**
- * ProcessTimeouts() handles DBus timeouts
- *
- * This function must be called with p_sys->lock locked
- *
- * @param intf_thread_t *p_intf This interface thread state
- * @param DBusTimeout **p_timeouts List of timeouts to process
- * @param int i_timeouts Size of p_timeouts
- */
-static void ProcessTimeouts( intf_thread_t *p_intf,
- DBusTimeout **p_timeouts, int i_timeouts )
-{
- VLC_UNUSED( p_intf );
-
- for( int i = 0; i < i_timeouts; i++ )
- {
- timeout_info_t *p_info = NULL;
-
- p_info = (timeout_info_t*) dbus_timeout_get_data( p_timeouts[i] );
-
- if( !dbus_timeout_get_enabled( p_info->p_timeout ) )
- continue;
-
- if( p_info->i_remaining > 0 )
- continue;
-
- dbus_timeout_handle( p_info->p_timeout );
- p_info->i_remaining = dbus_timeout_get_interval( p_info->p_timeout );
- }
-}
-
/**
* DispatchDBusMessages() dispatches incoming dbus messages
* (indirectly invoking the callbacks), then it sends outgoing
status = dbus_connection_get_dispatch_status( p_sys->p_conn );
while( status != DBUS_DISPATCH_COMPLETE )
{
- msg_Dbg( p_intf, "Dispatching incoming dbus message" );
dbus_connection_dispatch( p_sys->p_conn );
status = dbus_connection_get_dispatch_status( p_sys->p_conn );
}
/* Send outgoing data */
if( dbus_connection_has_messages_to_send( p_sys->p_conn ) )
- {
- msg_Dbg( p_intf, "Sending outgoing data" );
dbus_connection_flush( p_sys->p_conn );
- }
}
/**
DBusError error;
- if( strcmp( psz_interface, DBUS_INTERFACE_PROPERTIES ) )
+ if( psz_interface && strcmp( psz_interface, DBUS_INTERFACE_PROPERTIES ) )
psz_target_interface = psz_interface;
else
}
}
- msg_Dbg( (vlc_object_t*) p_this, "Routing %s.%s D-Bus method call to %s",
- psz_interface, psz_method,
- psz_target_interface );
-
if( !strcmp( psz_target_interface, DBUS_INTERFACE_INTROSPECTABLE ) )
return handle_introspect( p_conn, p_from, p_this );
if( !strcmp( psz_target_interface, DBUS_MPRIS_PLAYER_INTERFACE ) )
return handle_player( p_conn, p_from, p_this );
+ if( !strcmp( psz_target_interface, DBUS_MPRIS_TRACKLIST_INTERFACE ) )
+ return handle_tracklist( p_conn, p_from, p_this );
+
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
* Run: main loop
*****************************************************************************/
-static void Run ( intf_thread_t *p_intf )
+static void *Run( void *data )
{
+ intf_thread_t *p_intf = data;
intf_sys_t *p_sys = p_intf->p_sys;
- mtime_t i_last_run = mdate();
+
+ int canc = vlc_savecancel();
for( ;; )
{
- int canc = vlc_savecancel();
vlc_mutex_lock( &p_sys->lock );
int i_watches = vlc_array_count( p_sys->p_watches );
- struct pollfd *p_fds = calloc( i_watches, sizeof( struct pollfd ) );
-
- int i_fds = GetPollFds( p_intf, p_fds );
-
- mtime_t i_now = mdate(), i_loop_interval = i_now - i_last_run;
-
- msg_Dbg( p_intf,
- "%lld µs elapsed since last wakeup",
- (long long) i_loop_interval );
+ struct pollfd fds[i_watches];
+ memset(fds, 0, sizeof fds);
- int i_next_timeout = UpdateTimeouts( p_intf, i_loop_interval );
- i_last_run = i_now;
+ int i_fds = GetPollFds( p_intf, fds );
+ int timeout = next_timeout(p_intf);
vlc_mutex_unlock( &p_sys->lock );
- if( -1 != i_next_timeout )
- msg_Dbg( p_intf, "next timeout is in %d ms", i_next_timeout );
- msg_Dbg( p_intf, "Sleeping until something happens" );
-
/* thread cancellation is allowed while the main loop sleeps */
vlc_restorecancel( canc );
- int i_pollres = poll( p_fds, i_fds, i_next_timeout );
- int i_errsv = errno;
+ while (poll(fds, i_fds, timeout) == -1)
+ {
+ if (errno != EINTR)
+ goto error;
+ }
canc = vlc_savecancel();
- msg_Dbg( p_intf, "the main loop has been woken up" );
-
- if( -1 == i_pollres )
- { /* XXX: What should we do when poll() fails ? */
- char buf[64];
- msg_Err( p_intf, "poll() failed: %s", strerror_r( i_errsv, buf, 64 ) );
- free( p_fds ); p_fds = NULL;
- vlc_restorecancel( canc );
- continue;
- }
-
/* Was the main loop woken up manually ? */
- if( 0 < i_pollres && ( p_fds[0].revents & POLLIN ) )
+ if (fds[0].revents & POLLIN)
{
char buf;
- msg_Dbg( p_intf, "Removing a byte from the self-pipe" );
- (void)read( p_fds[0].fd, &buf, 1 );
+ (void)read( fds[0].fd, &buf, 1 );
}
/* We need to lock the mutex while building lists of events,
*/
vlc_mutex_lock( &p_intf->p_sys->lock );
- /* Get the list of timeouts to process */
- unsigned int i_timeouts = vlc_array_count( p_sys->p_timeouts );
- DBusTimeout *p_timeouts[i_timeouts];
- for( unsigned int i = 0; i < i_timeouts; i++ )
- {
- p_timeouts[i] = vlc_array_item_at_index( p_sys->p_timeouts, i );
- }
+ process_timeouts(p_intf);
/* Get the list of watches to process */
i_watches = vlc_array_count( p_sys->p_watches );
- DBusWatch *p_watches[i_watches];
+ DBusWatch *p_watches[i_watches ? i_watches : 1];
for( int i = 0; i < i_watches; i++ )
{
p_watches[i] = vlc_array_item_at_index( p_sys->p_watches, i );
/* Get the list of events to process */
int i_events = vlc_array_count( p_intf->p_sys->p_events );
- callback_info_t* p_info[i_events];
+ callback_info_t* p_info[i_events ? i_events : 1];
for( int i = i_events - 1; i >= 0; i-- )
{
p_info[i] = vlc_array_item_at_index( p_intf->p_sys->p_events, i );
vlc_mutex_unlock( &p_intf->p_sys->lock );
ProcessEvents( p_intf, p_info, i_events );
- ProcessWatches( p_intf, p_watches, i_watches, p_fds, i_fds );
+ ProcessWatches( p_intf, p_watches, i_watches, fds, i_fds );
- free( p_fds ); p_fds = NULL;
-
- ProcessTimeouts( p_intf, p_timeouts, i_timeouts );
DispatchDBusMessages( p_intf );
-
- vlc_restorecancel( canc );
}
+error:
+ vlc_restorecancel(canc);
+ return NULL;
}
static void wakeup_main_loop( void *p_data )
{
intf_thread_t *p_intf = (intf_thread_t*) p_data;
- msg_Dbg( p_intf, "Sending wakeup signal to the main loop" );
-
if( !write( p_intf->p_sys->p_pipe_fds[PIPE_IN], "\0", 1 ) )
- {
- msg_Err( p_intf,
- "Could not wake up the main loop: %s", strerror( errno ) );
- }
+ msg_Err( p_intf, "Could not wake up the main loop: %s",
+ vlc_strerror_c(errno) );
}
-/* InputIntfEventCallback() fills a callback_info_t data structure in response
+/* Flls a callback_info_t data structure in response
* to an "intf-event" input event.
*
- * Caution: This function executes in the input thread
+ * @warning This function executes in the input thread.
*
- * This function must be called with p_sys->lock locked
- *
- * @return int VLC_SUCCESS on success, VLC_E* on error
- * @param intf_thread_t *p_intf the interface thread
- * @param input_thread_t *p_input This input thread
- * @param const int i_event input event type
- * @param callback_info_t *p_info Location of the callback info to fill
+ * @return VLC_SUCCESS on success, VLC_E* on error.
*/
-static int InputIntfEventCallback( intf_thread_t *p_intf,
- input_thread_t *p_input,
- const int i_event,
- callback_info_t *p_info )
+static int InputCallback( vlc_object_t *p_this, const char *psz_var,
+ vlc_value_t oldval, vlc_value_t newval, void *data )
{
+ input_thread_t *p_input = (input_thread_t *)p_this;
+ intf_thread_t *p_intf = data;
+ intf_sys_t *p_sys = p_intf->p_sys;
+
dbus_int32_t i_state = PLAYBACK_STATE_INVALID;
- assert(!p_info->signal);
- mtime_t i_now = mdate(), i_pos, i_projected_pos, i_interval;
- float f_current_rate;
- switch( i_event )
+ callback_info_t *p_info = calloc( 1, sizeof( callback_info_t ) );
+ if( unlikely(p_info == NULL) )
+ return VLC_ENOMEM;
+
+ switch( newval.i_int )
{
case INPUT_EVENT_DEAD:
- case INPUT_EVENT_ABORT:
i_state = PLAYBACK_STATE_STOPPED;
break;
case INPUT_EVENT_STATE:
break;
case INPUT_EVENT_ITEM_META:
p_info->signal = SIGNAL_INPUT_METADATA;
- return VLC_SUCCESS;
+ break;
case INPUT_EVENT_RATE:
p_info->signal = SIGNAL_RATE;
- return VLC_SUCCESS;
+ break;
case INPUT_EVENT_POSITION:
+ {
+ mtime_t i_now = mdate(), i_pos, i_projected_pos, i_interval;
+ float f_current_rate;
+
/* Detect seeks
* XXX: This is way more convoluted than it should be... */
+ i_pos = var_GetTime( p_input, "time" );
+
if( !p_intf->p_sys->i_last_input_pos_event ||
!( var_GetInteger( p_input, "state" ) == PLAYING_S ) )
+ {
+ p_intf->p_sys->i_last_input_pos_event = i_now;
+ p_intf->p_sys->i_last_input_pos = i_pos;
break;
- i_pos = var_GetTime( p_input, "time" );
+ }
+
f_current_rate = var_GetFloat( p_input, "rate" );
i_interval = ( i_now - p_intf->p_sys->i_last_input_pos_event );
- i_projected_pos = p_intf->p_sys->i_last_input_pos + ( i_interval * f_current_rate );
+
+ i_projected_pos = p_intf->p_sys->i_last_input_pos +
+ ( i_interval * f_current_rate );
+
p_intf->p_sys->i_last_input_pos_event = i_now;
p_intf->p_sys->i_last_input_pos = i_pos;
- if( ABS( i_pos - i_projected_pos ) < SEEK_THRESHOLD )
+
+ if( llabs( i_pos - i_projected_pos ) < SEEK_THRESHOLD )
break;
+
p_info->signal = SIGNAL_SEEK;
p_info->i_item = input_GetItem( p_input )->i_id;
break;
+ }
default:
- return VLC_EGENERIC;
+ free( p_info );
+ return VLC_SUCCESS; /* don't care */
}
+ vlc_mutex_lock( &p_sys->lock );
if( i_state != PLAYBACK_STATE_INVALID &&
- i_state != p_intf->p_sys->i_playing_state )
+ i_state != p_sys->i_playing_state )
{
- p_intf->p_sys->i_playing_state = i_state;
+ p_sys->i_playing_state = i_state;
p_info->signal = SIGNAL_STATE;
}
+ if( p_info->signal )
+ vlc_array_append( p_intf->p_sys->p_events, p_info );
+ else
+ free( p_info );
+ vlc_mutex_unlock( &p_intf->p_sys->lock );
- return p_info->signal ? VLC_SUCCESS : VLC_EGENERIC;
+ wakeup_main_loop( p_intf );
+
+ (void)psz_var;
+ (void)oldval;
+ return VLC_SUCCESS;
}
// Get all the callbacks
static int AllCallback( vlc_object_t *p_this, const char *psz_var,
vlc_value_t oldval, vlc_value_t newval, void *p_data )
{
- (void)p_this;
- (void)oldval;
-
- intf_thread_t *p_intf = (intf_thread_t*)p_data;
- callback_info_t *info = calloc( 1, sizeof( callback_info_t ) );
-
- if( !info )
- return VLC_ENOMEM;
-
- vlc_mutex_lock( &p_intf->p_sys->lock );
+ intf_thread_t *p_intf = p_data;
+ callback_info_t info = { .signal = SIGNAL_NONE };
// Wich event is it ?
- if( !strcmp( "item-current", psz_var ) )
- info->signal = SIGNAL_ITEM_CURRENT;
-
- else if( !strcmp( "volume-change", psz_var ) )
- info->signal = SIGNAL_VOLUME_CHANGE;
-
- else if( !strcmp( "volume-muted", psz_var ) )
- info->signal = SIGNAL_VOLUME_MUTED;
-
+ if( !strcmp( "input-current", psz_var ) )
+ info.signal = SIGNAL_ITEM_CURRENT;
+ else if( !strcmp( "volume", psz_var ) )
+ {
+ if( oldval.f_float != newval.f_float )
+ info.signal = SIGNAL_VOLUME_CHANGE;
+ }
+ else if( !strcmp( "mute", psz_var ) )
+ {
+ if( oldval.b_bool != newval.b_bool )
+ info.signal = SIGNAL_VOLUME_MUTED;
+ }
else if( !strcmp( "intf-change", psz_var ) )
- info->signal = SIGNAL_INTF_CHANGE;
-
+ info.signal = SIGNAL_INTF_CHANGE;
else if( !strcmp( "playlist-item-append", psz_var ) )
{
- info->signal = SIGNAL_PLAYLIST_ITEM_APPEND;
- info->i_node = ((playlist_add_t*)newval.p_address)->i_node;
+ info.signal = SIGNAL_PLAYLIST_ITEM_APPEND;
+ info.i_node = ((playlist_add_t*)newval.p_address)->i_node;
}
-
else if( !strcmp( "playlist-item-deleted", psz_var ) )
- info->signal = SIGNAL_PLAYLIST_ITEM_DELETED;
-
+ info.signal = SIGNAL_PLAYLIST_ITEM_DELETED;
else if( !strcmp( "random", psz_var ) )
- info->signal = SIGNAL_RANDOM;
-
+ info.signal = SIGNAL_RANDOM;
+ else if( !strcmp( "fullscreen", psz_var ) )
+ info.signal = SIGNAL_FULLSCREEN;
else if( !strcmp( "repeat", psz_var ) )
- info->signal = SIGNAL_REPEAT;
-
+ info.signal = SIGNAL_REPEAT;
else if( !strcmp( "loop", psz_var ) )
- info->signal = SIGNAL_LOOP;
-
- else if( !strcmp( "intf-event", psz_var ) )
- {
- int i_res = InputIntfEventCallback( p_intf,
- (input_thread_t*) p_this,
- newval.i_int, info );
- if( VLC_SUCCESS != i_res )
- {
- vlc_mutex_unlock( &p_intf->p_sys->lock );
- free( info );
-
- return i_res;
- }
- }
-
+ info.signal = SIGNAL_LOOP;
else if( !strcmp( "can-seek", psz_var ) )
- info->signal = SIGNAL_CAN_SEEK;
-
+ info.signal = SIGNAL_CAN_SEEK;
else if( !strcmp( "can-pause", psz_var ) )
- info->signal = SIGNAL_CAN_PAUSE;
-
+ info.signal = SIGNAL_CAN_PAUSE;
else
- assert(0);
+ vlc_assert_unreachable();
+
+ if( info.signal == SIGNAL_NONE )
+ return VLC_SUCCESS;
+
+ callback_info_t *p_info = malloc( sizeof( *p_info ) );
+ if( unlikely(p_info == NULL) )
+ return VLC_ENOMEM;
// Append the event
- vlc_array_append( p_intf->p_sys->p_events, info );
+ *p_info = info;
+ vlc_mutex_lock( &p_intf->p_sys->lock );
+ vlc_array_append( p_intf->p_sys->p_events, p_info );
vlc_mutex_unlock( &p_intf->p_sys->lock );
- msg_Dbg( p_intf,
- "Got a VLC event on %s. The main loop needs to wake up "
- "in order to process it", psz_var );
-
wakeup_main_loop( p_intf );
-
+ (void) p_this;
return VLC_SUCCESS;
}
/*****************************************************************************
- * TrackChange: callback on playlist "item-current"
+ * TrackChange: callback on playlist "input-current"
*****************************************************************************/
static int TrackChange( intf_thread_t *p_intf )
{
intf_sys_t *p_sys = p_intf->p_sys;
- playlist_t *p_playlist = p_sys->p_playlist;
input_thread_t *p_input = NULL;
input_item_t *p_item = NULL;
if( p_sys->p_input )
{
- var_DelCallback( p_sys->p_input, "intf-event", AllCallback, p_intf );
+ var_DelCallback( p_sys->p_input, "intf-event", InputCallback, p_intf );
var_DelCallback( p_sys->p_input, "can-pause", AllCallback, p_intf );
var_DelCallback( p_sys->p_input, "can-seek", AllCallback, p_intf );
vlc_object_release( p_sys->p_input );
p_sys->b_meta_read = false;
- p_input = playlist_CurrentInput( p_playlist );
+ p_input = pl_CurrentInput( p_intf );
if( !p_input )
{
return VLC_SUCCESS;
p_sys->b_meta_read = true;
p_sys->p_input = p_input;
- var_AddCallback( p_input, "intf-event", AllCallback, p_intf );
+ var_AddCallback( p_input, "intf-event", InputCallback, p_intf );
var_AddCallback( p_input, "can-pause", AllCallback, p_intf );
var_AddCallback( p_input, "can-seek", AllCallback, p_intf );
free( psz ); \
}
+#define ADD_META_SINGLETON_STRING_LIST( entry, item ) \
+ { \
+ char * psz = input_item_Get##item( p_input );\
+ if( psz ) { \
+ dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
+ NULL, &dict_entry ); \
+ dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
+ &ppsz_meta_items[entry] ); \
+ dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
+ "as", &variant ); \
+ dbus_message_iter_open_container( &variant, DBUS_TYPE_ARRAY, "s", \
+ &list ); \
+ dbus_message_iter_append_basic( &list, \
+ DBUS_TYPE_STRING, \
+ &psz ); \
+ dbus_message_iter_close_container( &variant, &list ); \
+ dbus_message_iter_close_container( &dict_entry, &variant ); \
+ dbus_message_iter_close_container( &dict, &dict_entry ); \
+ } \
+ free( psz ); \
+ }
+
int GetInputMeta( input_item_t* p_input,
DBusMessageIter *args )
{
- DBusMessageIter dict, dict_entry, variant;
+ DBusMessageIter dict, dict_entry, variant, list;
/** The duration of the track can be expressed in second, milli-seconds and
µ-seconds */
dbus_int64_t i_mtime = input_item_GetDuration( p_input );
ADD_META( 0, DBUS_TYPE_OBJECT_PATH, psz_trackid );
ADD_VLC_META_STRING( 1, URI );
ADD_VLC_META_STRING( 2, Title );
- ADD_VLC_META_STRING( 3, Artist );
+ ADD_META_SINGLETON_STRING_LIST( 3, Artist );
ADD_VLC_META_STRING( 4, Album );
ADD_VLC_META_STRING( 5, TrackNum );
ADD_META( 6, DBUS_TYPE_UINT32, i_time );
ADD_META( 7, DBUS_TYPE_INT64, i_mtime );
- ADD_VLC_META_STRING( 8, Genre );
- ADD_VLC_META_STRING( 9, Rating );
- ADD_VLC_META_STRING( 10, Date );
+ ADD_META_SINGLETON_STRING_LIST( 8, Genre );
+ //ADD_META( 9, DBUS_TYPE_DOUBLE, rating );
+ ADD_VLC_META_STRING( 10, Date ); // this is supposed to be in ISO 8601 extended format
ADD_VLC_META_STRING( 11, ArtURL );
ADD_VLC_META_STRING( 12, TrackID );
ADD_VLC_META_STRING( 17, Copyright );
- ADD_VLC_META_STRING( 18, Description );
+ ADD_META_SINGLETON_STRING_LIST( 18, Description );
ADD_VLC_META_STRING( 19, EncodedBy );
ADD_VLC_META_STRING( 20, Language );
ADD_META( 21, DBUS_TYPE_INT64, i_length );
ADD_VLC_META_STRING( 24, Setting );
ADD_VLC_META_STRING( 25, URL );
+ free( psz_trackid );
+
vlc_mutex_lock( &p_input->lock );
if( p_input->p_meta )
{
return VLC_SUCCESS;
}
+int AddProperty( intf_thread_t *p_intf,
+ DBusMessageIter *p_container,
+ const char* psz_property_name,
+ const char* psz_signature,
+ int (*pf_marshaller) (intf_thread_t*, DBusMessageIter*) )
+{
+ DBusMessageIter entry, v;
+
+ if( !dbus_message_iter_open_container( p_container,
+ DBUS_TYPE_DICT_ENTRY, NULL,
+ &entry ) )
+ return VLC_ENOMEM;
+
+ if( !dbus_message_iter_append_basic( &entry,
+ DBUS_TYPE_STRING,
+ &psz_property_name ) )
+ return VLC_ENOMEM;
+
+ if( !dbus_message_iter_open_container( &entry,
+ DBUS_TYPE_VARIANT, psz_signature,
+ &v ) )
+ return VLC_ENOMEM;
+
+ if( VLC_SUCCESS != pf_marshaller( p_intf, &v ) )
+ return VLC_ENOMEM;
+
+ if( !dbus_message_iter_close_container( &entry, &v) )
+ return VLC_ENOMEM;
+
+ if( !dbus_message_iter_close_container( p_container, &entry ) )
+ return VLC_ENOMEM;
+
+ return VLC_SUCCESS;
+}
+
#undef ADD_META
#undef ADD_VLC_META_STRING