dbus: Implement the MPRIS v2.1, as published on http://www.mpris.org
[vlc.git] / modules / control / dbus / dbus_player.c
index 359ccfe..dea7851 100644 (file)
 
 static int MarshalStatus ( intf_thread_t *, DBusMessageIter * );
 
-/* XML data to answer org.freedesktop.DBus.Introspectable.Introspect requests */
-static const char* psz_player_introspection_xml =
-"<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
-"\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
-"<node>"
-"  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
-"    <method name=\"Introspect\">\n"
-"      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
-"    </method>\n"
-"  </interface>\n"
-"  <interface name=\"org.freedesktop.MediaPlayer\">\n"
-"    <method name=\"GetStatus\">\n"
-"      <arg type=\"(iiii)\" direction=\"out\" />\n"
-"    </method>\n"
-"    <method name=\"Prev\">\n"
-"    </method>\n"
-"    <method name=\"Next\">\n"
-"    </method>\n"
-"    <method name=\"Stop\">\n"
-"    </method>\n"
-"    <method name=\"Play\">\n"
-"    </method>\n"
-"    <method name=\"Pause\">\n"
-"    </method>\n"
-"    <method name=\"Repeat\">\n"
-"      <arg type=\"b\" direction=\"in\" />\n"
-"    </method>\n"
-"    <method name=\"VolumeSet\">\n"
-"      <arg type=\"i\" direction=\"in\" />\n"
-"    </method>\n"
-"    <method name=\"VolumeGet\">\n"
-"      <arg type=\"i\" direction=\"out\" />\n"
-"    </method>\n"
-"    <method name=\"PositionSet\">\n"
-"      <arg type=\"i\" direction=\"in\" />\n"
-"    </method>\n"
-"    <method name=\"PositionGet\">\n"
-"      <arg type=\"i\" direction=\"out\" />\n"
-"    </method>\n"
-"    <method name=\"GetMetadata\">\n"
-"      <arg type=\"a{sv}\" direction=\"out\" />\n"
-"    </method>\n"
-"    <method name=\"GetCaps\">\n"
-"      <arg type=\"i\" direction=\"out\" />\n"
-"    </method>\n"
-"    <signal name=\"TrackChange\">\n"
-"      <arg type=\"a{sv}\"/>\n"
-"    </signal>\n"
-"    <signal name=\"StatusChange\">\n"
-"      <arg type=\"(iiii)\"/>\n"
-"    </signal>\n"
-"    <signal name=\"CapsChange\">\n"
-"      <arg type=\"i\"/>\n"
-"    </signal>\n"
-"  </interface>\n"
-"</node>\n"
-;
-
-DBUS_METHOD( PositionGet )
-{ /* returns position in milliseconds */
+DBUS_METHOD( Position )
+{ /* returns position in microseconds */
     REPLY_INIT;
     OUT_ARGUMENTS;
+    DBusMessageIter v;
     dbus_int32_t i_pos;
 
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "i", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
     input_thread_t *p_input = playlist_CurrentInput( PL );
 
     if( !p_input )
         i_pos = 0;
+
     else
     {
-        i_pos = var_GetTime( p_input, "time" ) / 1000;
+        i_pos = var_GetTime( p_input, "time" );
         vlc_object_release( p_input );
     }
-    ADD_INT32( &i_pos );
+
+    if( !dbus_message_iter_append_basic( &v, DBUS_TYPE_INT32, &i_pos ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
     REPLY_SEND;
 }
 
-DBUS_METHOD( PositionSet )
-{ /* set position in milliseconds */
+DBUS_METHOD( SetPosition )
+{ /* set position in microseconds */
 
     REPLY_INIT;
-    vlc_value_t position;
     dbus_int32_t i_pos;
+    vlc_value_t position;
+    char *psz_trackid, *psz_dbus_trackid;
+    input_item_t *p_item;
 
     DBusError error;
     dbus_error_init( &error );
 
     dbus_message_get_args( p_from, &error,
-            DBUS_TYPE_INT32, &i_pos,
+            DBUS_TYPE_OBJECT_PATH, &psz_dbus_trackid,
+            DBUS_TYPE_INT64, &i_pos,
             DBUS_TYPE_INVALID );
 
     if( dbus_error_is_set( &error ) )
@@ -138,43 +94,47 @@ DBUS_METHOD( PositionSet )
         dbus_error_free( &error );
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
+
     input_thread_t *p_input = playlist_CurrentInput( PL );
 
     if( p_input )
     {
-        position.i_time = ((mtime_t)i_pos) * 1000;
-        var_Set( p_input, "time", position );
+        if( ( p_item = input_GetItem( p_input ) ) )
+        {
+            if( -1 == asprintf( &psz_trackid,
+                                MPRIS_TRACKID_FORMAT,
+                                p_item->i_id ) )
+            {
+                vlc_object_release( p_input );
+                return DBUS_HANDLER_RESULT_NEED_MEMORY;
+            }
+
+            if( !strcmp( psz_trackid, psz_dbus_trackid ) )
+            {
+                position.i_time = (mtime_t) i_pos;
+                var_Set( p_input, "time", position );
+            }
+        }
+
         vlc_object_release( p_input );
     }
-    REPLY_SEND;
-}
 
-DBUS_METHOD( VolumeGet )
-{ /* returns volume in percentage */
-    REPLY_INIT;
-    OUT_ARGUMENTS;
-    dbus_int32_t i_dbus_vol;
 
-    audio_volume_t i_vol = aout_VolumeGet( PL );
-    double f_vol = 100. * i_vol / AOUT_VOLUME_MAX;
-
-    i_dbus_vol = round( f_vol );
-    ADD_INT32( &i_dbus_vol );
     REPLY_SEND;
 }
 
-DBUS_METHOD( VolumeSet )
-{ /* set volume in percentage */
+DBUS_METHOD( Seek )
+{
     REPLY_INIT;
+    dbus_int32_t i_step;
+    vlc_value_t  newpos;
+    mtime_t      i_pos;
 
     DBusError error;
     dbus_error_init( &error );
 
-    dbus_int32_t i_dbus_vol;
-    audio_volume_t i_vol;
-
     dbus_message_get_args( p_from, &error,
-            DBUS_TYPE_INT32, &i_dbus_vol,
+            DBUS_TYPE_INT32, &i_step,
             DBUS_TYPE_INVALID );
 
     if( dbus_error_is_set( &error ) )
@@ -185,9 +145,70 @@ DBUS_METHOD( VolumeSet )
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
 
-    double f_vol = AOUT_VOLUME_MAX * i_dbus_vol / 100.;
-    i_vol = round( f_vol );
+    input_thread_t *p_input = playlist_CurrentInput( PL );
+    if( p_input && var_GetBool( p_input, "can-seek" ) )
+    {
+        i_pos = var_GetTime( p_input, "time" );
+        newpos.i_time = (mtime_t) i_step + i_pos;
+
+        if( newpos.i_time < 0 )
+            newpos.i_time = 0;
+
+        var_Set( p_input, "time", newpos );
+    }
+
+    if( p_input )
+        vlc_object_release( p_input );
+
+    REPLY_SEND;
+}
+
+static void
+MarshalVolume( intf_thread_t *p_intf, DBusMessageIter *container )
+{
+    audio_volume_t i_vol = aout_VolumeGet( p_intf->p_sys->p_playlist );
+
+    /* A volume of 1.0 represents a sensible maximum, ie: 0dB */
+    double d_vol = (double) i_vol / ( AOUT_VOLUME_MAX >> 2 );
+
+    dbus_message_iter_append_basic( container, DBUS_TYPE_DOUBLE, &d_vol );
+}
+
+DBUS_METHOD( VolumeGet )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "d", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    MarshalVolume( p_this, &v );
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    REPLY_SEND;
+}
+
+DBUS_METHOD( VolumeSet )
+{
+    REPLY_INIT;
+    double d_dbus_vol;
+
+    if( VLC_SUCCESS != DemarshalSetPropertyValue( p_from, &d_dbus_vol ) )
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if( d_dbus_vol > 1. )
+        d_dbus_vol = 1.;
+    else if( d_dbus_vol < 0. )
+        d_dbus_vol = 0.;
+
+    double d_vol = d_dbus_vol * AOUT_VOLUME_MAX;
+    audio_volume_t i_vol = round( d_vol );
     aout_VolumeSet( PL, i_vol );
+
     REPLY_SEND;
 }
 
@@ -212,18 +233,16 @@ DBUS_METHOD( Stop )
     REPLY_SEND;
 }
 
-DBUS_METHOD( GetStatus )
-{ /* returns the current status as a struct of 4 ints */
-/*
-    First   0 = Playing, 1 = Paused, 2 = Stopped.
-    Second  0 = Playing linearly , 1 = Playing randomly.
-    Third   0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
-    Fourth  0 = Stop playing once the last element has been played, 1 = Never give up playing *
- */
+DBUS_METHOD( Play )
+{
     REPLY_INIT;
-    OUT_ARGUMENTS;
+    input_thread_t *p_input =  playlist_CurrentInput( PL );
 
-    MarshalStatus( p_this, &args );
+    if( !p_input || var_GetInteger( p_input, "state" ) != PLAYING_S )
+        playlist_Play( PL );
+
+    if( p_input )
+        vlc_object_release( p_input );
 
     REPLY_SEND;
 }
@@ -231,39 +250,43 @@ DBUS_METHOD( GetStatus )
 DBUS_METHOD( Pause )
 {
     REPLY_INIT;
-    playlist_Pause( PL );
+    input_thread_t *p_input = playlist_CurrentInput( PL );
+
+    if( p_input && var_GetInteger(p_input, "state") == PLAYING_S )
+        playlist_Pause( PL );
+
+    if( p_input )
+        vlc_object_release( p_input );
+
     REPLY_SEND;
 }
 
-DBUS_METHOD( Play )
+DBUS_METHOD( PlayPause )
 {
     REPLY_INIT;
+    input_thread_t *p_input = playlist_CurrentInput( PL );
 
-    input_thread_t *p_input =  playlist_CurrentInput( PL );
+    if( p_input && var_GetInteger(p_input, "state") == PLAYING_S )
+        playlist_Pause( PL );
+    else
+        playlist_Play( PL );
 
     if( p_input )
-    {
-        double i_pos = 0;
-        input_Control( p_input, INPUT_SET_POSITION, i_pos );
         vlc_object_release( p_input );
-    }
-    else
-        playlist_Play( PL );
 
     REPLY_SEND;
 }
 
-DBUS_METHOD( Repeat )
+DBUS_METHOD( OpenUri )
 {
     REPLY_INIT;
-    OUT_ARGUMENTS;
 
+    char *psz_mrl;
     DBusError error;
-    dbus_bool_t b_repeat;
-
     dbus_error_init( &error );
+
     dbus_message_get_args( p_from, &error,
-            DBUS_TYPE_BOOLEAN, &b_repeat,
+            DBUS_TYPE_STRING, &psz_mrl,
             DBUS_TYPE_INVALID );
 
     if( dbus_error_is_set( &error ) )
@@ -274,96 +297,510 @@ DBUS_METHOD( Repeat )
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
     }
 
-    var_SetBool( PL, "repeat", ( b_repeat == TRUE ) );
+    playlist_Add( PL, psz_mrl, NULL,
+                  PLAYLIST_APPEND | PLAYLIST_GO,
+                  PLAYLIST_END, true, false );
 
     REPLY_SEND;
 }
 
-DBUS_METHOD( GetCurrentMetadata )
+static void
+MarshalCanPlay( intf_thread_t *p_intf, DBusMessageIter *container )
 {
-    REPLY_INIT;
-    OUT_ARGUMENTS;
-    playlist_t *p_playlist = PL;
+    playlist_t *p_playlist = p_intf->p_sys->p_playlist;
 
     PL_LOCK;
-    playlist_item_t* p_item =  playlist_CurrentPlayingItem( p_playlist );
-    if( p_item )
-        GetInputMeta( p_item->p_input, &args );
+    dbus_bool_t b_can_play = playlist_CurrentSize( p_playlist );
     PL_UNLOCK;
+
+    dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_can_play );
+}
+
+DBUS_METHOD( CanPlay )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "b", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    MarshalCanPlay( p_this, &v );
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    REPLY_SEND;
+}
+
+static void
+MarshalCanPause( intf_thread_t *p_intf, DBusMessageIter *container )
+{
+    dbus_bool_t b_can_pause = FALSE;
+    input_thread_t *p_input;
+    p_input = playlist_CurrentInput( p_intf->p_sys->p_playlist );
+
+    if( p_input )
+    {
+        b_can_pause = var_GetBool( p_input, "can-pause" );
+        vlc_object_release( p_input );
+    }
+
+    dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN,
+                                    &b_can_pause );
+}
+
+DBUS_METHOD( CanPause )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "b", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    MarshalCanPause( p_this, &v );
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
     REPLY_SEND;
 }
 
-DBUS_METHOD( GetCaps )
+DBUS_METHOD( CanControl )
 {
     REPLY_INIT;
     OUT_ARGUMENTS;
 
-    ADD_INT32( &INTF->p_sys->i_caps );
+    DBusMessageIter v;
+    dbus_bool_t b_can_control = TRUE;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT,
+                                           "b", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    if( !dbus_message_iter_append_basic( &v, DBUS_TYPE_BOOLEAN,
+                                         &b_can_control ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
 
     REPLY_SEND;
 }
 
-/*****************************************************************************
- * StatusChange: Player status change signal
- *****************************************************************************/
+static void
+MarshalCanSeek( intf_thread_t *p_intf, DBusMessageIter *container )
+{
+    dbus_bool_t b_can_seek = FALSE;
+    input_thread_t *p_input;
+    p_input = playlist_CurrentInput( p_intf->p_sys->p_playlist );
 
-DBUS_SIGNAL( StatusChangeSignal )
-{ /* send the updated status info on the bus */
-    SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
-                 DBUS_MPRIS_PLAYER_PATH,
-                 "StatusChange" );
+    if( p_input )
+    {
+        b_can_seek = var_GetBool( p_input, "can-seek" );
+        vlc_object_release( p_input );
+    }
+
+    dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_can_seek );
+}
+
+DBUS_METHOD( CanSeek )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "b", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    MarshalCanSeek( p_this, &v );
+
+    REPLY_SEND;
+}
+
+static void
+MarshalShuffle( intf_thread_t *p_intf, DBusMessageIter *container )
+{
+    dbus_bool_t b_shuffle = var_GetBool( p_intf->p_sys->p_playlist, "random" );
+    dbus_message_iter_append_basic( container, DBUS_TYPE_BOOLEAN, &b_shuffle );
+}
 
+DBUS_METHOD( ShuffleGet )
+{
+    REPLY_INIT;
     OUT_ARGUMENTS;
 
-    /* we're called from a callback of input_thread_t, so it can not be
-     * destroyed before we return */
-    MarshalStatus( (intf_thread_t*) p_data, &args );
+    DBusMessageIter v;
 
-    SIGNAL_SEND;
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "b", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    MarshalShuffle( p_this, &v );
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    REPLY_SEND;
 }
 
-/*****************************************************************************
- * TrackChange: Playlist item change callback
- *****************************************************************************/
+DBUS_METHOD( ShuffleSet )
+{
+    REPLY_INIT;
+    dbus_bool_t b_shuffle;
 
-DBUS_SIGNAL( TrackChangeSignal )
-{ /* emit the metadata of the new item */
-    SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
-                 DBUS_MPRIS_PLAYER_PATH,
-                 "TrackChange" );
+    if( VLC_SUCCESS != DemarshalSetPropertyValue( p_from, &b_shuffle ) )
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    var_SetBool( PL, "random", ( b_shuffle == TRUE ) );
+
+    REPLY_SEND;
+}
+
+static void
+MarshalPlaybackStatus( intf_thread_t *p_intf, DBusMessageIter *container )
+{
+    input_thread_t *p_input;
+    char *psz_playback_status;
+    if( ( p_input = playlist_CurrentInput( p_intf->p_sys->p_playlist ) ) )
+    {
+        switch( var_GetInteger( p_input, "state" ) )
+        {
+            case OPENING_S:
+            case PLAYING_S:
+                psz_playback_status = PLAYBACK_STATUS_PLAYING;
+                break;
+            case PAUSE_S:
+                psz_playback_status = PLAYBACK_STATUS_PAUSED;
+                break;
+            default:
+                psz_playback_status = PLAYBACK_STATUS_STOPPED;
+        }
+
+        vlc_object_release( (vlc_object_t*) p_input );
+    }
+    else
+        psz_playback_status = PLAYBACK_STATUS_STOPPED;
+
+    dbus_message_iter_append_basic( container, DBUS_TYPE_STRING,
+                                    &psz_playback_status );
+}
 
+DBUS_METHOD( PlaybackStatus )
+{
+    REPLY_INIT;
     OUT_ARGUMENTS;
 
-    input_item_t *p_item = (input_item_t*) p_data;
-    GetInputMeta ( p_item, &args );
+    DBusMessageIter v;
 
-    SIGNAL_SEND;
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "s", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    MarshalPlaybackStatus( p_this, &v );
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    REPLY_SEND;
+}
+
+static void
+MarshalRate( intf_thread_t *p_intf, DBusMessageIter *container )
+{
+    double d_rate;
+    input_thread_t *p_input;
+    if( ( p_input = playlist_CurrentInput( p_intf->p_sys->p_playlist ) ) )
+    {
+        d_rate = var_GetFloat( p_input, "rate" );
+        vlc_object_release( (vlc_object_t*) p_input );
+    }
+    else
+        d_rate = 0.;
+
+    dbus_message_iter_append_basic( container, DBUS_TYPE_DOUBLE, &d_rate );
+}
+
+DBUS_METHOD( RateGet )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "d", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    MarshalRate( p_this, &v );
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    REPLY_SEND;
+}
+
+DBUS_METHOD( RateSet )
+{
+    REPLY_INIT;
+
+    double d_rate;
+
+    if( VLC_SUCCESS != DemarshalSetPropertyValue( p_from, &d_rate ) )
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    input_thread_t *p_input;
+    if( ( p_input = playlist_CurrentInput( PL ) ) )
+    {
+        var_SetFloat( p_input, "rate", (float) d_rate );
+        vlc_object_release( (vlc_object_t*) p_input );
+    }
+    else
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    REPLY_SEND;
+}
+
+DBUS_METHOD( MinimumRate )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v;
+    double d_min_rate = (double) INPUT_RATE_MIN / INPUT_RATE_DEFAULT;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "d", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    if( !dbus_message_iter_append_basic( &v, DBUS_TYPE_DOUBLE, &d_min_rate ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    REPLY_SEND;
+}
+
+DBUS_METHOD( MaximumRate )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v;
+    double d_max_rate = (double) INPUT_RATE_MAX / INPUT_RATE_DEFAULT;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "d", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    if( !dbus_message_iter_append_basic( &v, DBUS_TYPE_DOUBLE, &d_max_rate ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    REPLY_SEND;
+}
+
+static void
+MarshalLoopStatus( intf_thread_t *p_intf, DBusMessageIter *container )
+{
+    char *psz_loop_status;
+    if( var_GetBool( p_intf->p_sys->p_playlist, "repeat" ) )
+        psz_loop_status = LOOP_STATUS_TRACK;
+    else if( var_GetBool( p_intf->p_sys->p_playlist, "loop" ) )
+        psz_loop_status = LOOP_STATUS_PLAYLIST;
+    else
+        psz_loop_status = LOOP_STATUS_NONE;
+
+    dbus_message_iter_append_basic( container, DBUS_TYPE_STRING,
+                                    &psz_loop_status );
+}
+
+DBUS_METHOD( LoopStatusGet )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT, "s", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    MarshalLoopStatus( p_this, &v );
+
+    if( !dbus_message_iter_close_container( &args, &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    REPLY_SEND;
+}
+
+DBUS_METHOD( LoopStatusSet )
+{
+    REPLY_INIT;
+    char *psz_loop_status;
+
+    if( VLC_SUCCESS != DemarshalSetPropertyValue( p_from, &psz_loop_status ) )
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if( !strcmp( psz_loop_status, LOOP_STATUS_NONE ) )
+    {
+        var_SetBool( PL, "loop",   FALSE );
+        var_SetBool( PL, "repeat", FALSE );
+    }
+    else if( !strcmp( psz_loop_status, LOOP_STATUS_TRACK ) )
+    {
+        var_SetBool( PL, "loop",   FALSE );
+        var_SetBool( PL, "repeat", TRUE  );
+    }
+    else if( !strcmp( psz_loop_status, LOOP_STATUS_PLAYLIST ) )
+    {
+        var_SetBool( PL, "loop",   TRUE );
+        var_SetBool( PL, "repeat", FALSE  );
+    }
+    else
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    REPLY_SEND;
+}
+
+DBUS_METHOD( Metadata )
+{
+    REPLY_INIT;
+    OUT_ARGUMENTS;
+
+    DBusMessageIter v, a;
+    playlist_t *p_playlist = PL;
+
+    if( !dbus_message_iter_open_container( &args, DBUS_TYPE_VARIANT,
+                                           "a{sv}", &v ) )
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+
+    PL_LOCK;
+    playlist_item_t* p_item =  playlist_CurrentPlayingItem( p_playlist );
+
+    if( p_item )
+        GetInputMeta( p_item->p_input, &v );
+
+    PL_UNLOCK;
+
+    if( ( !p_item &&
+        ( !dbus_message_iter_open_container( &v, DBUS_TYPE_ARRAY, "{sv}", &a ) ||
+          !dbus_message_iter_close_container( &v, &a ) ) ) ||
+
+        !dbus_message_iter_close_container( &args, &v ) ) {
+        return DBUS_HANDLER_RESULT_NEED_MEMORY;
+    }
+
+    REPLY_SEND;
 }
 
 /******************************************************************************
- * CapsChange: player capabilities change signal
+ * Seeked: non-linear playback signal
  *****************************************************************************/
-DBUS_SIGNAL( CapsChangeSignal )
+DBUS_SIGNAL( SeekedSignal )
 {
     SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
-                 DBUS_MPRIS_PLAYER_PATH,
-                 "CapsChange" );
+                 DBUS_MPRIS_OBJECT_PATH,
+                 "Seeked" );
 
     OUT_ARGUMENTS;
 
-    ADD_INT32( &((intf_thread_t*)p_data)->p_sys->i_caps );
+    dbus_int64_t i_pos = 0;
+    intf_thread_t *p_intf = (intf_thread_t*) p_data;
+    input_thread_t *p_input = playlist_CurrentInput( p_intf->p_sys->p_playlist );
+
+    if( p_input )
+    {
+        i_pos = var_GetTime( p_input, "time" );
+        vlc_object_release( p_input );
+    }
+
+    ADD_INT64( &i_pos );
     SIGNAL_SEND;
 }
 
-DBUS_METHOD( handle_introspect_player )
+#define PROPERTY_MAPPING_BEGIN if( 0 ) {}
+#define PROPERTY_FUNC( interface, property, function ) \
+    else if( !strcmp( psz_interface_name, interface ) && \
+             !strcmp( psz_property_name,  property ) ) \
+        return function( p_conn, p_from, p_this );
+#define PROPERTY_MAPPING_END return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+DBUS_METHOD( GetProperty )
 {
-    VLC_UNUSED(p_this);
-    REPLY_INIT;
-    OUT_ARGUMENTS;
-    ADD_STRING( &psz_player_introspection_xml );
-    REPLY_SEND;
+    DBusError error;
+
+    char *psz_interface_name = NULL;
+    char *psz_property_name  = NULL;
+
+    dbus_error_init( &error );
+    dbus_message_get_args( p_from, &error,
+            DBUS_TYPE_STRING, &psz_interface_name,
+            DBUS_TYPE_STRING, &psz_property_name,
+            DBUS_TYPE_INVALID );
+
+    if( dbus_error_is_set( &error ) )
+    {
+        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
+                                         error.message );
+        dbus_error_free( &error );
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+    msg_Dbg( (vlc_object_t*) p_this, "Getting property %s",
+                                     psz_property_name );
+
+    PROPERTY_MAPPING_BEGIN
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Metadata", Metadata )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Position", Position )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "PlaybackStatus", PlaybackStatus )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "LoopStatus", LoopStatusGet )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Shuffle", ShuffleGet )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Volume", VolumeGet )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Rate", RateGet )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "MinimumRate", MinimumRate )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "MaximumRate", MaximumRate )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "CanControl", CanControl )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "CanPlay", CanPlay )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "CanPause", CanPause )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "CanSeek", CanSeek )
+    PROPERTY_MAPPING_END
+}
+
+DBUS_METHOD( SetProperty )
+{
+    DBusError error;
+
+    char *psz_interface_name = NULL;
+    char *psz_property_name  = NULL;
+
+    dbus_error_init( &error );
+    dbus_message_get_args( p_from, &error,
+            DBUS_TYPE_STRING, &psz_interface_name,
+            DBUS_TYPE_STRING, &psz_property_name,
+            DBUS_TYPE_INVALID );
+
+    if( dbus_error_is_set( &error ) )
+    {
+        msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
+                                         error.message );
+        dbus_error_free( &error );
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+    }
+
+    PROPERTY_MAPPING_BEGIN
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "LoopStatus", LoopStatusSet )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Shuffle",    ShuffleSet )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Volume",     VolumeSet )
+    PROPERTY_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Rate",       RateSet )
+    PROPERTY_MAPPING_END
 }
 
+#undef PROPERTY_MAPPING_BEGIN
+#undef PROPERTY_GET_FUNC
+#undef PROPERTY_MAPPING_END
+
 #define METHOD_FUNC( interface, method, function ) \
     else if( dbus_message_is_method_call( p_from, interface, method ) )\
         return function( p_conn, p_from, p_this )
@@ -371,25 +808,22 @@ DBUS_METHOD( handle_introspect_player )
 DBusHandlerResult
 handle_player ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
 {
-    if( dbus_message_is_method_call( p_from,
-                DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
-        return handle_introspect_player( p_conn, p_from, p_this );
+    if(0);
+    METHOD_FUNC( DBUS_INTERFACE_PROPERTIES,   "Get",        GetProperty );
+    METHOD_FUNC( DBUS_INTERFACE_PROPERTIES,   "Set",        SetProperty );
+/*  METHOD_FUNC( DBUS_INTERFACE_PROPERTIES,   "GetAll",     GetAllProperties );*/
 
     /* here D-Bus method names are associated to an handler */
 
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Prev",        Prev );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Next",        Next );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Stop",        Stop );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Play",        Play );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Pause",       Pause );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Repeat",      Repeat );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "VolumeSet",   VolumeSet );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "VolumeGet",   VolumeGet );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "PositionSet", PositionSet );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "PositionGet", PositionGet );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetStatus",   GetStatus );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetMetadata", GetCurrentMetadata );
-    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetCaps",     GetCaps );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Previous",     Prev );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Next",         Next );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Stop",         Stop );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Seek",         Seek );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Play",         Play );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Pause",        Pause );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "PlayPause",    PlayPause );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "OpenUri",      OpenUri );
+    METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "SetPosition",  SetPosition );
 
     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 }
@@ -397,72 +831,154 @@ handle_player ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
 #undef METHOD_FUNC
 
 /*****************************************************************************
- * StatusChangeEmit: Emits the StatusChange signal
+ * SeekedEmit: Emits the Seeked signal
  *****************************************************************************/
-int StatusChangeEmit( intf_thread_t * p_intf )
+int SeekedEmit( intf_thread_t * p_intf )
 {
     if( p_intf->p_sys->b_dead )
         return VLC_SUCCESS;
 
-    UpdateCaps( p_intf );
-    StatusChangeSignal( p_intf->p_sys->p_conn, p_intf );
+    SeekedSignal( p_intf->p_sys->p_conn, p_intf );
     return VLC_SUCCESS;
 }
 
-/*****************************************************************************
- * CapsChangeEmit: Emits the CapsChange signal
- *****************************************************************************/
-int CapsChangeEmit( intf_thread_t * p_intf )
+/**
+ * PropertiesChangedSignal() synthetizes and sends the
+ * org.freedesktop.DBus.Properties.PropertiesChanged signal
+ */
+static DBusHandlerResult
+PropertiesChangedSignal( intf_thread_t    *p_intf,
+                         vlc_dictionary_t *p_changed_properties )
 {
-    if( p_intf->p_sys->b_dead )
-        return VLC_SUCCESS;
+    DBusConnection  *p_conn = p_intf->p_sys->p_conn;
+    DBusMessageIter changed_properties, invalidated_properties, entry, variant;
+    const char *psz_interface_name = DBUS_MPRIS_PLAYER_INTERFACE;
+    char **ppsz_properties = NULL;
+    int i_properties = 0;
 
-    CapsChangeSignal( p_intf->p_sys->p_conn, p_intf );
-    return VLC_SUCCESS;
+    SIGNAL_INIT( DBUS_INTERFACE_PROPERTIES,
+                 DBUS_MPRIS_OBJECT_PATH,
+                 "PropertiesChanged" );
+
+    OUT_ARGUMENTS;
+    ADD_STRING( &psz_interface_name );
+    dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "{sv}",
+                                      &changed_properties );
+
+    i_properties = vlc_dictionary_keys_count( p_changed_properties );
+    ppsz_properties = vlc_dictionary_all_keys( p_changed_properties );
+
+    for( int i = 0; i < i_properties; i++ )
+    {
+        dbus_message_iter_open_container( &changed_properties,
+                                          DBUS_TYPE_DICT_ENTRY, NULL,
+                                          &entry );
+
+        dbus_message_iter_append_basic( &entry, DBUS_TYPE_STRING,
+                                        &ppsz_properties[i] );
+
+        if( !strcmp( ppsz_properties[i], "Metadata" ) )
+        {
+            input_thread_t *p_input;
+            p_input = playlist_CurrentInput( p_intf->p_sys->p_playlist );
+
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "a{sv}",
+                                              &variant );
+
+            if( p_input )
+            {
+                input_item_t *p_item = input_GetItem( p_input );
+                GetInputMeta( p_item, &variant );
+                vlc_object_release( p_input );
+            }
+
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        else if( !strcmp( ppsz_properties[i], "PlaybackStatus" ) )
+        {
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "s",
+                                              &variant );
+            MarshalPlaybackStatus( p_intf, &variant );
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        else if( !strcmp( ppsz_properties[i], "LoopStatus" ) )
+        {
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "s",
+                                              &variant );
+            MarshalLoopStatus( p_intf, &variant );
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        else if( !strcmp( ppsz_properties[i], "Rate" ) )
+        {
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "d",
+                                              &variant );
+            MarshalRate( p_intf, &variant );
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        else if( !strcmp( ppsz_properties[i], "Shuffle" ) )
+        {
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "b",
+                                              &variant );
+            MarshalShuffle( p_intf, &variant );
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        else if( !strcmp( ppsz_properties[i], "Volume" ) )
+        {
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "d",
+                                              &variant );
+            MarshalVolume( p_intf, &variant );
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        else if( !strcmp( ppsz_properties[i], "CanSeek" ) )
+        {
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "b",
+                                              &variant );
+            MarshalCanSeek( p_intf, &variant );
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        else if( !strcmp( ppsz_properties[i], "CanPlay" ) )
+        {
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "b",
+                                              &variant );
+            MarshalCanPlay( p_intf, &variant );
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        else if( !strcmp( ppsz_properties[i], "CanPause" ) )
+        {
+            dbus_message_iter_open_container( &entry,
+                                              DBUS_TYPE_VARIANT, "b",
+                                              &variant );
+            MarshalCanPause( p_intf, &variant );
+            dbus_message_iter_close_container( &entry, &variant );
+        }
+        dbus_message_iter_close_container( &changed_properties, &entry );
+    }
+
+    dbus_message_iter_close_container( &args, &changed_properties );
+    dbus_message_iter_open_container( &args, DBUS_TYPE_ARRAY, "s",
+                                      &invalidated_properties );
+    dbus_message_iter_close_container( &args, &invalidated_properties );
+
+    SIGNAL_SEND;
 }
 
 /*****************************************************************************
- * TrackChangeEmit: Emits the TrackChange signal
+ * PropertiesChangedEmit: Emits the Seeked signal
  *****************************************************************************/
-int TrackChangeEmit( intf_thread_t * p_intf, input_item_t* p_item )
+int PlayerPropertiesChangedEmit( intf_thread_t    * p_intf,
+                                 vlc_dictionary_t * p_changed_properties )
 {
     if( p_intf->p_sys->b_dead )
         return VLC_SUCCESS;
 
-    UpdateCaps( p_intf );
-    TrackChangeSignal( p_intf->p_sys->p_conn, p_item );
-    return VLC_SUCCESS;
-}
-
-/*****************************************************************************
- * MarshalStatus: Fill a DBusMessage with the current player status
- *****************************************************************************/
-
-static int MarshalStatus( intf_thread_t* p_intf, DBusMessageIter* args )
-{ /* This is NOT the right way to do that, it would be better to sore
-     the status information in p_sys and update it on change, thus
-     avoiding a long lock */
-
-    DBusMessageIter status;
-    dbus_int32_t i_state, i_random, i_repeat, i_loop;
-    playlist_t* p_playlist = p_intf->p_sys->p_playlist;
-
-    vlc_mutex_lock( &p_intf->p_sys->lock );
-    i_state = p_intf->p_sys->i_playing_state;
-    vlc_mutex_unlock( &p_intf->p_sys->lock );
-
-    i_random = var_CreateGetBool( p_playlist, "random" );
-
-    i_repeat = var_CreateGetBool( p_playlist, "repeat" );
-
-    i_loop = var_CreateGetBool( p_playlist, "loop" );
-
-    dbus_message_iter_open_container( args, DBUS_TYPE_STRUCT, NULL, &status );
-    dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_state );
-    dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_random );
-    dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_repeat );
-    dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_loop );
-    dbus_message_iter_close_container( args, &status );
-
+    PropertiesChangedSignal( p_intf, p_changed_properties );
     return VLC_SUCCESS;
 }