]> git.sesse.net Git - vlc/blob - modules/control/dbus/dbus_player.c
Do not include vlc_fixups, config.h is already doing it.
[vlc] / modules / control / dbus / dbus_player.c
1 /*****************************************************************************
2  * dbus-player.h : dbus control module (mpris v1.0) - /Player object
3  *****************************************************************************
4  * Copyright © 2006-2008 Rafaël Carré
5  * Copyright © 2007-2010 Mirsal Ennaime
6  * Copyright © 2009-2010 The VideoLAN team
7  * $Id$
8  *
9  * Authors:    Mirsal Ennaime <mirsal at mirsal fr>
10  *             Rafaël Carré <funman at videolanorg>
11  *
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.
16  *
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.
21  *
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  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_playlist.h>
33 #include <vlc_interface.h>
34 #include <vlc_aout.h>
35
36 #include <math.h>
37
38 #include "dbus_player.h"
39 #include "dbus_common.h"
40
41 static int MarshalStatus ( intf_thread_t *, DBusMessageIter * );
42
43 /* XML data to answer org.freedesktop.DBus.Introspectable.Introspect requests */
44 static const char* psz_player_introspection_xml =
45 "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\"\n"
46 "\"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">\n"
47 "<node>"
48 "  <interface name=\"org.freedesktop.DBus.Introspectable\">\n"
49 "    <method name=\"Introspect\">\n"
50 "      <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"
51 "    </method>\n"
52 "  </interface>\n"
53 "  <interface name=\"org.freedesktop.MediaPlayer\">\n"
54 "    <method name=\"GetStatus\">\n"
55 "      <arg type=\"(iiii)\" direction=\"out\" />\n"
56 "    </method>\n"
57 "    <method name=\"Prev\">\n"
58 "    </method>\n"
59 "    <method name=\"Next\">\n"
60 "    </method>\n"
61 "    <method name=\"Stop\">\n"
62 "    </method>\n"
63 "    <method name=\"Play\">\n"
64 "    </method>\n"
65 "    <method name=\"Pause\">\n"
66 "    </method>\n"
67 "    <method name=\"Repeat\">\n"
68 "      <arg type=\"b\" direction=\"in\" />\n"
69 "    </method>\n"
70 "    <method name=\"VolumeSet\">\n"
71 "      <arg type=\"i\" direction=\"in\" />\n"
72 "    </method>\n"
73 "    <method name=\"VolumeGet\">\n"
74 "      <arg type=\"i\" direction=\"out\" />\n"
75 "    </method>\n"
76 "    <method name=\"PositionSet\">\n"
77 "      <arg type=\"i\" direction=\"in\" />\n"
78 "    </method>\n"
79 "    <method name=\"PositionGet\">\n"
80 "      <arg type=\"i\" direction=\"out\" />\n"
81 "    </method>\n"
82 "    <method name=\"GetMetadata\">\n"
83 "      <arg type=\"a{sv}\" direction=\"out\" />\n"
84 "    </method>\n"
85 "    <method name=\"GetCaps\">\n"
86 "      <arg type=\"i\" direction=\"out\" />\n"
87 "    </method>\n"
88 "    <signal name=\"TrackChange\">\n"
89 "      <arg type=\"a{sv}\"/>\n"
90 "    </signal>\n"
91 "    <signal name=\"StatusChange\">\n"
92 "      <arg type=\"(iiii)\"/>\n"
93 "    </signal>\n"
94 "    <signal name=\"CapsChange\">\n"
95 "      <arg type=\"i\"/>\n"
96 "    </signal>\n"
97 "  </interface>\n"
98 "</node>\n"
99 ;
100
101 DBUS_METHOD( PositionGet )
102 { /* returns position in milliseconds */
103     REPLY_INIT;
104     OUT_ARGUMENTS;
105     dbus_int32_t i_pos;
106
107     input_thread_t *p_input = playlist_CurrentInput( PL );
108
109     if( !p_input )
110         i_pos = 0;
111     else
112     {
113         i_pos = var_GetTime( p_input, "time" ) / 1000;
114         vlc_object_release( p_input );
115     }
116     ADD_INT32( &i_pos );
117     REPLY_SEND;
118 }
119
120 DBUS_METHOD( PositionSet )
121 { /* set position in milliseconds */
122
123     REPLY_INIT;
124     vlc_value_t position;
125     dbus_int32_t i_pos;
126
127     DBusError error;
128     dbus_error_init( &error );
129
130     dbus_message_get_args( p_from, &error,
131             DBUS_TYPE_INT32, &i_pos,
132             DBUS_TYPE_INVALID );
133
134     if( dbus_error_is_set( &error ) )
135     {
136         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
137                 error.message );
138         dbus_error_free( &error );
139         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
140     }
141     input_thread_t *p_input = playlist_CurrentInput( PL );
142
143     if( p_input )
144     {
145         position.i_time = ((mtime_t)i_pos) * 1000;
146         var_Set( p_input, "time", position );
147         vlc_object_release( p_input );
148     }
149     REPLY_SEND;
150 }
151
152 DBUS_METHOD( VolumeGet )
153 { /* returns volume in percentage */
154     REPLY_INIT;
155     OUT_ARGUMENTS;
156     dbus_int32_t i_dbus_vol;
157     audio_volume_t i_vol;
158
159     /* 2nd argument of aout_VolumeGet is int32 */
160     aout_VolumeGet( PL, &i_vol );
161
162     double f_vol = 100. * i_vol / AOUT_VOLUME_MAX;
163     i_dbus_vol = round( f_vol );
164     ADD_INT32( &i_dbus_vol );
165     REPLY_SEND;
166 }
167
168 DBUS_METHOD( VolumeSet )
169 { /* set volume in percentage */
170     REPLY_INIT;
171
172     DBusError error;
173     dbus_error_init( &error );
174
175     dbus_int32_t i_dbus_vol;
176     audio_volume_t i_vol;
177
178     dbus_message_get_args( p_from, &error,
179             DBUS_TYPE_INT32, &i_dbus_vol,
180             DBUS_TYPE_INVALID );
181
182     if( dbus_error_is_set( &error ) )
183     {
184         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
185                 error.message );
186         dbus_error_free( &error );
187         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
188     }
189
190     double f_vol = AOUT_VOLUME_MAX * i_dbus_vol / 100.;
191     i_vol = round( f_vol );
192     aout_VolumeSet( PL, i_vol );
193     REPLY_SEND;
194 }
195
196 DBUS_METHOD( Next )
197 { /* next playlist item */
198     REPLY_INIT;
199     playlist_Next( PL );
200     REPLY_SEND;
201 }
202
203 DBUS_METHOD( Prev )
204 { /* previous playlist item */
205     REPLY_INIT;
206     playlist_Prev( PL );
207     REPLY_SEND;
208 }
209
210 DBUS_METHOD( Stop )
211 { /* stop playing */
212     REPLY_INIT;
213     playlist_Stop( PL );
214     REPLY_SEND;
215 }
216
217 DBUS_METHOD( GetStatus )
218 { /* returns the current status as a struct of 4 ints */
219 /*
220     First   0 = Playing, 1 = Paused, 2 = Stopped.
221     Second  0 = Playing linearly , 1 = Playing randomly.
222     Third   0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
223     Fourth  0 = Stop playing once the last element has been played, 1 = Never give up playing *
224  */
225     REPLY_INIT;
226     OUT_ARGUMENTS;
227
228     MarshalStatus( p_this, &args );
229
230     REPLY_SEND;
231 }
232
233 DBUS_METHOD( Pause )
234 {
235     REPLY_INIT;
236     playlist_Pause( PL );
237     REPLY_SEND;
238 }
239
240 DBUS_METHOD( Play )
241 {
242     REPLY_INIT;
243
244     input_thread_t *p_input =  playlist_CurrentInput( PL );
245
246     if( p_input )
247     {
248         double i_pos = 0;
249         input_Control( p_input, INPUT_SET_POSITION, i_pos );
250         vlc_object_release( p_input );
251     }
252     else
253         playlist_Play( PL );
254
255     REPLY_SEND;
256 }
257
258 DBUS_METHOD( Repeat )
259 {
260     REPLY_INIT;
261     OUT_ARGUMENTS;
262
263     DBusError error;
264     dbus_bool_t b_repeat;
265
266     dbus_error_init( &error );
267     dbus_message_get_args( p_from, &error,
268             DBUS_TYPE_BOOLEAN, &b_repeat,
269             DBUS_TYPE_INVALID );
270
271     if( dbus_error_is_set( &error ) )
272     {
273         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
274                 error.message );
275         dbus_error_free( &error );
276         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
277     }
278
279     var_SetBool( PL, "repeat", ( b_repeat == TRUE ) );
280
281     REPLY_SEND;
282 }
283
284 DBUS_METHOD( GetCurrentMetadata )
285 {
286     REPLY_INIT;
287     OUT_ARGUMENTS;
288     playlist_t *p_playlist = PL;
289
290     PL_LOCK;
291     playlist_item_t* p_item =  playlist_CurrentPlayingItem( p_playlist );
292     if( p_item )
293         GetInputMeta( p_item->p_input, &args );
294     PL_UNLOCK;
295     REPLY_SEND;
296 }
297
298 DBUS_METHOD( GetCaps )
299 {
300     REPLY_INIT;
301     OUT_ARGUMENTS;
302
303     ADD_INT32( &INTF->p_sys->i_caps );
304
305     REPLY_SEND;
306 }
307
308 /*****************************************************************************
309  * StatusChange: Player status change signal
310  *****************************************************************************/
311
312 DBUS_SIGNAL( StatusChangeSignal )
313 { /* send the updated status info on the bus */
314     SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
315                  DBUS_MPRIS_PLAYER_PATH,
316                  "StatusChange" );
317
318     OUT_ARGUMENTS;
319
320     /* we're called from a callback of input_thread_t, so it can not be
321      * destroyed before we return */
322     MarshalStatus( (intf_thread_t*) p_data, &args );
323
324     SIGNAL_SEND;
325 }
326
327 /*****************************************************************************
328  * TrackChange: Playlist item change callback
329  *****************************************************************************/
330
331 DBUS_SIGNAL( TrackChangeSignal )
332 { /* emit the metadata of the new item */
333     SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
334                  DBUS_MPRIS_PLAYER_PATH,
335                  "TrackChange" );
336
337     OUT_ARGUMENTS;
338
339     input_item_t *p_item = (input_item_t*) p_data;
340     GetInputMeta ( p_item, &args );
341
342     SIGNAL_SEND;
343 }
344
345 /******************************************************************************
346  * CapsChange: player capabilities change signal
347  *****************************************************************************/
348 DBUS_SIGNAL( CapsChangeSignal )
349 {
350     SIGNAL_INIT( DBUS_MPRIS_PLAYER_INTERFACE,
351                  DBUS_MPRIS_PLAYER_PATH,
352                  "CapsChange" );
353
354     OUT_ARGUMENTS;
355
356     ADD_INT32( &((intf_thread_t*)p_data)->p_sys->i_caps );
357     SIGNAL_SEND;
358 }
359
360 DBUS_METHOD( handle_introspect_player )
361 {
362     VLC_UNUSED(p_this);
363     REPLY_INIT;
364     OUT_ARGUMENTS;
365     ADD_STRING( &psz_player_introspection_xml );
366     REPLY_SEND;
367 }
368
369 #define METHOD_FUNC( interface, method, function ) \
370     else if( dbus_message_is_method_call( p_from, interface, method ) )\
371         return function( p_conn, p_from, p_this )
372
373 DBusHandlerResult
374 handle_player ( DBusConnection *p_conn, DBusMessage *p_from, void *p_this )
375 {
376     if( dbus_message_is_method_call( p_from,
377                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
378         return handle_introspect_player( p_conn, p_from, p_this );
379
380     /* here D-Bus method names are associated to an handler */
381
382     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Prev",        Prev );
383     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Next",        Next );
384     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Stop",        Stop );
385     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Play",        Play );
386     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Pause",       Pause );
387     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "Repeat",      Repeat );
388     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "VolumeSet",   VolumeSet );
389     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "VolumeGet",   VolumeGet );
390     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "PositionSet", PositionSet );
391     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "PositionGet", PositionGet );
392     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetStatus",   GetStatus );
393     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetMetadata", GetCurrentMetadata );
394     METHOD_FUNC( DBUS_MPRIS_PLAYER_INTERFACE, "GetCaps",     GetCaps );
395
396     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
397 }
398
399 #undef METHOD_FUNC
400
401 /*****************************************************************************
402  * StatusChangeEmit: Emits the StatusChange signal
403  *****************************************************************************/
404 int StatusChangeEmit( intf_thread_t * p_intf )
405 {
406     if( p_intf->p_sys->b_dead )
407         return VLC_SUCCESS;
408
409     UpdateCaps( p_intf );
410     StatusChangeSignal( p_intf->p_sys->p_conn, p_intf );
411     return VLC_SUCCESS;
412 }
413
414 /*****************************************************************************
415  * CapsChangeEmit: Emits the CapsChange signal
416  *****************************************************************************/
417 int CapsChangeEmit( intf_thread_t * p_intf )
418 {
419     if( p_intf->p_sys->b_dead )
420         return VLC_SUCCESS;
421
422     CapsChangeSignal( p_intf->p_sys->p_conn, p_intf );
423     return VLC_SUCCESS;
424 }
425
426 /*****************************************************************************
427  * TrackChangeEmit: Emits the TrackChange signal
428  *****************************************************************************/
429 int TrackChangeEmit( intf_thread_t * p_intf, input_item_t* p_item )
430 {
431     if( p_intf->p_sys->b_dead )
432         return VLC_SUCCESS;
433
434     UpdateCaps( p_intf );
435     TrackChangeSignal( p_intf->p_sys->p_conn, p_item );
436     return VLC_SUCCESS;
437 }
438
439 /*****************************************************************************
440  * MarshalStatus: Fill a DBusMessage with the current player status
441  *****************************************************************************/
442
443 static int MarshalStatus( intf_thread_t* p_intf, DBusMessageIter* args )
444 { /* This is NOT the right way to do that, it would be better to sore
445      the status information in p_sys and update it on change, thus
446      avoiding a long lock */
447
448     DBusMessageIter status;
449     dbus_int32_t i_state, i_random, i_repeat, i_loop;
450     playlist_t* p_playlist = p_intf->p_sys->p_playlist;
451
452     vlc_mutex_lock( &p_intf->p_sys->lock );
453     i_state = p_intf->p_sys->i_playing_state;
454     vlc_mutex_unlock( &p_intf->p_sys->lock );
455
456     i_random = var_CreateGetBool( p_playlist, "random" );
457
458     i_repeat = var_CreateGetBool( p_playlist, "repeat" );
459
460     i_loop = var_CreateGetBool( p_playlist, "loop" );
461
462     dbus_message_iter_open_container( args, DBUS_TYPE_STRUCT, NULL, &status );
463     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_state );
464     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_random );
465     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_repeat );
466     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_loop );
467     dbus_message_iter_close_container( args, &status );
468
469     return VLC_SUCCESS;
470 }