1 /*****************************************************************************
2 * dbus.c : D-Bus control interface
3 *****************************************************************************
4 * Copyright (C) 2006 Rafaël Carré
7 * Authors: Rafaël Carré <funman at videolanorg>
8 * Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
26 * D-Bus Specification:
27 * http://dbus.freedesktop.org/doc/dbus-specification.html
28 * D-Bus low-level C API (libdbus)
29 * http://dbus.freedesktop.org/doc/dbus/api/html/index.html
31 "If you use this low-level API directly, you're signing up for some pain."
32 * MPRIS Specification (still drafting on June, 25 of 2007):
33 * http://wiki.xmms2.xmms.se/index.php/Media_Player_Interfaces
36 /*****************************************************************************
38 *****************************************************************************/
40 #include <dbus/dbus.h>
46 #include <vlc_interface.h>
48 #include <vlc_input.h>
49 #include <vlc_playlist.h>
51 /*****************************************************************************
53 *****************************************************************************/
55 static int Open ( vlc_object_t * );
56 static void Close ( vlc_object_t * );
57 static void Run ( intf_thread_t * );
59 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
60 vlc_value_t oldval, vlc_value_t newval, void *p_data );
62 static int GetInputMeta ( input_item_t *p_input,
63 DBusMessageIter *args);
67 DBusConnection *p_conn;
70 /*****************************************************************************
72 *****************************************************************************/
75 set_shortname( _("dbus"));
76 set_category( CAT_INTERFACE );
77 set_subcategory( SUBCAT_INTERFACE_CONTROL );
78 set_description( _("D-Bus control interface") );
79 set_capability( "interface", 0 );
80 set_callbacks( Open, Close );
83 /*****************************************************************************
85 *****************************************************************************/
92 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
93 playlist_Stop( p_playlist );
94 pl_Release( ((vlc_object_t*) p_this) );
95 vlc_object_kill(((vlc_object_t*)p_this)->p_libvlc);
99 DBUS_METHOD( PositionGet )
100 { /* returns position as an int in the range [0;1000] */
103 vlc_value_t position;
106 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
107 input_thread_t *p_input = p_playlist->p_input;
113 var_Get( p_input, "position", &position );
114 i_pos = position.f_float * 1000 ;
116 pl_Release( ((vlc_object_t*) p_this) );
121 DBUS_METHOD( PositionSet )
122 { /* set position from an int in the range [0;1000] */
125 vlc_value_t position;
126 playlist_t* p_playlist = NULL;
130 dbus_error_init( &error );
132 dbus_message_get_args( p_from, &error,
133 DBUS_TYPE_INT32, &i_pos,
136 if( dbus_error_is_set( &error ) )
138 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
140 dbus_error_free( &error );
141 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
143 p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
144 input_thread_t *p_input = p_playlist->p_input;
148 position.f_float = ((float)i_pos) / 1000;
149 var_Set( p_input, "position", position );
151 pl_Release( ((vlc_object_t*) p_this) );
155 DBUS_METHOD( VolumeGet )
156 { /* returns volume in percentage */
159 dbus_int32_t i_dbus_vol;
160 audio_volume_t i_vol;
161 /* 2nd argument of aout_VolumeGet is int32 */
162 aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
163 i_dbus_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
164 ADD_INT32( &i_dbus_vol );
168 DBUS_METHOD( VolumeSet )
169 { /* set volume in percentage */
173 dbus_error_init( &error );
175 dbus_int32_t i_dbus_vol;
176 audio_volume_t i_vol;
178 dbus_message_get_args( p_from, &error,
179 DBUS_TYPE_INT32, &i_dbus_vol,
182 if( dbus_error_is_set( &error ) )
184 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
186 dbus_error_free( &error );
187 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
190 i_vol = ( AOUT_VOLUME_MAX / 100 ) *i_dbus_vol;
191 aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
197 { /* next playlist item */
199 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
200 playlist_Next( p_playlist );
201 pl_Release( ((vlc_object_t*) p_this) );
206 { /* previous playlist item */
208 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
209 playlist_Prev( p_playlist );
210 pl_Release( ((vlc_object_t*) p_this) );
217 playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
218 playlist_Stop( p_playlist );
219 pl_Release( ((vlc_object_t*) p_this) );
223 DBUS_METHOD( GetStatus )
224 { /* returns an int: 0=playing 1=paused 2=stopped */
228 dbus_int32_t i_status;
231 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
232 input_thread_t *p_input = p_playlist->p_input;
237 var_Get( p_input, "state", &val );
238 if( val.i_int == PAUSE_S )
240 else if( val.i_int == PLAYING_S )
244 pl_Release( p_playlist );
246 ADD_INT32( &i_status );
253 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
254 playlist_Pause( p_playlist );
255 pl_Release( p_playlist );
262 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
263 playlist_Play( p_playlist );
264 pl_Release( p_playlist );
268 DBUS_METHOD( GetCurrentMetadata )
272 playlist_t* p_playlist = pl_Yield( (vlc_object_t*) p_this );
274 if( p_playlist->status.p_item )
275 GetInputMeta( p_playlist->status.p_item->p_input, &args );
277 pl_Release( p_playlist );
281 /* Media Player information */
283 DBUS_METHOD( Identity )
287 char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 2 );
288 sprintf( psz_identity, "%s %s", PACKAGE, VERSION );
289 ADD_STRING( &psz_identity );
290 free( psz_identity );
296 DBUS_METHOD( AddTrack )
297 { /* add the string to the playlist, and play it if the boolean is true */
301 dbus_error_init( &error );
302 playlist_t* p_playlist = NULL;
307 dbus_message_get_args( p_from, &error,
308 DBUS_TYPE_STRING, &psz_mrl,
309 DBUS_TYPE_BOOLEAN, &b_play,
312 if( dbus_error_is_set( &error ) )
314 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
316 dbus_error_free( &error );
317 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
320 p_playlist = pl_Yield( (vlc_object_t*) p_this );
321 playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
322 ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
323 PLAYLIST_END, VLC_TRUE, VLC_FALSE );
324 pl_Release( p_playlist );
329 DBUS_METHOD( GetCurrentTrack )
333 /* FIXME 0 indicates the first item,
334 * what to do if we're stopped, or empty ? */
335 dbus_int32_t i_position = 0;
336 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
337 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
339 if( p_playlist->status.p_item )
340 while ( p_tested_item && p_tested_item->p_input->i_id !=
341 p_playlist->status.p_item->p_input->i_id )
346 /* FIXME if p_tested_item is NULL at that point, what do we do ? */
347 pl_Release( p_playlist );
349 ADD_INT32( &i_position );
353 DBUS_METHOD( GetMetadata )
358 dbus_error_init( &error );
360 dbus_int32_t i_position, i_count = 0;
362 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
363 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
365 dbus_message_get_args( p_from, &error,
366 DBUS_TYPE_INT32, &i_position,
369 if( dbus_error_is_set( &error ) )
371 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
373 dbus_error_free( &error );
374 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
377 while ( p_tested_item && ( i_count < i_position ) )
384 GetInputMeta ( p_tested_item->p_input, &args );
386 pl_Release( p_playlist );
390 DBUS_METHOD( GetLength )
395 dbus_int32_t i_elements = 0;
396 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
397 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
398 playlist_item_t* p_last_item = playlist_GetLastLeaf( p_playlist,
399 p_playlist->p_root_onelevel );
401 while ( p_tested_item &&
402 ( p_tested_item->p_input->i_id != p_last_item->p_input->i_id ) )
408 pl_Release( p_playlist );
410 ADD_INT32( &i_elements );
414 DBUS_METHOD( DelTrack )
419 dbus_error_init( &error );
421 dbus_int32_t i_position, i_count = 0;
422 playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
423 playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
425 dbus_message_get_args( p_from, &error,
426 DBUS_TYPE_INT32, &i_position,
429 if( dbus_error_is_set( &error ) )
431 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
433 dbus_error_free( &error );
434 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
437 while ( p_tested_item && ( i_count < i_position ) )
446 playlist_DeleteFromInput( p_playlist,
447 p_tested_item->p_input->i_id,
452 pl_Release( p_playlist );
465 playlist_t* p_playlist = NULL;
467 dbus_error_init( &error );
468 dbus_message_get_args( p_from, &error,
469 DBUS_TYPE_BOOLEAN, &b_loop,
472 if( dbus_error_is_set( &error ) )
474 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
476 dbus_error_free( &error );
477 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
480 val.b_bool = ( b_loop == TRUE ) ? VLC_TRUE : VLC_FALSE ;
481 p_playlist = pl_Yield( (vlc_object_t*) p_this );
482 var_Set ( p_playlist, "loop", val );
483 pl_Release( ((vlc_object_t*) p_this) );
488 DBUS_METHOD( Repeat )
494 dbus_bool_t b_repeat;
496 playlist_t* p_playlist = NULL;
498 dbus_error_init( &error );
499 dbus_message_get_args( p_from, &error,
500 DBUS_TYPE_BOOLEAN, &b_repeat,
503 if( dbus_error_is_set( &error ) )
505 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
507 dbus_error_free( &error );
508 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
511 val.b_bool = ( b_repeat == TRUE ) ? VLC_TRUE : VLC_FALSE ;
513 p_playlist = pl_Yield( (vlc_object_t*) p_this );
514 var_Set ( p_playlist, "repeat", val );
515 pl_Release( ((vlc_object_t*) p_this) );
520 DBUS_METHOD( Random )
526 dbus_bool_t b_random;
528 playlist_t* p_playlist = NULL;
530 dbus_error_init( &error );
531 dbus_message_get_args( p_from, &error,
532 DBUS_TYPE_BOOLEAN, &b_random,
535 if( dbus_error_is_set( &error ) )
537 msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
539 dbus_error_free( &error );
540 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
543 val.b_bool = ( b_random == TRUE ) ? VLC_TRUE : VLC_FALSE ;
545 p_playlist = pl_Yield( (vlc_object_t*) p_this );
546 var_Set ( p_playlist, "random", val );
547 pl_Release( ((vlc_object_t*) p_this) );
551 /*****************************************************************************
552 * Introspection method
553 *****************************************************************************/
555 DBUS_METHOD( handle_introspect_root )
556 { /* handles introspection of root object */
559 ADD_STRING( &psz_introspection_xml_data_root );
563 DBUS_METHOD( handle_introspect_player )
567 ADD_STRING( &psz_introspection_xml_data_player );
571 DBUS_METHOD( handle_introspect_tracklist )
575 ADD_STRING( &psz_introspection_xml_data_tracklist );
579 /*****************************************************************************
580 * handle_*: answer to incoming messages
581 *****************************************************************************/
583 #define METHOD_FUNC( method, function ) \
584 else if( dbus_message_is_method_call( p_from, MPRIS_DBUS_INTERFACE, method ) )\
585 return function( p_conn, p_from, p_this )
587 DBUS_METHOD( handle_root )
590 if( dbus_message_is_method_call( p_from,
591 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
592 return handle_introspect_root( p_conn, p_from, p_this );
594 /* here D-Bus method's names are associated to an handler */
596 METHOD_FUNC( "Identity", Identity );
598 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
602 DBUS_METHOD( handle_player )
604 if( dbus_message_is_method_call( p_from,
605 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
606 return handle_introspect_player( p_conn, p_from, p_this );
608 /* here D-Bus method's names are associated to an handler */
610 METHOD_FUNC( "Prev", Prev );
611 METHOD_FUNC( "Next", Next );
612 METHOD_FUNC( "Quit", Quit );
613 METHOD_FUNC( "Stop", Stop );
614 METHOD_FUNC( "Play", Play );
615 METHOD_FUNC( "Pause", Pause );
616 METHOD_FUNC( "Repeat", Repeat );
617 METHOD_FUNC( "VolumeSet", VolumeSet );
618 METHOD_FUNC( "VolumeGet", VolumeGet );
619 METHOD_FUNC( "PositionSet", PositionSet );
620 METHOD_FUNC( "PositionGet", PositionGet );
621 METHOD_FUNC( "GetStatus", GetStatus );
622 METHOD_FUNC( "GetMetadata", GetCurrentMetadata );
624 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
627 DBUS_METHOD( handle_tracklist )
629 if( dbus_message_is_method_call( p_from,
630 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
631 return handle_introspect_tracklist( p_conn, p_from, p_this );
633 /* here D-Bus method's names are associated to an handler */
635 METHOD_FUNC( "GetMetadata", GetMetadata );
636 METHOD_FUNC( "GetCurrentTrack", GetCurrentTrack );
637 METHOD_FUNC( "GetLength", GetLength );
638 METHOD_FUNC( "AddTrack", AddTrack );
639 METHOD_FUNC( "DelTrack", DelTrack );
640 METHOD_FUNC( "Loop", Loop );
641 METHOD_FUNC( "Random", Random );
643 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
646 /*****************************************************************************
647 * Open: initialize interface
648 *****************************************************************************/
650 static int Open( vlc_object_t *p_this )
651 { /* initialisation of the connection */
652 intf_thread_t *p_intf = (intf_thread_t*)p_this;
653 intf_sys_t *p_sys = malloc( sizeof( intf_sys_t ) );
654 playlist_t *p_playlist;
655 DBusConnection *p_conn;
661 dbus_threads_init_default();
663 dbus_error_init( &error );
665 /* connect to the session bus */
666 p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
669 msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
671 dbus_error_free( &error );
676 /* register a well-known name on the bus */
677 dbus_bus_request_name( p_conn, VLC_MPRIS_DBUS_SERVICE, 0, &error );
678 if( dbus_error_is_set( &error ) )
680 msg_Err( p_this, "Error requesting service " VLC_MPRIS_DBUS_SERVICE
681 ": %s", error.message );
682 dbus_error_free( &error );
687 /* we register the objects */
688 dbus_connection_register_object_path( p_conn, MPRIS_DBUS_ROOT_PATH,
689 &vlc_dbus_root_vtable, p_this );
690 dbus_connection_register_object_path( p_conn, MPRIS_DBUS_PLAYER_PATH,
691 &vlc_dbus_player_vtable, p_this );
692 dbus_connection_register_object_path( p_conn, MPRIS_DBUS_TRACKLIST_PATH,
693 &vlc_dbus_tracklist_vtable, p_this );
695 dbus_connection_flush( p_conn );
697 p_playlist = pl_Yield( p_intf );
699 var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
701 pl_Release( p_playlist );
703 p_intf->pf_run = Run;
704 p_intf->p_sys = p_sys;
705 p_sys->p_conn = p_conn;
710 /*****************************************************************************
711 * Close: destroy interface
712 *****************************************************************************/
714 static void Close ( vlc_object_t *p_this )
716 intf_thread_t *p_intf = (intf_thread_t*) p_this;
717 playlist_t *p_playlist = pl_Yield( p_intf );;
720 var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
722 pl_Release( p_playlist );
724 dbus_connection_unref( p_intf->p_sys->p_conn );
726 free( p_intf->p_sys );
729 /*****************************************************************************
731 *****************************************************************************/
733 static void Run ( intf_thread_t *p_intf )
735 while( !p_intf->b_die )
737 msleep( INTF_IDLE_SLEEP );
738 dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
742 /*****************************************************************************
743 * TrackChange: Playlist item change callback
744 *****************************************************************************/
746 DBUS_SIGNAL( TrackChangeSignal )
747 { /* emit the metadata of the new item */
748 SIGNAL_INIT( "TrackChange" );
751 input_thread_t *p_input = (input_thread_t*) p_data;
752 GetInputMeta ( input_GetItem(p_input), &args );
757 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
758 vlc_value_t oldval, vlc_value_t newval, void *p_data )
760 intf_thread_t *p_intf = ( intf_thread_t* ) p_data;
761 intf_sys_t *p_sys = p_intf->p_sys;
762 playlist_t *p_playlist;
763 input_thread_t *p_input = NULL;
764 (void)p_this; (void)psz_var; (void)oldval; (void)newval;
766 p_playlist = pl_Yield( p_intf );
768 p_input = p_playlist->p_input;
773 pl_Release( p_playlist );
777 vlc_object_yield( p_input );
779 pl_Release( p_playlist );
781 TrackChangeSignal( p_sys->p_conn, p_input );
783 vlc_object_release( p_input );
787 /*****************************************************************************
788 * GetInputMeta: Fill a DBusMessage with the given input item metadata
789 *****************************************************************************/
791 #define ADD_META( entry, type, data ) \
793 dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
794 NULL, &dict_entry ); \
795 dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
796 &ppsz_meta_items[entry] ); \
797 dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
798 type##_AS_STRING, &variant ); \
799 dbus_message_iter_append_basic( &variant, \
802 dbus_message_iter_close_container( &dict_entry, &variant ); \
803 dbus_message_iter_close_container( &dict, &dict_entry ); }
805 #define ADD_VLC_META_STRING( entry, item ) \
807 char * psz = input_item_Get##item( p_input );\
808 ADD_META( entry, DBUS_TYPE_STRING, \
813 static int GetInputMeta( input_item_t* p_input,
814 DBusMessageIter *args )
815 { /*FIXME: Works only for already read metadata. */
817 DBusMessageIter dict, dict_entry, variant;
818 /* We need the track length to be expressed in seconds
819 * instead of milliseconds */
820 dbus_int64_t i_length = ( input_item_GetDuration( p_input ) / 1000 );
822 const char* ppsz_meta_items[] =
824 "title", "artist", "genre", "copyright", "album", "tracknum",
825 "description", "rating", "date", "setting", "url", "language",
826 "nowplaying", "publisher", "encodedby", "arturl", "trackid",
827 "status", "URI", "length", "video-codec", "audio-codec",
828 "video-bitrate", "audio-bitrate", "audio-samplerate"
831 dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
833 ADD_VLC_META_STRING( 0, Title );
834 ADD_VLC_META_STRING( 1, Artist );
835 ADD_VLC_META_STRING( 2, Genre );
836 ADD_VLC_META_STRING( 3, Copyright );
837 ADD_VLC_META_STRING( 4, Album );
838 ADD_VLC_META_STRING( 5, TrackNum );
839 ADD_VLC_META_STRING( 6, Description );
840 ADD_VLC_META_STRING( 7, Rating );
841 ADD_VLC_META_STRING( 8, Date );
842 ADD_VLC_META_STRING( 9, Setting );
843 ADD_VLC_META_STRING( 10, URL );
844 ADD_VLC_META_STRING( 11, Language );
845 ADD_VLC_META_STRING( 12, NowPlaying );
846 ADD_VLC_META_STRING( 13, Publisher );
847 ADD_VLC_META_STRING( 14, EncodedBy );
848 ADD_VLC_META_STRING( 15, ArtURL );
849 ADD_VLC_META_STRING( 16, TrackID );
851 vlc_mutex_lock( &p_input->lock );
852 if( p_input->p_meta )
853 ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status );
854 vlc_mutex_unlock( &p_input->lock );
856 ADD_VLC_META_STRING( 18, URI );
857 ADD_META( 19, DBUS_TYPE_INT64, i_length );
859 dbus_message_iter_close_container( args, &dict );
864 #undef ADD_VLC_META_STRING