X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fcontrol%2Fdbus.c;h=f2dd61ac8bf7b7013040240969956c8042ce87d8;hb=3a6282755277ba9321d405c635e50da935d258a6;hp=79918e091ebcc99f2e6b62326955762f9ea5f1f7;hpb=9025fab9931ecc35fe247b0a63d0cea166dee88d;p=vlc diff --git a/modules/control/dbus.c b/modules/control/dbus.c index 79918e091e..f2dd61ac8b 100644 --- a/modules/control/dbus.c +++ b/modules/control/dbus.c @@ -1,7 +1,8 @@ /***************************************************************************** * dbus.c : D-Bus control interface ***************************************************************************** - * Copyright (C) 2006 Rafaël Carré + * Copyright © 2006-2008 Rafaël Carré + * Copyright © 2007 Mirsal Ennaime * $Id$ * * Authors: Rafaël Carré @@ -27,18 +28,11 @@ * http://dbus.freedesktop.org/doc/dbus-specification.html * D-Bus low-level C API (libdbus) * http://dbus.freedesktop.org/doc/dbus/api/html/index.html - */ - -/* - * TODO: - * properties ? - * - * macros to read incoming arguments - * - * explore different possible types (arrays..) + * extract: + * "If you use this low-level API directly, you're signing up for some pain." * - * what must we do if org.videolan.vlc already exist on the bus ? - * ( there is more than one vlc instance ) + * MPRIS Specification (still drafting on Jan, 23 of 2008): + * http://wiki.xmms2.xmms.se/index.php/MPRIS */ /***************************************************************************** @@ -46,19 +40,21 @@ *****************************************************************************/ #include -#include -#include -#include #include "dbus.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + #include #include #include #include #include #include - +#include +#include /***************************************************************************** * Local prototypes. @@ -66,18 +62,37 @@ static int Open ( vlc_object_t * ); static void Close ( vlc_object_t * ); -static void Run ( intf_thread_t * ); +static void Run ( intf_thread_t * ); +static int StateChange( vlc_object_t *, const char *, vlc_value_t, + vlc_value_t, void * ); -static int TrackChange( vlc_object_t *p_this, const char *psz_var, - vlc_value_t oldval, vlc_value_t newval, void *p_data ); +static int TrackChange( vlc_object_t *, const char *, vlc_value_t, + vlc_value_t, void * ); -static int GetInputMeta ( input_item_t *p_input, - DBusMessageIter *args); +static int StatusChangeEmit( vlc_object_t *, const char *, vlc_value_t, + vlc_value_t, void * ); + +static int GetInputMeta ( input_item_t *, DBusMessageIter * ); +static int MarshalStatus ( intf_thread_t *, DBusMessageIter *, vlc_bool_t ); + +/* GetCaps() capabilities */ +enum +{ + CAPS_NONE = 0, + CAPS_CAN_GO_NEXT = 1 << 0, + CAPS_CAN_GO_PREV = 1 << 1, + CAPS_CAN_PAUSE = 1 << 2, + CAPS_CAN_PLAY = 1 << 3, + CAPS_CAN_SEEK = 1 << 4, + CAPS_CAN_PROVIDE_METADATA = 1 << 5 +}; struct intf_sys_t { DBusConnection *p_conn; + vlc_bool_t b_meta_read; + dbus_int32_t i_caps; }; /***************************************************************************** @@ -96,58 +111,6 @@ vlc_module_end(); /***************************************************************************** * Methods *****************************************************************************/ -#if 0 -DBUS_METHOD( PlaylistExport_XSPF ) -{ /*export playlist to an xspf file */ - - /* reads the filename to export to */ - /* returns the status as int32: - * 0 : success - * 1 : error - * 2 : playlist empty - */ - REPLY_INIT; - OUT_ARGUMENTS; - - DBusError error; - dbus_error_init( &error ); - - char *psz_file; - dbus_int32_t i_ret; - - dbus_message_get_args( p_from, &error, - DBUS_TYPE_STRING, &psz_file, - DBUS_TYPE_INVALID ); - - if( dbus_error_is_set( &error ) ) - { - msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n", - error.message ); - dbus_error_free( &error ); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - - playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this ); - - if( ( !playlist_IsEmpty( p_playlist ) ) && - ( p_playlist->p_root_category->i_children > 0 ) ) - { - if( playlist_Export( p_playlist, psz_file, - p_playlist->p_root_category->pp_children[0], - "export-xspf" ) == VLC_SUCCESS ) - i_ret = 0; - else - i_ret = 1; - } - else - i_ret = 2; - - pl_Release( ((vlc_object_t*) p_this ) ); - - ADD_INT32( &i_ret ); - REPLY_SEND; -} -#endif /* Player */ @@ -162,32 +125,35 @@ DBUS_METHOD( Quit ) } DBUS_METHOD( PositionGet ) -{ /* returns position as an int in the range [0;1000] */ +{ /* returns position in milliseconds */ REPLY_INIT; OUT_ARGUMENTS; vlc_value_t position; dbus_int32_t i_pos; playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) ); + PL_LOCK; input_thread_t *p_input = p_playlist->p_input; if( !p_input ) i_pos = 0; else { - var_Get( p_input, "position", &position ); - i_pos = position.f_float * 1000 ; + var_Get( p_input, "time", &position ); + i_pos = position.i_time / 1000; } - ADD_INT32( &i_pos ); + PL_UNLOCK; pl_Release( ((vlc_object_t*) p_this) ); + ADD_INT32( &i_pos ); REPLY_SEND; } DBUS_METHOD( PositionSet ) -{ /* set position from an int in the range [0;1000] */ +{ /* set position in milliseconds */ REPLY_INIT; vlc_value_t position; + playlist_t* p_playlist = NULL; dbus_int32_t i_pos; DBusError error; @@ -204,14 +170,16 @@ DBUS_METHOD( PositionSet ) dbus_error_free( &error ); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) ); + p_playlist = pl_Yield( ((vlc_object_t*) p_this) ); + PL_LOCK; input_thread_t *p_input = p_playlist->p_input; if( p_input ) { - position.f_float = ((float)i_pos) / 1000; - var_Set( p_input, "position", position ); + position.i_time = i_pos * 1000; + var_Set( p_input, "time", position ); } + PL_UNLOCK; pl_Release( ((vlc_object_t*) p_this) ); REPLY_SEND; } @@ -285,29 +253,18 @@ DBUS_METHOD( Stop ) } DBUS_METHOD( GetStatus ) -{ /* returns an int: 0=playing 1=paused 2=stopped */ +{ /* 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 * + */ REPLY_INIT; OUT_ARGUMENTS; - dbus_int32_t i_status; - vlc_value_t val; - - playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this ); - input_thread_t *p_input = p_playlist->p_input; - - i_status = 2; - if( p_input ) - { - var_Get( p_input, "state", &val ); - if( val.i_int == PAUSE_S ) - i_status = 1; - else if( val.i_int == PLAYING_S ) - i_status = 0; - } + MarshalStatus( p_this, &args, VLC_TRUE ); - pl_Release( p_playlist ); - - ADD_INT32( &i_status ); REPLY_SEND; } @@ -329,16 +286,87 @@ DBUS_METHOD( Play ) REPLY_SEND; } +DBUS_METHOD( GetCurrentMetadata ) +{ + REPLY_INIT; + OUT_ARGUMENTS; + playlist_t* p_playlist = pl_Yield( (vlc_object_t*) p_this ); + PL_LOCK; + if( p_playlist->status.p_item ) + GetInputMeta( p_playlist->status.p_item->p_input, &args ); + PL_UNLOCK; + pl_Release( p_playlist ); + REPLY_SEND; +} + +DBUS_METHOD( GetCaps ) +{ + REPLY_INIT; + OUT_ARGUMENTS; + playlist_t* p_playlist = pl_Yield( (vlc_object_t*) p_this ); + PL_LOCK; + + dbus_int32_t i_caps = CAPS_NONE; + + /* FIXME: + * Every capability should be checked in a callback, modifying p_sys->i_caps + * so we can send a signal whenever it changes. + * When it is done, GetCaps method will just return p_sys->i_caps + */ + + if( p_playlist->items.i_size > 0 ) + i_caps |= CAPS_CAN_PLAY | CAPS_CAN_GO_PREV | CAPS_CAN_GO_NEXT; + + if( p_playlist->p_input ) + { + access_t *p_access = (access_t*)vlc_object_find( p_playlist->p_input, + VLC_OBJECT_ACCESS, FIND_CHILD ); + if( p_access ) + { + vlc_bool_t b_can_pause; + if( !access2_Control( p_access, ACCESS_CAN_PAUSE, &b_can_pause ) && + b_can_pause ) + i_caps |= CAPS_CAN_PAUSE; + vlc_object_release( p_access ); + } + demux_t *p_demux = (demux_t*)vlc_object_find( p_playlist->p_input, + VLC_OBJECT_DEMUX, FIND_CHILD ); + if( p_demux ) + { /* XXX: is: demux can seek and access can not a possibility ? */ + vlc_bool_t b_can_seek; + if( !stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek ) && + b_can_seek ) + i_caps |= CAPS_CAN_SEEK; + vlc_object_release( p_demux ); + } + } + + if( ((intf_thread_t*)p_this)->p_sys->b_meta_read ) + i_caps |= CAPS_CAN_PROVIDE_METADATA; + + PL_UNLOCK; + + ADD_INT32( &i_caps ); + + REPLY_SEND; +} + /* Media Player information */ DBUS_METHOD( Identity ) { + VLC_UNUSED(p_this); REPLY_INIT; OUT_ARGUMENTS; - char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 1 ); - sprintf( psz_identity, "%s %s", PACKAGE, VERSION ); - ADD_STRING( &psz_identity ); - free( psz_identity ); + char *psz_identity; + if( asprintf( &psz_identity, "%s %s", PACKAGE, VERSION ) != -1 ) + { + ADD_STRING( &psz_identity ); + free( psz_identity ); + } + else + return DBUS_HANDLER_RESULT_NEED_MEMORY; + REPLY_SEND; } @@ -350,6 +378,7 @@ DBUS_METHOD( AddTrack ) DBusError error; dbus_error_init( &error ); + playlist_t* p_playlist = NULL; char *psz_mrl; dbus_bool_t b_play; @@ -367,10 +396,10 @@ DBUS_METHOD( AddTrack ) return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this ); + p_playlist = pl_Yield( (vlc_object_t*) p_this ); playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND | - ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) , - PLAYLIST_END, VLC_TRUE, VLC_FALSE ); + ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) , + PLAYLIST_END, VLC_TRUE, VLC_FALSE ); pl_Release( p_playlist ); REPLY_SEND; @@ -380,17 +409,9 @@ DBUS_METHOD( GetCurrentTrack ) { REPLY_INIT; OUT_ARGUMENTS; - dbus_int32_t i_position = 0; - playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this ); - playlist_item_t* p_tested_item = p_playlist->p_root_onelevel; - - while ( p_tested_item->p_input->i_id != - p_playlist->status.p_item->p_input->i_id ) - { - i_position++; - TEST_NEXT; - } + playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this ); + dbus_int32_t i_position = p_playlist->i_current_index; pl_Release( p_playlist ); ADD_INT32( &i_position ); @@ -398,60 +419,50 @@ DBUS_METHOD( GetCurrentTrack ) } DBUS_METHOD( GetMetadata ) -{ +{ REPLY_INIT; OUT_ARGUMENTS; DBusError error; dbus_error_init( &error ); - dbus_int32_t i_position, i_count = 0; - + dbus_int32_t i_position; + playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this ); - playlist_item_t* p_tested_item = p_playlist->p_root_onelevel; + PL_LOCK; dbus_message_get_args( p_from, &error, - DBUS_TYPE_INT32, &i_position, - DBUS_TYPE_INVALID ); + DBUS_TYPE_INT32, &i_position, + DBUS_TYPE_INVALID ); if( dbus_error_is_set( &error ) ) { + PL_UNLOCK; + pl_Release( p_playlist ); msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n", error.message ); dbus_error_free( &error ); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - - while ( i_count < i_position ) + + if( i_position <= p_playlist->items.i_size / 2 ) { - i_count++; - TEST_NEXT; + GetInputMeta( p_playlist->items.p_elems[i_position*2-1]->p_input, &args ); } - GetInputMeta ( p_tested_item->p_input, &args ); - + PL_UNLOCK; pl_Release( p_playlist ); REPLY_SEND; } DBUS_METHOD( GetLength ) -{ +{ REPLY_INIT; OUT_ARGUMENTS; - dbus_int32_t i_elements = 0; playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this ); - playlist_item_t* p_tested_item = p_playlist->p_root_onelevel; - playlist_item_t* p_last_item = playlist_GetLastLeaf( p_playlist, - p_playlist->p_root_onelevel ); - - while ( p_tested_item->p_input->i_id != p_last_item->p_input->i_id ) - { - i_elements++; - TEST_NEXT; - } - + dbus_int32_t i_elements = p_playlist->items.i_size / 2; pl_Release( p_playlist ); - + ADD_INT32( &i_elements ); REPLY_SEND; } @@ -463,9 +474,8 @@ DBUS_METHOD( DelTrack ) DBusError error; dbus_error_init( &error ); - dbus_int32_t i_position, i_count = 0; + dbus_int32_t i_position; playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this ); - playlist_item_t* p_tested_item = p_playlist->p_root_onelevel; dbus_message_get_args( p_from, &error, DBUS_TYPE_INT32, &i_position, @@ -478,30 +488,120 @@ DBUS_METHOD( DelTrack ) dbus_error_free( &error ); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } - - while ( i_count < i_position ) + + if( i_position <= p_playlist->items.i_size / 2 ) { - i_count++; - TEST_NEXT; + playlist_DeleteFromInput( p_playlist, + p_playlist->items.p_elems[i_position*2-1]->i_id, + VLC_FALSE ); } - PL_LOCK; - playlist_DeleteFromInput( p_playlist, - p_tested_item->p_input->i_id, - VLC_TRUE ); - PL_UNLOCK; - pl_Release( p_playlist ); - + REPLY_SEND; } +DBUS_METHOD( Loop ) +{ + REPLY_INIT; + OUT_ARGUMENTS; + + DBusError error; + dbus_bool_t b_loop; + vlc_value_t val; + playlist_t* p_playlist = NULL; + + dbus_error_init( &error ); + dbus_message_get_args( p_from, &error, + DBUS_TYPE_BOOLEAN, &b_loop, + DBUS_TYPE_INVALID ); + + if( dbus_error_is_set( &error ) ) + { + msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n", + error.message ); + dbus_error_free( &error ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + val.b_bool = ( b_loop == TRUE ) ? VLC_TRUE : VLC_FALSE ; + p_playlist = pl_Yield( (vlc_object_t*) p_this ); + var_Set ( p_playlist, "loop", val ); + pl_Release( ((vlc_object_t*) p_this) ); + + REPLY_SEND; +} + +DBUS_METHOD( Repeat ) +{ + REPLY_INIT; + OUT_ARGUMENTS; + + DBusError error; + dbus_bool_t b_repeat; + vlc_value_t val; + playlist_t* p_playlist = NULL; + + dbus_error_init( &error ); + dbus_message_get_args( p_from, &error, + DBUS_TYPE_BOOLEAN, &b_repeat, + DBUS_TYPE_INVALID ); + + if( dbus_error_is_set( &error ) ) + { + msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n", + error.message ); + dbus_error_free( &error ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + val.b_bool = ( b_repeat == TRUE ) ? VLC_TRUE : VLC_FALSE ; + + p_playlist = pl_Yield( (vlc_object_t*) p_this ); + var_Set ( p_playlist, "repeat", val ); + pl_Release( ((vlc_object_t*) p_this) ); + + REPLY_SEND; +} + +DBUS_METHOD( Random ) +{ + REPLY_INIT; + OUT_ARGUMENTS; + + DBusError error; + dbus_bool_t b_random; + vlc_value_t val; + playlist_t* p_playlist = NULL; + + dbus_error_init( &error ); + dbus_message_get_args( p_from, &error, + DBUS_TYPE_BOOLEAN, &b_random, + DBUS_TYPE_INVALID ); + + if( dbus_error_is_set( &error ) ) + { + msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n", + error.message ); + dbus_error_free( &error ); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + val.b_bool = ( b_random == TRUE ) ? VLC_TRUE : VLC_FALSE ; + + p_playlist = pl_Yield( (vlc_object_t*) p_this ); + var_Set ( p_playlist, "random", val ); + pl_Release( ((vlc_object_t*) p_this) ); + + REPLY_SEND; +} /***************************************************************************** * Introspection method *****************************************************************************/ DBUS_METHOD( handle_introspect_root ) -{ /* handles introspection of /org/videolan/vlc */ +{ /* handles introspection of root object */ + VLC_UNUSED(p_this); REPLY_INIT; OUT_ARGUMENTS; ADD_STRING( &psz_introspection_xml_data_root ); @@ -510,6 +610,7 @@ DBUS_METHOD( handle_introspect_root ) DBUS_METHOD( handle_introspect_player ) { + VLC_UNUSED(p_this); REPLY_INIT; OUT_ARGUMENTS; ADD_STRING( &psz_introspection_xml_data_player ); @@ -518,6 +619,7 @@ DBUS_METHOD( handle_introspect_player ) DBUS_METHOD( handle_introspect_tracklist ) { + VLC_UNUSED(p_this); REPLY_INIT; OUT_ARGUMENTS; ADD_STRING( &psz_introspection_xml_data_tracklist ); @@ -529,7 +631,7 @@ DBUS_METHOD( handle_introspect_tracklist ) *****************************************************************************/ #define METHOD_FUNC( method, function ) \ - else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\ + else if( dbus_message_is_method_call( p_from, MPRIS_DBUS_INTERFACE, method ) )\ return function( p_conn, p_from, p_this ) DBUS_METHOD( handle_root ) @@ -551,7 +653,7 @@ DBUS_METHOD( handle_player ) { if( dbus_message_is_method_call( p_from, DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) ) - return handle_introspect_player( p_conn, p_from, p_this ); + return handle_introspect_player( p_conn, p_from, p_this ); /* here D-Bus method's names are associated to an handler */ @@ -561,11 +663,14 @@ DBUS_METHOD( handle_player ) METHOD_FUNC( "Stop", Stop ); METHOD_FUNC( "Play", Play ); METHOD_FUNC( "Pause", Pause ); + METHOD_FUNC( "Repeat", Repeat ); METHOD_FUNC( "VolumeSet", VolumeSet ); METHOD_FUNC( "VolumeGet", VolumeGet ); METHOD_FUNC( "PositionSet", PositionSet ); METHOD_FUNC( "PositionGet", PositionGet ); METHOD_FUNC( "GetStatus", GetStatus ); + METHOD_FUNC( "GetMetadata", GetCurrentMetadata ); + METHOD_FUNC( "GetCaps", GetCaps ); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -583,6 +688,8 @@ DBUS_METHOD( handle_tracklist ) METHOD_FUNC( "GetLength", GetLength ); METHOD_FUNC( "AddTrack", AddTrack ); METHOD_FUNC( "DelTrack", DelTrack ); + METHOD_FUNC( "Loop", Loop ); + METHOD_FUNC( "Random", Random ); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -602,7 +709,8 @@ static int Open( vlc_object_t *p_this ) if( !p_sys ) return VLC_ENOMEM; - dbus_threads_init_default(); + p_sys->b_meta_read = VLC_FALSE; + p_sys->i_caps = CAPS_NONE; dbus_error_init( &error ); @@ -618,21 +726,22 @@ static int Open( vlc_object_t *p_this ) } /* register a well-known name on the bus */ - dbus_bus_request_name( p_conn, "org.freedesktop.MediaPlayer", 0, &error ); + dbus_bus_request_name( p_conn, VLC_MPRIS_DBUS_SERVICE, 0, &error ); if( dbus_error_is_set( &error ) ) { - msg_Err( p_this, "Error requesting org.freedesktop.MediaPlayer service:" " %s\n", error.message ); + msg_Err( p_this, "Error requesting service " VLC_MPRIS_DBUS_SERVICE + ": %s", error.message ); dbus_error_free( &error ); free( p_sys ); return VLC_EGENERIC; } /* we register the objects */ - dbus_connection_register_object_path( p_conn, VLC_DBUS_ROOT_PATH, + dbus_connection_register_object_path( p_conn, MPRIS_DBUS_ROOT_PATH, &vlc_dbus_root_vtable, p_this ); - dbus_connection_register_object_path( p_conn, VLC_DBUS_PLAYER_PATH, + dbus_connection_register_object_path( p_conn, MPRIS_DBUS_PLAYER_PATH, &vlc_dbus_player_vtable, p_this ); - dbus_connection_register_object_path( p_conn, VLC_DBUS_TRACKLIST_PATH, + dbus_connection_register_object_path( p_conn, MPRIS_DBUS_TRACKLIST_PATH, &vlc_dbus_tracklist_vtable, p_this ); dbus_connection_flush( p_conn ); @@ -640,6 +749,9 @@ static int Open( vlc_object_t *p_this ) p_playlist = pl_Yield( p_intf ); PL_LOCK; var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf ); + var_AddCallback( p_playlist, "random", StatusChangeEmit, p_intf ); + var_AddCallback( p_playlist, "repeat", StatusChangeEmit, p_intf ); + var_AddCallback( p_playlist, "loop", StatusChangeEmit, p_intf ); PL_UNLOCK; pl_Release( p_playlist ); @@ -658,9 +770,24 @@ static void Close ( vlc_object_t *p_this ) { intf_thread_t *p_intf = (intf_thread_t*) p_this; playlist_t *p_playlist = pl_Yield( p_intf );; + input_thread_t *p_input; + + p_this->b_dead = VLC_TRUE; PL_LOCK; var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf ); + var_DelCallback( p_playlist, "random", StatusChangeEmit, p_intf ); + var_DelCallback( p_playlist, "repeat", StatusChangeEmit, p_intf ); + var_DelCallback( p_playlist, "loop", StatusChangeEmit, p_intf ); + + p_input = p_playlist->p_input; + if ( p_input ) + { + vlc_object_yield( p_input ); + var_DelCallback( p_input, "state", StateChange, p_intf ); + vlc_object_release( p_input ); + } + PL_UNLOCK; pl_Release( p_playlist ); @@ -675,7 +802,7 @@ static void Close ( vlc_object_t *p_this ) static void Run ( intf_thread_t *p_intf ) { - while( !p_intf->b_die ) + while( !intf_ShouldDie( p_intf ) ) { msleep( INTF_IDLE_SLEEP ); dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 ); @@ -691,12 +818,80 @@ DBUS_SIGNAL( TrackChangeSignal ) SIGNAL_INIT( "TrackChange" ); OUT_ARGUMENTS; - input_thread_t *p_input = (input_thread_t*) p_data; - GetInputMeta ( input_GetItem(p_input), &args ); + input_item_t *p_item = (input_item_t*) p_data; + GetInputMeta ( p_item, &args ); SIGNAL_SEND; } +/***************************************************************************** + * StatusChange: Player status change signal + *****************************************************************************/ + +DBUS_SIGNAL( StatusChangeSignal ) +{ /* send the updated status info on the bus */ + SIGNAL_INIT( "StatusChange" ); + 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, VLC_FALSE ); + + SIGNAL_SEND; +} + +/***************************************************************************** + * StateChange: callback on input "state" + *****************************************************************************/ +static int StateChange( vlc_object_t *p_this, const char* psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + VLC_UNUSED(psz_var); VLC_UNUSED(oldval); + intf_thread_t *p_intf = ( intf_thread_t* ) p_data; + intf_sys_t *p_sys = p_intf->p_sys; + + if( p_intf->b_dead ) + return VLC_SUCCESS; + + if( !p_sys->b_meta_read && newval.i_int == PLAYING_S ) + { + input_item_t *p_item = input_GetItem( (input_thread_t*)p_this ); + if( p_item ) + { + p_sys->b_meta_read = VLC_TRUE; + TrackChangeSignal( p_sys->p_conn, p_item ); + } + } + + if( newval.i_int == PLAYING_S || newval.i_int == PAUSE_S || + newval.i_int == END_S ) + { + StatusChangeSignal( p_sys->p_conn, (void*) p_intf ); + } + + return VLC_SUCCESS; +} + +/***************************************************************************** + * StatusChangeEmit: Emits the StatusChange signal + *****************************************************************************/ +static int StatusChangeEmit( vlc_object_t *p_this, const char *psz_var, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + VLC_UNUSED(p_this); VLC_UNUSED(psz_var); + VLC_UNUSED(oldval); VLC_UNUSED(newval); + intf_thread_t *p_intf = p_data; + + if( p_intf->b_dead ) + return VLC_SUCCESS; + + StatusChangeSignal( p_intf->p_sys->p_conn, p_data ); + return VLC_SUCCESS; +} + +/***************************************************************************** + * TrackChange: callback on playlist "playlist-current" + *****************************************************************************/ static int TrackChange( vlc_object_t *p_this, const char *psz_var, vlc_value_t oldval, vlc_value_t newval, void *p_data ) { @@ -704,7 +899,14 @@ static int TrackChange( vlc_object_t *p_this, const char *psz_var, intf_sys_t *p_sys = p_intf->p_sys; playlist_t *p_playlist; input_thread_t *p_input = NULL; - (void)p_this; (void)psz_var; (void)oldval; (void)newval; + input_item_t *p_item = NULL; + VLC_UNUSED( p_this ); VLC_UNUSED( psz_var ); + VLC_UNUSED( oldval ); VLC_UNUSED( newval ); + + if( p_intf->b_dead ) + return VLC_SUCCESS; + + p_sys->b_meta_read = VLC_FALSE; p_playlist = pl_Yield( p_intf ); PL_LOCK; @@ -721,7 +923,53 @@ static int TrackChange( vlc_object_t *p_this, const char *psz_var, PL_UNLOCK; pl_Release( p_playlist ); - TrackChangeSignal( p_sys->p_conn, p_input ); + p_item = input_GetItem( p_input ); + if( !p_item ) + { + vlc_object_release( p_input ); + return VLC_EGENERIC; + } + + if( input_item_IsPreparsed( p_item ) ) + { + p_sys->b_meta_read = VLC_TRUE; + TrackChangeSignal( p_sys->p_conn, p_item ); + } + + dbus_int32_t i_caps = CAPS_NONE; + + if( p_playlist->items.i_size > 0 ) + i_caps |= CAPS_CAN_PLAY | CAPS_CAN_GO_PREV | CAPS_CAN_GO_NEXT; + + if( p_playlist->p_input ) + { + access_t *p_access = (access_t*)vlc_object_find( p_playlist->p_input, + VLC_OBJECT_ACCESS, FIND_CHILD ); + if( p_access ) + { + vlc_bool_t b_can_pause; + if( !access2_Control( p_access, ACCESS_CAN_PAUSE, &b_can_pause ) && + b_can_pause ) + i_caps |= CAPS_CAN_PAUSE; + vlc_object_release( p_access ); + } + demux_t *p_demux = (demux_t*)vlc_object_find( p_playlist->p_input, + VLC_OBJECT_DEMUX, FIND_CHILD ); + if( p_demux ) + { /* XXX: is: demux can seek and access can not a possibility ? */ + vlc_bool_t b_can_seek; + if( !stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek ) && + b_can_seek ) + i_caps |= CAPS_CAN_SEEK; + vlc_object_release( p_demux ); + } + } + + if( ((intf_thread_t*)p_this)->p_sys->b_meta_read ) + i_caps |= CAPS_CAN_PROVIDE_METADATA; + + + var_AddCallback( p_input, "state", StateChange, p_intf ); vlc_object_release( p_input ); return VLC_SUCCESS; @@ -737,26 +985,31 @@ static int TrackChange( vlc_object_t *p_this, const char *psz_var, 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, \ + dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \ type##_AS_STRING, &variant ); \ dbus_message_iter_append_basic( &variant, \ type, \ & data ); \ dbus_message_iter_close_container( &dict_entry, &variant ); \ - dbus_message_iter_close_container( &dict, &dict_entry ); }\ + dbus_message_iter_close_container( &dict, &dict_entry ); } #define ADD_VLC_META_STRING( entry, item ) \ + { \ + char * psz = input_item_Get##item( p_input );\ ADD_META( entry, DBUS_TYPE_STRING, \ - p_input->p_meta->psz_##item ); + psz ); \ + free( psz ); \ + } -static int GetInputMeta( input_item_t* p_input, +static int GetInputMeta( input_item_t* p_input, DBusMessageIter *args ) -{ /*FIXME: Works only for already read metadata. */ - /*FIXME: Should return the length in seconds rather than milliseconds */ - +{ DBusMessageIter dict, dict_entry, variant; + /* We need the track length to be expressed in milli-seconds + * instead of µ-seconds */ + dbus_int64_t i_length = ( input_item_GetDuration( p_input ) / 1000 ); - const char* ppsz_meta_items[] = + const char* ppsz_meta_items[] = { "title", "artist", "genre", "copyright", "album", "tracknum", "description", "rating", "date", "setting", "url", "language", @@ -764,30 +1017,34 @@ static int GetInputMeta( input_item_t* p_input, "status", "URI", "length", "video-codec", "audio-codec", "video-bitrate", "audio-bitrate", "audio-samplerate" }; - + dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict ); - ADD_VLC_META_STRING( 0, title ); - ADD_VLC_META_STRING( 1, artist ); - ADD_VLC_META_STRING( 2, genre ); - ADD_VLC_META_STRING( 3, copyright ); - ADD_VLC_META_STRING( 4, album ); - ADD_VLC_META_STRING( 5, tracknum ); - ADD_VLC_META_STRING( 6, description ); - ADD_VLC_META_STRING( 7, rating ); - ADD_VLC_META_STRING( 8, date ); - ADD_VLC_META_STRING( 9, setting ); - ADD_VLC_META_STRING( 10, url ); - ADD_VLC_META_STRING( 11, language ); - ADD_VLC_META_STRING( 12, nowplaying ); - ADD_VLC_META_STRING( 13, publisher ); - ADD_VLC_META_STRING( 14, encodedby ); - ADD_VLC_META_STRING( 15, arturl ); - ADD_VLC_META_STRING( 16, trackid ); - - ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status ); - ADD_META( 18, DBUS_TYPE_STRING, p_input->psz_uri ); - ADD_META( 19, DBUS_TYPE_INT64, p_input->i_duration ); + ADD_VLC_META_STRING( 0, Title ); + ADD_VLC_META_STRING( 1, Artist ); + ADD_VLC_META_STRING( 2, Genre ); + ADD_VLC_META_STRING( 3, Copyright ); + ADD_VLC_META_STRING( 4, Album ); + ADD_VLC_META_STRING( 5, TrackNum ); + ADD_VLC_META_STRING( 6, Description ); + ADD_VLC_META_STRING( 7, Rating ); + ADD_VLC_META_STRING( 8, Date ); + ADD_VLC_META_STRING( 9, Setting ); + ADD_VLC_META_STRING( 10, URL ); + ADD_VLC_META_STRING( 11, Language ); + ADD_VLC_META_STRING( 12, NowPlaying ); + ADD_VLC_META_STRING( 13, Publisher ); + ADD_VLC_META_STRING( 14, EncodedBy ); + ADD_VLC_META_STRING( 15, ArtURL ); + ADD_VLC_META_STRING( 16, TrackID ); + + vlc_mutex_lock( &p_input->lock ); + if( p_input->p_meta ) + ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status ); + vlc_mutex_unlock( &p_input->lock ); + + ADD_VLC_META_STRING( 18, URI ); + ADD_META( 19, DBUS_TYPE_INT64, i_length ); dbus_message_iter_close_container( args, &dict ); return VLC_SUCCESS; @@ -795,3 +1052,60 @@ static int GetInputMeta( input_item_t* p_input, #undef ADD_META #undef ADD_VLC_META_STRING + +/***************************************************************************** + * MarshalStatus: Fill a DBusMessage with the current player status + *****************************************************************************/ + +static int MarshalStatus( intf_thread_t* p_intf, DBusMessageIter* args, + vlc_bool_t lock ) +{ /* 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; + vlc_value_t val; + playlist_t* p_playlist = NULL; + input_thread_t* p_input = NULL; + + p_playlist = pl_Yield( (vlc_object_t*) p_intf ); + if( lock ) + PL_LOCK; + + i_state = 2; + + p_input = p_playlist->p_input; + if( p_input ) + { + var_Get( p_input, "state", &val ); + if( val.i_int >= END_S ) + i_state = 2; + else if( val.i_int == PAUSE_S ) + i_state = 1; + else if( val.i_int <= PLAYING_S ) + i_state = 0; + } + + var_Get( p_playlist, "random", &val ); + i_random = val.i_int; + + var_Get( p_playlist, "repeat", &val ); + i_repeat = val.i_int; + + var_Get( p_playlist, "loop", &val ); + i_loop = val.i_int; + + if( lock ) + PL_UNLOCK; + pl_Release( p_playlist ); + + 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 ); + + return VLC_SUCCESS; +}