1 /*****************************************************************************
2 * dbus.c : D-Bus control interface
3 *****************************************************************************
4 * Copyright © 2006-2008 Rafaël Carré
5 * Copyright © 2007-2008 Mirsal Ennaime
6 * Copyright © 2009 The VideoLAN team
9 * Authors: Rafaël Carré <funman at videolanorg>
10 * Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
28 * D-Bus Specification:
29 * http://dbus.freedesktop.org/doc/dbus-specification.html
30 * D-Bus low-level C API (libdbus)
31 * http://dbus.freedesktop.org/doc/dbus/api/html/index.html
33 * "If you use this low-level API directly, you're signing up for some pain."
35 * MPRIS Specification version 1.0
36 * http://wiki.xmms2.xmms.se/index.php/MPRIS
39 /*****************************************************************************
41 *****************************************************************************/
47 #include <dbus/dbus.h>
50 #include <vlc_common.h>
51 #include <vlc_plugin.h>
53 #include <vlc_interface.h>
54 #include <vlc_playlist.h>
60 /*****************************************************************************
62 *****************************************************************************/
64 static int Open ( vlc_object_t * );
65 static void Close ( vlc_object_t * );
66 static void Run ( intf_thread_t * );
68 static int StateChange( intf_thread_t *, int );
69 static int TrackChange( intf_thread_t * );
70 static int StatusChangeEmit( intf_thread_t *);
71 static int TrackListChangeEmit( intf_thread_t *, int, int );
73 static int AllCallback( vlc_object_t*, const char*, vlc_value_t, vlc_value_t, void* );
75 static int GetInputMeta ( input_item_t *, DBusMessageIter * );
76 static int MarshalStatus ( intf_thread_t *, DBusMessageIter * );
77 static int UpdateCaps( intf_thread_t* );
79 /* GetCaps() capabilities */
83 CAPS_CAN_GO_NEXT = 1 << 0,
84 CAPS_CAN_GO_PREV = 1 << 1,
85 CAPS_CAN_PAUSE = 1 << 2,
86 CAPS_CAN_PLAY = 1 << 3,
87 CAPS_CAN_SEEK = 1 << 4,
88 CAPS_CAN_PROVIDE_METADATA = 1 << 5,
89 CAPS_CAN_HAS_TRACKLIST = 1 << 6
92 // The signal that can be get from the callbacks
97 SIGNAL_PLAYLIST_ITEM_APPEND,
98 SIGNAL_PLAYLIST_ITEM_DELETED,
107 DBusConnection *p_conn;
108 playlist_t *p_playlist;
112 vlc_array_t *p_events;
123 #define INTF ((intf_thread_t *)p_this)
124 #define PL (INTF->p_sys->p_playlist)
127 /*****************************************************************************
129 *****************************************************************************/
132 set_shortname( N_("dbus"))
133 set_category( CAT_INTERFACE )
134 set_subcategory( SUBCAT_INTERFACE_CONTROL )
135 set_description( N_("D-Bus control interface") )
136 set_capability( "interface", 0 )
137 set_callbacks( Open, Close )
140 /*****************************************************************************
142 *****************************************************************************/
149 libvlc_Quit(INTF->p_libvlc);
153 DBUS_METHOD( MprisVersion )
154 { /*implemented version of the mpris spec */
157 VLC_UNUSED( p_this );
158 dbus_uint16_t i_major = VLC_MPRIS_VERSION_MAJOR;
159 dbus_uint16_t i_minor = VLC_MPRIS_VERSION_MINOR;
160 DBusMessageIter version;
162 if( !dbus_message_iter_open_container( &args, DBUS_TYPE_STRUCT, NULL,
164 return DBUS_HANDLER_RESULT_NEED_MEMORY;
166 if( !dbus_message_iter_append_basic( &version, DBUS_TYPE_UINT16,
168 return DBUS_HANDLER_RESULT_NEED_MEMORY;
170 if( !dbus_message_iter_append_basic( &version, DBUS_TYPE_UINT16,
172 return DBUS_HANDLER_RESULT_NEED_MEMORY;
174 if( !dbus_message_iter_close_container( &args, &version ) )
175 return DBUS_HANDLER_RESULT_NEED_MEMORY;
179 DBUS_METHOD( PositionGet )
180 { /* returns position in milliseconds */
185 input_thread_t *p_input = playlist_CurrentInput( PL );
191 i_pos = var_GetTime( p_input, "time" ) / 1000;
192 vlc_object_release( p_input );
198 DBUS_METHOD( PositionSet )
199 { /* set position in milliseconds */
202 vlc_value_t position;
206 dbus_error_init( &error );
208 dbus_message_get_args( p_from, &error,
209 DBUS_TYPE_INT32, &i_pos,
212 if( dbus_error_is_set( &error ) )
214 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
216 dbus_error_free( &error );
217 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
219 input_thread_t *p_input = playlist_CurrentInput( PL );
223 position.i_time = ((mtime_t)i_pos) * 1000;
224 var_Set( p_input, "time", position );
225 vlc_object_release( p_input );
230 DBUS_METHOD( VolumeGet )
231 { /* returns volume in percentage */
234 dbus_int32_t i_dbus_vol;
235 audio_volume_t i_vol;
237 /* 2nd argument of aout_VolumeGet is int32 */
238 aout_VolumeGet( PL, &i_vol );
240 double f_vol = 100. * i_vol / AOUT_VOLUME_MAX;
241 i_dbus_vol = round( f_vol );
242 ADD_INT32( &i_dbus_vol );
246 DBUS_METHOD( VolumeSet )
247 { /* set volume in percentage */
251 dbus_error_init( &error );
253 dbus_int32_t i_dbus_vol;
254 audio_volume_t i_vol;
256 dbus_message_get_args( p_from, &error,
257 DBUS_TYPE_INT32, &i_dbus_vol,
260 if( dbus_error_is_set( &error ) )
262 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
264 dbus_error_free( &error );
265 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
268 double f_vol = AOUT_VOLUME_MAX * i_dbus_vol / 100.;
269 i_vol = round( f_vol );
270 aout_VolumeSet( PL, i_vol );
275 { /* next playlist item */
282 { /* previous playlist item */
295 DBUS_METHOD( GetStatus )
296 { /* returns the current status as a struct of 4 ints */
298 First 0 = Playing, 1 = Paused, 2 = Stopped.
299 Second 0 = Playing linearly , 1 = Playing randomly.
300 Third 0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
301 Fourth 0 = Stop playing once the last element has been played, 1 = Never give up playing *
306 MarshalStatus( p_this, &args );
314 playlist_Pause( PL );
322 input_thread_t *p_input = playlist_CurrentInput( PL );
327 input_Control( p_input, INPUT_SET_POSITION, i_pos );
328 vlc_object_release( p_input );
336 DBUS_METHOD( GetCurrentMetadata )
340 playlist_t *p_playlist = PL;
343 playlist_item_t* p_item = playlist_CurrentPlayingItem( p_playlist );
345 GetInputMeta( p_item->p_input, &args );
350 DBUS_METHOD( GetCaps )
355 ADD_INT32( &INTF->p_sys->i_caps );
360 /* Media Player information */
362 DBUS_METHOD( Identity )
369 if( asprintf( &psz_identity, "%s %s", PACKAGE, VERSION ) != -1 )
371 ADD_STRING( &psz_identity );
372 free( psz_identity );
375 return DBUS_HANDLER_RESULT_NEED_MEMORY;
382 DBUS_METHOD( AddTrack )
383 { /* add the string to the playlist, and play it if the boolean is true */
388 dbus_error_init( &error );
393 dbus_message_get_args( p_from, &error,
394 DBUS_TYPE_STRING, &psz_mrl,
395 DBUS_TYPE_BOOLEAN, &b_play,
398 if( dbus_error_is_set( &error ) )
400 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
402 dbus_error_free( &error );
403 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
406 playlist_Add( PL, psz_mrl, NULL, PLAYLIST_APPEND |
407 ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
408 PLAYLIST_END, true, false );
410 dbus_int32_t i_success = 0;
411 ADD_INT32( &i_success );
416 DBUS_METHOD( GetCurrentTrack )
421 /* XXX: how about locking?! */
422 dbus_int32_t i_position = PL->i_current_index;
424 ADD_INT32( &i_position );
428 DBUS_METHOD( GetMetadata )
433 dbus_error_init( &error );
435 dbus_int32_t i_position;
436 playlist_t *p_playlist = PL;
438 dbus_message_get_args( p_from, &error,
439 DBUS_TYPE_INT32, &i_position,
442 if( dbus_error_is_set( &error ) )
444 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
446 dbus_error_free( &error );
447 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
451 if( i_position < p_playlist->current.i_size )
453 GetInputMeta( p_playlist->current.p_elems[i_position]->p_input, &args );
460 DBUS_METHOD( GetLength )
465 /* XXX: how about locking */
466 dbus_int32_t i_elements = PL->current.i_size;
468 ADD_INT32( &i_elements );
472 DBUS_METHOD( DelTrack )
477 dbus_error_init( &error );
479 dbus_int32_t i_position;
480 playlist_t *p_playlist = PL;
482 dbus_message_get_args( p_from, &error,
483 DBUS_TYPE_INT32, &i_position,
486 if( dbus_error_is_set( &error ) )
488 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
490 dbus_error_free( &error );
491 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
495 if( i_position < p_playlist->current.i_size )
497 playlist_DeleteFromInput( p_playlist,
498 p_playlist->current.p_elems[i_position]->p_input,
506 DBUS_METHOD( SetLoop )
514 dbus_error_init( &error );
515 dbus_message_get_args( p_from, &error,
516 DBUS_TYPE_BOOLEAN, &b_loop,
519 if( dbus_error_is_set( &error ) )
521 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
523 dbus_error_free( &error );
524 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
527 var_SetBool( PL, "loop", ( b_loop == TRUE ) );
532 DBUS_METHOD( Repeat )
538 dbus_bool_t b_repeat;
540 dbus_error_init( &error );
541 dbus_message_get_args( p_from, &error,
542 DBUS_TYPE_BOOLEAN, &b_repeat,
545 if( dbus_error_is_set( &error ) )
547 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
549 dbus_error_free( &error );
550 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
553 var_SetBool( PL, "repeat", ( b_repeat == TRUE ) );
558 DBUS_METHOD( SetRandom )
564 dbus_bool_t b_random;
566 dbus_error_init( &error );
567 dbus_message_get_args( p_from, &error,
568 DBUS_TYPE_BOOLEAN, &b_random,
571 if( dbus_error_is_set( &error ) )
573 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
575 dbus_error_free( &error );
576 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
579 var_SetBool( PL, "random", ( b_random == TRUE ) );
583 /*****************************************************************************
584 * Introspection method
585 *****************************************************************************/
587 DBUS_METHOD( handle_introspect_root )
588 { /* handles introspection of root object */
592 ADD_STRING( &psz_introspection_xml_data_root );
596 DBUS_METHOD( handle_introspect_player )
601 ADD_STRING( &psz_introspection_xml_data_player );
605 DBUS_METHOD( handle_introspect_tracklist )
610 ADD_STRING( &psz_introspection_xml_data_tracklist );
614 /*****************************************************************************
615 * handle_*: answer to incoming messages
616 *****************************************************************************/
618 #define METHOD_FUNC( method, function ) \
619 else if( dbus_message_is_method_call( p_from, MPRIS_DBUS_INTERFACE, method ) )\
620 return function( p_conn, p_from, p_this )
622 DBUS_METHOD( handle_root )
625 if( dbus_message_is_method_call( p_from,
626 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
627 return handle_introspect_root( p_conn, p_from, p_this );
629 /* here D-Bus method's names are associated to an handler */
631 METHOD_FUNC( "Identity", Identity );
632 METHOD_FUNC( "MprisVersion", MprisVersion );
633 METHOD_FUNC( "Quit", Quit );
635 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
639 DBUS_METHOD( handle_player )
641 if( dbus_message_is_method_call( p_from,
642 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
643 return handle_introspect_player( p_conn, p_from, p_this );
645 /* here D-Bus method's names are associated to an handler */
647 METHOD_FUNC( "Prev", Prev );
648 METHOD_FUNC( "Next", Next );
649 METHOD_FUNC( "Stop", Stop );
650 METHOD_FUNC( "Play", Play );
651 METHOD_FUNC( "Pause", Pause );
652 METHOD_FUNC( "Repeat", Repeat );
653 METHOD_FUNC( "VolumeSet", VolumeSet );
654 METHOD_FUNC( "VolumeGet", VolumeGet );
655 METHOD_FUNC( "PositionSet", PositionSet );
656 METHOD_FUNC( "PositionGet", PositionGet );
657 METHOD_FUNC( "GetStatus", GetStatus );
658 METHOD_FUNC( "GetMetadata", GetCurrentMetadata );
659 METHOD_FUNC( "GetCaps", GetCaps );
661 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
664 DBUS_METHOD( handle_tracklist )
666 if( dbus_message_is_method_call( p_from,
667 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
668 return handle_introspect_tracklist( p_conn, p_from, p_this );
670 /* here D-Bus method's names are associated to an handler */
672 METHOD_FUNC( "GetMetadata", GetMetadata );
673 METHOD_FUNC( "GetCurrentTrack", GetCurrentTrack );
674 METHOD_FUNC( "GetLength", GetLength );
675 METHOD_FUNC( "AddTrack", AddTrack );
676 METHOD_FUNC( "DelTrack", DelTrack );
677 METHOD_FUNC( "SetLoop", SetLoop );
678 METHOD_FUNC( "SetRandom", SetRandom );
680 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
683 /*****************************************************************************
684 * Open: initialize interface
685 *****************************************************************************/
687 static int Open( vlc_object_t *p_this )
688 { /* initialisation of the connection */
689 intf_thread_t *p_intf = (intf_thread_t*)p_this;
690 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
691 playlist_t *p_playlist;
692 DBusConnection *p_conn;
698 p_sys->b_meta_read = false;
699 p_sys->i_caps = CAPS_NONE;
700 p_sys->b_dead = false;
702 dbus_error_init( &error );
704 /* connect to the session bus */
705 p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
708 msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
710 dbus_error_free( &error );
715 /* register a well-known name on the bus */
716 dbus_bus_request_name( p_conn, VLC_MPRIS_DBUS_SERVICE, 0, &error );
717 if( dbus_error_is_set( &error ) )
719 msg_Err( p_this, "Error requesting service " VLC_MPRIS_DBUS_SERVICE
720 ": %s", error.message );
721 dbus_error_free( &error );
726 /* we register the objects */
727 dbus_connection_register_object_path( p_conn, MPRIS_DBUS_ROOT_PATH,
728 &vlc_dbus_root_vtable, p_this );
729 dbus_connection_register_object_path( p_conn, MPRIS_DBUS_PLAYER_PATH,
730 &vlc_dbus_player_vtable, p_this );
731 dbus_connection_register_object_path( p_conn, MPRIS_DBUS_TRACKLIST_PATH,
732 &vlc_dbus_tracklist_vtable, p_this );
734 dbus_connection_flush( p_conn );
736 p_intf->pf_run = Run;
737 p_intf->p_sys = p_sys;
738 p_sys->p_conn = p_conn;
739 p_sys->p_events = vlc_array_new();
740 vlc_mutex_init( &p_sys->lock );
742 p_playlist = pl_Hold( p_intf );
743 p_sys->p_playlist = p_playlist;
746 var_AddCallback( p_playlist, "item-current", AllCallback, p_intf );
747 var_AddCallback( p_playlist, "intf-change", AllCallback, p_intf );
748 var_AddCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
749 var_AddCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
750 var_AddCallback( p_playlist, "random", AllCallback, p_intf );
751 var_AddCallback( p_playlist, "repeat", AllCallback, p_intf );
752 var_AddCallback( p_playlist, "loop", AllCallback, p_intf );
755 UpdateCaps( p_intf );
760 /*****************************************************************************
761 * Close: destroy interface
762 *****************************************************************************/
764 static void Close ( vlc_object_t *p_this )
766 intf_thread_t *p_intf = (intf_thread_t*) p_this;
767 intf_sys_t *p_sys = p_intf->p_sys;
768 playlist_t *p_playlist = p_sys->p_playlist;
769 input_thread_t *p_input;
771 var_DelCallback( p_playlist, "item-current", AllCallback, p_intf );
772 var_DelCallback( p_playlist, "intf-change", AllCallback, p_intf );
773 var_DelCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
774 var_DelCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
775 var_DelCallback( p_playlist, "random", AllCallback, p_intf );
776 var_DelCallback( p_playlist, "repeat", AllCallback, p_intf );
777 var_DelCallback( p_playlist, "loop", AllCallback, p_intf );
779 p_input = playlist_CurrentInput( p_playlist );
782 var_DelCallback( p_input, "state", AllCallback, p_intf );
783 vlc_object_release( p_input );
785 pl_Release( p_intf );
787 dbus_connection_unref( p_sys->p_conn );
789 // Free the events array
790 for( int i = 0; i < vlc_array_count( p_sys->p_events ); i++ )
792 callback_info_t* info = vlc_array_item_at_index( p_sys->p_events, i );
795 vlc_mutex_destroy( &p_sys->lock );
796 vlc_array_destroy( p_sys->p_events );
800 /*****************************************************************************
802 *****************************************************************************/
804 static void Run ( intf_thread_t *p_intf )
808 if( dbus_connection_get_dispatch_status(p_intf->p_sys->p_conn)
809 == DBUS_DISPATCH_COMPLETE )
810 msleep( INTF_IDLE_SLEEP );
811 int canc = vlc_savecancel();
812 dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
814 /* Get the list of events to process
816 * We can't keep the lock on p_intf->p_sys->p_events, else we risk a
818 * The signal functions could lock mutex X while p_events is locked;
819 * While some other function in vlc (playlist) might lock mutex X
820 * and then set a variable which would call AllCallback(), which itself
821 * needs to lock p_events to add a new event.
823 vlc_mutex_lock( &p_intf->p_sys->lock );
824 int i_events = vlc_array_count( p_intf->p_sys->p_events );
825 callback_info_t* info[i_events];
826 for( int i = i_events - 1; i >= 0; i-- )
828 info[i] = vlc_array_item_at_index( p_intf->p_sys->p_events, i );
829 vlc_array_remove( p_intf->p_sys->p_events, i );
831 vlc_mutex_unlock( &p_intf->p_sys->lock );
833 for( int i = 0; i < i_events; i++ )
835 switch( info[i]->signal )
837 case SIGNAL_ITEM_CURRENT:
838 TrackChange( p_intf );
840 case SIGNAL_INTF_CHANGE:
841 case SIGNAL_PLAYLIST_ITEM_APPEND:
842 case SIGNAL_PLAYLIST_ITEM_DELETED:
843 TrackListChangeEmit( p_intf, info[i]->signal, info[i]->i_node );
848 StatusChangeEmit( p_intf );
851 StateChange( p_intf, info[i]->i_input_state );
858 vlc_restorecancel( canc );
863 // Get all the callbacks
864 static int AllCallback( vlc_object_t *p_this, const char *psz_var,
865 vlc_value_t oldval, vlc_value_t newval, void *p_data )
869 intf_thread_t *p_intf = (intf_thread_t*)p_data;
871 callback_info_t *info = malloc( sizeof( callback_info_t ) );
875 // Wich event is it ?
876 if( !strcmp( "item-current", psz_var ) )
877 info->signal = SIGNAL_ITEM_CURRENT;
878 else if( !strcmp( "intf-change", psz_var ) )
879 info->signal = SIGNAL_INTF_CHANGE;
880 else if( !strcmp( "playlist-item-append", psz_var ) )
882 info->signal = SIGNAL_PLAYLIST_ITEM_APPEND;
883 info->i_node = ((playlist_add_t*)newval.p_address)->i_node;
885 else if( !strcmp( "playlist-item-deleted", psz_var ) )
886 info->signal = SIGNAL_PLAYLIST_ITEM_DELETED;
887 else if( !strcmp( "random", psz_var ) )
888 info->signal = SIGNAL_RANDOM;
889 else if( !strcmp( "repeat", psz_var ) )
890 info->signal = SIGNAL_REPEAT;
891 else if( !strcmp( "loop", psz_var ) )
892 info->signal = SIGNAL_LOOP;
893 else if( !strcmp( "state", psz_var ) )
895 info->signal = SIGNAL_STATE;
896 info->i_input_state = newval.i_int;
902 vlc_mutex_lock( &p_intf->p_sys->lock );
903 vlc_array_append( p_intf->p_sys->p_events, info );
904 vlc_mutex_unlock( &p_intf->p_sys->lock );
908 /******************************************************************************
909 * CapsChange: player capabilities change signal
910 *****************************************************************************/
911 DBUS_SIGNAL( CapsChangeSignal )
913 SIGNAL_INIT( "CapsChange" );
916 ADD_INT32( &((intf_thread_t*)p_data)->p_sys->i_caps );
920 /******************************************************************************
921 * TrackListChange: tracklist order / length change signal
922 *****************************************************************************/
923 DBUS_SIGNAL( TrackListChangeSignal )
924 { /* emit the new tracklist lengh */
925 SIGNAL_INIT("TrackListChange");
929 dbus_int32_t i_elements = ((intf_thread_t*)p_data)->p_sys->p_playlist->current.i_size;
931 ADD_INT32( &i_elements );
935 /*****************************************************************************
936 * TrackListChangeEmit: Emits the TrackListChange signal
937 *****************************************************************************/
938 /* FIXME: It is not called on tracklist reordering */
939 static int TrackListChangeEmit( intf_thread_t *p_intf, int signal, int i_node )
941 // "playlist-item-append"
942 if( signal == SIGNAL_PLAYLIST_ITEM_APPEND )
944 /* don't signal when items are added/removed in p_category */
945 playlist_t *p_playlist = p_intf->p_sys->p_playlist;
947 playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_node );
949 while( p_item->p_parent )
950 p_item = p_item->p_parent;
951 if( p_item == p_playlist->p_root_category )
959 if( p_intf->p_sys->b_dead )
962 UpdateCaps( p_intf );
963 TrackListChangeSignal( p_intf->p_sys->p_conn, p_intf );
966 /*****************************************************************************
967 * TrackChange: Playlist item change callback
968 *****************************************************************************/
970 DBUS_SIGNAL( TrackChangeSignal )
971 { /* emit the metadata of the new item */
972 SIGNAL_INIT( "TrackChange" );
975 input_item_t *p_item = (input_item_t*) p_data;
976 GetInputMeta ( p_item, &args );
981 /*****************************************************************************
982 * StatusChange: Player status change signal
983 *****************************************************************************/
985 DBUS_SIGNAL( StatusChangeSignal )
986 { /* send the updated status info on the bus */
987 SIGNAL_INIT( "StatusChange" );
990 /* we're called from a callback of input_thread_t, so it can not be
991 * destroyed before we return */
992 MarshalStatus( (intf_thread_t*) p_data, &args );
997 /*****************************************************************************
998 * StateChange: callback on input "state"
999 *****************************************************************************/
1000 //static int StateChange( vlc_object_t *p_this, const char* psz_var,
1001 // vlc_value_t oldval, vlc_value_t newval, void *p_data )
1002 static int StateChange( intf_thread_t *p_intf, int i_input_state )
1004 intf_sys_t *p_sys = p_intf->p_sys;
1005 playlist_t *p_playlist = p_sys->p_playlist;
1006 input_thread_t *p_input;
1007 input_item_t *p_item;
1009 if( p_intf->p_sys->b_dead )
1012 UpdateCaps( p_intf );
1014 if( !p_sys->b_meta_read && i_input_state == PLAYING_S )
1016 p_input = playlist_CurrentInput( p_playlist );
1019 p_item = input_GetItem( p_input );
1022 p_sys->b_meta_read = true;
1023 TrackChangeSignal( p_sys->p_conn, p_item );
1025 vlc_object_release( p_input );
1029 if( i_input_state == PLAYING_S || i_input_state == PAUSE_S ||
1030 i_input_state == END_S )
1032 StatusChangeSignal( p_sys->p_conn, p_intf );
1038 /*****************************************************************************
1039 * StatusChangeEmit: Emits the StatusChange signal
1040 *****************************************************************************/
1041 static int StatusChangeEmit( intf_thread_t * p_intf )
1043 if( p_intf->p_sys->b_dead )
1046 UpdateCaps( p_intf );
1047 StatusChangeSignal( p_intf->p_sys->p_conn, p_intf );
1051 /*****************************************************************************
1052 * TrackChange: callback on playlist "item-current"
1053 *****************************************************************************/
1054 static int TrackChange( intf_thread_t *p_intf )
1056 intf_sys_t *p_sys = p_intf->p_sys;
1057 playlist_t *p_playlist = p_sys->p_playlist;
1058 input_thread_t *p_input = NULL;
1059 input_item_t *p_item = NULL;
1061 if( p_intf->p_sys->b_dead )
1064 p_sys->b_meta_read = false;
1066 p_input = playlist_CurrentInput( p_playlist );
1072 p_item = input_GetItem( p_input );
1075 vlc_object_release( p_input );
1076 return VLC_EGENERIC;
1079 if( input_item_IsPreparsed( p_item ) )
1081 p_sys->b_meta_read = true;
1082 TrackChangeSignal( p_sys->p_conn, p_item );
1085 var_AddCallback( p_input, "state", AllCallback, p_intf );
1087 vlc_object_release( p_input );
1091 /*****************************************************************************
1092 * UpdateCaps: update p_sys->i_caps
1093 * This function have to be called with the playlist unlocked
1094 ****************************************************************************/
1095 static int UpdateCaps( intf_thread_t* p_intf )
1097 intf_sys_t* p_sys = p_intf->p_sys;
1098 dbus_int32_t i_caps = CAPS_CAN_HAS_TRACKLIST;
1099 playlist_t* p_playlist = p_sys->p_playlist;
1102 if( p_playlist->current.i_size > 0 )
1103 i_caps |= CAPS_CAN_PLAY | CAPS_CAN_GO_PREV | CAPS_CAN_GO_NEXT;
1106 input_thread_t* p_input = playlist_CurrentInput( p_playlist );
1109 /* XXX: if UpdateCaps() is called too early, these are
1110 * unconditionnaly true */
1111 if( var_GetBool( p_input, "can-pause" ) )
1112 i_caps |= CAPS_CAN_PAUSE;
1113 if( var_GetBool( p_input, "can-seek" ) )
1114 i_caps |= CAPS_CAN_SEEK;
1115 vlc_object_release( p_input );
1118 if( p_sys->b_meta_read )
1119 i_caps |= CAPS_CAN_PROVIDE_METADATA;
1121 if( i_caps != p_intf->p_sys->i_caps )
1123 p_sys->i_caps = i_caps;
1124 CapsChangeSignal( p_intf->p_sys->p_conn, (vlc_object_t*)p_intf );
1130 /*****************************************************************************
1131 * GetInputMeta: Fill a DBusMessage with the given input item metadata
1132 *****************************************************************************/
1134 #define ADD_META( entry, type, data ) \
1136 dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
1137 NULL, &dict_entry ); \
1138 dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
1139 &ppsz_meta_items[entry] ); \
1140 dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
1141 type##_AS_STRING, &variant ); \
1142 dbus_message_iter_append_basic( &variant, \
1145 dbus_message_iter_close_container( &dict_entry, &variant ); \
1146 dbus_message_iter_close_container( &dict, &dict_entry ); }
1148 #define ADD_VLC_META_STRING( entry, item ) \
1150 char * psz = input_item_Get##item( p_input );\
1151 ADD_META( entry, DBUS_TYPE_STRING, \
1156 static int GetInputMeta( input_item_t* p_input,
1157 DBusMessageIter *args )
1159 DBusMessageIter dict, dict_entry, variant;
1160 /* We need the track length to be expressed in milli-seconds
1161 * instead of µ-seconds */
1162 dbus_int64_t i_length = ( input_item_GetDuration( p_input ) / 1000 );
1164 const char* ppsz_meta_items[] =
1166 "title", "artist", "genre", "copyright", "album", "tracknum",
1167 "description", "rating", "date", "setting", "url", "language",
1168 "nowplaying", "publisher", "encodedby", "arturl", "trackid",
1169 "status", "location", "length", "video-codec", "audio-codec",
1170 "video-bitrate", "audio-bitrate", "audio-samplerate"
1173 dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
1175 ADD_VLC_META_STRING( 0, Title );
1176 ADD_VLC_META_STRING( 1, Artist );
1177 ADD_VLC_META_STRING( 2, Genre );
1178 ADD_VLC_META_STRING( 3, Copyright );
1179 ADD_VLC_META_STRING( 4, Album );
1180 ADD_VLC_META_STRING( 5, TrackNum );
1181 ADD_VLC_META_STRING( 6, Description );
1182 ADD_VLC_META_STRING( 7, Rating );
1183 ADD_VLC_META_STRING( 8, Date );
1184 ADD_VLC_META_STRING( 9, Setting );
1185 ADD_VLC_META_STRING( 10, URL );
1186 ADD_VLC_META_STRING( 11, Language );
1187 ADD_VLC_META_STRING( 12, NowPlaying );
1188 ADD_VLC_META_STRING( 13, Publisher );
1189 ADD_VLC_META_STRING( 14, EncodedBy );
1190 ADD_VLC_META_STRING( 15, ArtURL );
1191 ADD_VLC_META_STRING( 16, TrackID );
1193 vlc_mutex_lock( &p_input->lock );
1194 if( p_input->p_meta )
1195 ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status );
1196 vlc_mutex_unlock( &p_input->lock );
1198 ADD_VLC_META_STRING( 18, URI );
1199 ADD_META( 19, DBUS_TYPE_INT64, i_length );
1201 dbus_message_iter_close_container( args, &dict );
1206 #undef ADD_VLC_META_STRING
1208 /*****************************************************************************
1209 * MarshalStatus: Fill a DBusMessage with the current player status
1210 *****************************************************************************/
1212 static int MarshalStatus( intf_thread_t* p_intf, DBusMessageIter* args )
1213 { /* This is NOT the right way to do that, it would be better to sore
1214 the status information in p_sys and update it on change, thus
1215 avoiding a long lock */
1217 DBusMessageIter status;
1218 dbus_int32_t i_state, i_random, i_repeat, i_loop;
1220 playlist_t* p_playlist = p_intf->p_sys->p_playlist;
1221 input_thread_t* p_input = NULL;
1225 p_input = playlist_CurrentInput( p_playlist );
1228 i_val = var_GetInteger( p_input, "state" );
1229 if( i_val >= END_S )
1231 else if( i_val == PAUSE_S )
1233 else if( i_val <= PLAYING_S )
1235 vlc_object_release( p_input );
1238 i_random = var_CreateGetBool( p_playlist, "random" );
1240 i_repeat = var_CreateGetBool( p_playlist, "repeat" );
1242 i_loop = var_CreateGetBool( p_playlist, "loop" );
1244 dbus_message_iter_open_container( args, DBUS_TYPE_STRUCT, NULL, &status );
1245 dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_state );
1246 dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_random );
1247 dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_repeat );
1248 dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_loop );
1249 dbus_message_iter_close_container( args, &status );