dbus control: event based message notification
[vlc.git] / modules / control / dbus.c
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
7  * $Id$
8  *
9  * Authors:    Rafaël Carré <funman at videolanorg>
10  *             Mirsal Ennaime <mirsal dot ennaime at gmail dot com>
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 /*
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
32  *  extract:
33  *   "If you use this low-level API directly, you're signing up for some pain."
34  *
35  * MPRIS Specification version 1.0
36  *      http://wiki.xmms2.xmms.se/index.php/MPRIS
37  */
38
39 /*****************************************************************************
40  * Preamble
41  *****************************************************************************/
42
43 #ifdef HAVE_CONFIG_H
44 # include "config.h"
45 #endif
46
47 #include <dbus/dbus.h>
48 #include "dbus.h"
49
50 #include <vlc_common.h>
51 #include <vlc_plugin.h>
52 #include <vlc_aout.h>
53 #include <vlc_interface.h>
54 #include <vlc_playlist.h>
55
56 #include <math.h>
57
58 #include <assert.h>
59
60 /*****************************************************************************
61  * Local prototypes.
62  *****************************************************************************/
63
64 static int  Open    ( vlc_object_t * );
65 static void Close   ( vlc_object_t * );
66 static void Run     ( intf_thread_t * );
67
68 static void dbus_dispatch( DBusConnection *connection,
69     DBusDispatchStatus new_status, void *data );
70
71 static int StateChange( intf_thread_t *, int );
72 static int TrackChange( intf_thread_t * );
73 static int StatusChangeEmit( intf_thread_t *);
74 static int TrackListChangeEmit( intf_thread_t *, int, int );
75
76 static int AllCallback( vlc_object_t*, const char*, vlc_value_t, vlc_value_t, void* );
77
78 static int GetInputMeta ( input_item_t *, DBusMessageIter * );
79 static int MarshalStatus ( intf_thread_t *, DBusMessageIter * );
80 static int UpdateCaps( intf_thread_t* );
81
82 /* GetCaps() capabilities */
83 enum
84 {
85      CAPS_NONE                  = 0,
86      CAPS_CAN_GO_NEXT           = 1 << 0,
87      CAPS_CAN_GO_PREV           = 1 << 1,
88      CAPS_CAN_PAUSE             = 1 << 2,
89      CAPS_CAN_PLAY              = 1 << 3,
90      CAPS_CAN_SEEK              = 1 << 4,
91      CAPS_CAN_PROVIDE_METADATA  = 1 << 5,
92      CAPS_CAN_HAS_TRACKLIST     = 1 << 6
93 };
94
95 // The signal that can be get from the callbacks
96 enum
97 {
98     SIGNAL_ITEM_CURRENT,
99     SIGNAL_INTF_CHANGE,
100     SIGNAL_PLAYLIST_ITEM_APPEND,
101     SIGNAL_PLAYLIST_ITEM_DELETED,
102     SIGNAL_RANDOM,
103     SIGNAL_REPEAT,
104     SIGNAL_LOOP,
105     SIGNAL_STATE
106 };
107
108 struct intf_sys_t
109 {
110     DBusConnection *p_conn;
111     bool            b_meta_read;
112     dbus_int32_t    i_caps;
113     bool            b_dead;
114     vlc_array_t    *p_events;
115
116     vlc_cond_t      wait;
117     vlc_mutex_t     lock;
118 };
119
120 typedef struct
121 {
122     int signal;
123     int i_node;
124     int i_input_state;
125 } callback_info_t;
126
127
128 /*****************************************************************************
129  * Module descriptor
130  *****************************************************************************/
131
132 vlc_module_begin ()
133     set_shortname( N_("dbus"))
134     set_category( CAT_INTERFACE )
135     set_subcategory( SUBCAT_INTERFACE_CONTROL )
136     set_description( N_("D-Bus control interface") )
137     set_capability( "interface", 0 )
138     set_callbacks( Open, Close )
139 vlc_module_end ()
140
141 /*****************************************************************************
142  * Methods
143  *****************************************************************************/
144
145 /* Player */
146
147 DBUS_METHOD( Quit )
148 { /* exits vlc */
149     REPLY_INIT;
150     libvlc_Quit(((vlc_object_t*)p_this)->p_libvlc);
151     REPLY_SEND;
152 }
153
154 DBUS_METHOD( MprisVersion )
155 { /*implemented version of the mpris spec */
156     REPLY_INIT;
157     OUT_ARGUMENTS;
158     VLC_UNUSED( p_this );
159     dbus_uint16_t i_major = VLC_MPRIS_VERSION_MAJOR;
160     dbus_uint16_t i_minor = VLC_MPRIS_VERSION_MINOR;
161     DBusMessageIter version;
162
163     if( !dbus_message_iter_open_container( &args, DBUS_TYPE_STRUCT, NULL,
164             &version ) )
165         return DBUS_HANDLER_RESULT_NEED_MEMORY;
166
167     if( !dbus_message_iter_append_basic( &version, DBUS_TYPE_UINT16,
168             &i_major ) )
169         return DBUS_HANDLER_RESULT_NEED_MEMORY;
170
171     if( !dbus_message_iter_append_basic( &version, DBUS_TYPE_UINT16,
172             &i_minor ) )
173         return DBUS_HANDLER_RESULT_NEED_MEMORY;
174
175     if( !dbus_message_iter_close_container( &args, &version ) )
176         return DBUS_HANDLER_RESULT_NEED_MEMORY;
177     REPLY_SEND;
178 }
179
180 DBUS_METHOD( PositionGet )
181 { /* returns position in milliseconds */
182     REPLY_INIT;
183     OUT_ARGUMENTS;
184     vlc_value_t position;
185     dbus_int32_t i_pos;
186
187     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
188     input_thread_t *p_input = playlist_CurrentInput( p_playlist );
189
190     if( !p_input )
191         i_pos = 0;
192     else
193     {
194         var_Get( p_input, "time", &position );
195         i_pos = position.i_time / 1000;
196         vlc_object_release( p_input );
197     }
198     pl_Release( ((vlc_object_t*) p_this) );
199     ADD_INT32( &i_pos );
200     REPLY_SEND;
201 }
202
203 DBUS_METHOD( PositionSet )
204 { /* set position in milliseconds */
205
206     REPLY_INIT;
207     vlc_value_t position;
208     playlist_t* p_playlist = NULL;
209     dbus_int32_t i_pos;
210
211     DBusError error;
212     dbus_error_init( &error );
213
214     dbus_message_get_args( p_from, &error,
215             DBUS_TYPE_INT32, &i_pos,
216             DBUS_TYPE_INVALID );
217
218     if( dbus_error_is_set( &error ) )
219     {
220         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
221                 error.message );
222         dbus_error_free( &error );
223         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
224     }
225     p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
226     input_thread_t *p_input = playlist_CurrentInput( p_playlist );
227
228     if( p_input )
229     {
230         position.i_time = ((mtime_t)i_pos) * 1000;
231         var_Set( p_input, "time", position );
232         vlc_object_release( p_input );
233     }
234     pl_Release( ((vlc_object_t*) p_this) );
235     REPLY_SEND;
236 }
237
238 DBUS_METHOD( VolumeGet )
239 { /* returns volume in percentage */
240     REPLY_INIT;
241     OUT_ARGUMENTS;
242     dbus_int32_t i_dbus_vol;
243     audio_volume_t i_vol;
244
245     /* 2nd argument of aout_VolumeGet is int32 */
246     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
247     aout_VolumeGet( p_playlist, &i_vol );
248     pl_Release( ((vlc_object_t*) p_this) );
249
250     double f_vol = 100. * i_vol / AOUT_VOLUME_MAX;
251     i_dbus_vol = round( f_vol );
252     ADD_INT32( &i_dbus_vol );
253     REPLY_SEND;
254 }
255
256 DBUS_METHOD( VolumeSet )
257 { /* set volume in percentage */
258     REPLY_INIT;
259
260     DBusError error;
261     dbus_error_init( &error );
262
263     dbus_int32_t i_dbus_vol;
264     audio_volume_t i_vol;
265
266     dbus_message_get_args( p_from, &error,
267             DBUS_TYPE_INT32, &i_dbus_vol,
268             DBUS_TYPE_INVALID );
269
270     if( dbus_error_is_set( &error ) )
271     {
272         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
273                 error.message );
274         dbus_error_free( &error );
275         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
276     }
277
278     double f_vol = AOUT_VOLUME_MAX * i_dbus_vol / 100.;
279     i_vol = round( f_vol );
280     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
281     aout_VolumeSet( p_playlist, i_vol );
282     pl_Release( ((vlc_object_t*) p_this) );
283     REPLY_SEND;
284 }
285
286 DBUS_METHOD( Next )
287 { /* next playlist item */
288     REPLY_INIT;
289     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
290     playlist_Next( p_playlist );
291     pl_Release( ((vlc_object_t*) p_this) );
292     REPLY_SEND;
293 }
294
295 DBUS_METHOD( Prev )
296 { /* previous playlist item */
297     REPLY_INIT;
298     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
299     playlist_Prev( p_playlist );
300     pl_Release( ((vlc_object_t*) p_this) );
301     REPLY_SEND;
302 }
303
304 DBUS_METHOD( Stop )
305 { /* stop playing */
306     REPLY_INIT;
307     playlist_t *p_playlist = pl_Hold( ((vlc_object_t*) p_this) );
308     playlist_Stop( p_playlist );
309     pl_Release( ((vlc_object_t*) p_this) );
310     REPLY_SEND;
311 }
312
313 DBUS_METHOD( GetStatus )
314 { /* returns the current status as a struct of 4 ints */
315 /*
316     First   0 = Playing, 1 = Paused, 2 = Stopped.
317     Second  0 = Playing linearly , 1 = Playing randomly.
318     Third   0 = Go to the next element once the current has finished playing , 1 = Repeat the current element
319     Fourth  0 = Stop playing once the last element has been played, 1 = Never give up playing *
320  */
321     REPLY_INIT;
322     OUT_ARGUMENTS;
323
324     MarshalStatus( p_this, &args );
325
326     REPLY_SEND;
327 }
328
329 DBUS_METHOD( Pause )
330 {
331     REPLY_INIT;
332     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
333     playlist_Pause( p_playlist );
334     pl_Release( (vlc_object_t*) p_this );
335     REPLY_SEND;
336 }
337
338 DBUS_METHOD( Play )
339 {
340     REPLY_INIT;
341     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
342
343     input_thread_t *p_input =  playlist_CurrentInput( p_playlist );
344
345     if( p_input )
346     {
347         double i_pos = 0;
348         input_Control( p_input, INPUT_SET_POSITION, i_pos );
349         vlc_object_release( p_input );
350     }
351     else
352         playlist_Play( p_playlist );
353
354     pl_Release( (vlc_object_t*) p_this );
355     REPLY_SEND;
356 }
357
358 DBUS_METHOD( GetCurrentMetadata )
359 {
360     REPLY_INIT;
361     OUT_ARGUMENTS;
362     playlist_t* p_playlist = pl_Hold( (vlc_object_t*) p_this );
363     PL_LOCK;
364     playlist_item_t* p_item =  playlist_CurrentPlayingItem( p_playlist );
365     if( p_item )
366         GetInputMeta( p_item->p_input, &args );
367     PL_UNLOCK;
368     pl_Release( (vlc_object_t*) p_this );
369     REPLY_SEND;
370 }
371
372 DBUS_METHOD( GetCaps )
373 {
374     REPLY_INIT;
375     OUT_ARGUMENTS;
376
377     ADD_INT32( &((intf_thread_t*)p_this)->p_sys->i_caps );
378
379     REPLY_SEND;
380 }
381
382 /* Media Player information */
383
384 DBUS_METHOD( Identity )
385 {
386     VLC_UNUSED(p_this);
387     REPLY_INIT;
388     OUT_ARGUMENTS;
389     char *psz_identity;
390     if( asprintf( &psz_identity, "%s %s", PACKAGE, VERSION ) != -1 )
391     {
392         ADD_STRING( &psz_identity );
393         free( psz_identity );
394     }
395     else
396         return DBUS_HANDLER_RESULT_NEED_MEMORY;
397
398     REPLY_SEND;
399 }
400
401 /* TrackList */
402
403 DBUS_METHOD( AddTrack )
404 { /* add the string to the playlist, and play it if the boolean is true */
405     REPLY_INIT;
406     OUT_ARGUMENTS;
407
408     DBusError error;
409     dbus_error_init( &error );
410     playlist_t* p_playlist = NULL;
411
412     char *psz_mrl;
413     dbus_bool_t b_play;
414
415     dbus_message_get_args( p_from, &error,
416             DBUS_TYPE_STRING, &psz_mrl,
417             DBUS_TYPE_BOOLEAN, &b_play,
418             DBUS_TYPE_INVALID );
419
420     if( dbus_error_is_set( &error ) )
421     {
422         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
423                 error.message );
424         dbus_error_free( &error );
425         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
426     }
427
428     p_playlist = pl_Hold( (vlc_object_t*) p_this );
429     playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
430             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) ,
431             PLAYLIST_END, true, false );
432     pl_Release( (vlc_object_t*) p_this );
433
434     dbus_int32_t i_success = 0;
435     ADD_INT32( &i_success );
436
437     REPLY_SEND;
438 }
439
440 DBUS_METHOD( GetCurrentTrack )
441 {
442     REPLY_INIT;
443     OUT_ARGUMENTS;
444
445     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
446     dbus_int32_t i_position = p_playlist->i_current_index;
447     pl_Release( (vlc_object_t*) p_this );
448
449     ADD_INT32( &i_position );
450     REPLY_SEND;
451 }
452
453 DBUS_METHOD( GetMetadata )
454 {
455     REPLY_INIT;
456     OUT_ARGUMENTS;
457     DBusError error;
458     dbus_error_init( &error );
459
460     dbus_int32_t i_position;
461     playlist_t *p_playlist;
462
463     dbus_message_get_args( p_from, &error,
464            DBUS_TYPE_INT32, &i_position,
465            DBUS_TYPE_INVALID );
466
467     if( dbus_error_is_set( &error ) )
468     {
469         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
470                 error.message );
471         dbus_error_free( &error );
472         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
473     }
474
475     p_playlist = pl_Hold( (vlc_object_t*) p_this );
476     PL_LOCK;
477     if( i_position < p_playlist->current.i_size )
478     {
479         GetInputMeta( p_playlist->current.p_elems[i_position]->p_input, &args );
480     }
481
482     PL_UNLOCK;
483     pl_Release( (vlc_object_t*) p_this );
484     REPLY_SEND;
485 }
486
487 DBUS_METHOD( GetLength )
488 {
489     REPLY_INIT;
490     OUT_ARGUMENTS;
491
492     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_this );
493     dbus_int32_t i_elements = p_playlist->current.i_size;
494     pl_Release( (vlc_object_t*) p_this );
495
496     ADD_INT32( &i_elements );
497     REPLY_SEND;
498 }
499
500 DBUS_METHOD( DelTrack )
501 {
502     REPLY_INIT;
503
504     DBusError error;
505     dbus_error_init( &error );
506
507     dbus_int32_t i_position;
508     playlist_t *p_playlist;
509
510     dbus_message_get_args( p_from, &error,
511             DBUS_TYPE_INT32, &i_position,
512             DBUS_TYPE_INVALID );
513
514     if( dbus_error_is_set( &error ) )
515     {
516         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
517                 error.message );
518         dbus_error_free( &error );
519         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
520     }
521
522     p_playlist = pl_Hold( (vlc_object_t*) p_this );
523     PL_LOCK;
524     if( i_position < p_playlist->current.i_size )
525     {
526         playlist_DeleteFromInput( p_playlist,
527             p_playlist->current.p_elems[i_position]->p_input,
528             pl_Locked );
529     }
530     PL_UNLOCK;
531
532     pl_Release( (vlc_object_t*) p_this );
533
534     REPLY_SEND;
535 }
536
537 DBUS_METHOD( SetLoop )
538 {
539     REPLY_INIT;
540     OUT_ARGUMENTS;
541
542     DBusError error;
543     dbus_bool_t b_loop;
544     playlist_t* p_playlist;
545
546     dbus_error_init( &error );
547     dbus_message_get_args( p_from, &error,
548             DBUS_TYPE_BOOLEAN, &b_loop,
549             DBUS_TYPE_INVALID );
550
551     if( dbus_error_is_set( &error ) )
552     {
553         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
554                 error.message );
555         dbus_error_free( &error );
556         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
557     }
558
559     p_playlist = pl_Hold( (vlc_object_t*) p_this );
560     var_SetBool( p_playlist, "loop", ( b_loop == TRUE ) );
561     pl_Release( ((vlc_object_t*) p_this) );
562
563     REPLY_SEND;
564 }
565
566 DBUS_METHOD( Repeat )
567 {
568     REPLY_INIT;
569     OUT_ARGUMENTS;
570
571     DBusError error;
572     dbus_bool_t b_repeat;
573     playlist_t* p_playlist;
574
575     dbus_error_init( &error );
576     dbus_message_get_args( p_from, &error,
577             DBUS_TYPE_BOOLEAN, &b_repeat,
578             DBUS_TYPE_INVALID );
579
580     if( dbus_error_is_set( &error ) )
581     {
582         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
583                 error.message );
584         dbus_error_free( &error );
585         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
586     }
587
588     p_playlist = pl_Hold( (vlc_object_t*) p_this );
589     var_SetBool( p_playlist, "repeat", ( b_repeat == TRUE ) );
590     pl_Release( ((vlc_object_t*) p_this) );
591
592     REPLY_SEND;
593 }
594
595 DBUS_METHOD( SetRandom )
596 {
597     REPLY_INIT;
598     OUT_ARGUMENTS;
599
600     DBusError error;
601     dbus_bool_t b_random;
602     playlist_t* p_playlist;
603
604     dbus_error_init( &error );
605     dbus_message_get_args( p_from, &error,
606             DBUS_TYPE_BOOLEAN, &b_random,
607             DBUS_TYPE_INVALID );
608
609     if( dbus_error_is_set( &error ) )
610     {
611         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s",
612                 error.message );
613         dbus_error_free( &error );
614         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
615     }
616
617     p_playlist = pl_Hold( (vlc_object_t*) p_this );
618     var_SetBool( p_playlist, "random", ( b_random == TRUE ) );
619     pl_Release( ((vlc_object_t*) p_this) );
620
621     REPLY_SEND;
622 }
623 /*****************************************************************************
624  * Introspection method
625  *****************************************************************************/
626
627 DBUS_METHOD( handle_introspect_root )
628 { /* handles introspection of root object */
629     VLC_UNUSED(p_this);
630     REPLY_INIT;
631     OUT_ARGUMENTS;
632     ADD_STRING( &psz_introspection_xml_data_root );
633     REPLY_SEND;
634 }
635
636 DBUS_METHOD( handle_introspect_player )
637 {
638     VLC_UNUSED(p_this);
639     REPLY_INIT;
640     OUT_ARGUMENTS;
641     ADD_STRING( &psz_introspection_xml_data_player );
642     REPLY_SEND;
643 }
644
645 DBUS_METHOD( handle_introspect_tracklist )
646 {
647     VLC_UNUSED(p_this);
648     REPLY_INIT;
649     OUT_ARGUMENTS;
650     ADD_STRING( &psz_introspection_xml_data_tracklist );
651     REPLY_SEND;
652 }
653
654 /*****************************************************************************
655  * handle_*: answer to incoming messages
656  *****************************************************************************/
657
658 #define METHOD_FUNC( method, function ) \
659     else if( dbus_message_is_method_call( p_from, MPRIS_DBUS_INTERFACE, method ) )\
660         return function( p_conn, p_from, p_this )
661
662 DBUS_METHOD( handle_root )
663 {
664
665     if( dbus_message_is_method_call( p_from,
666                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
667         return handle_introspect_root( p_conn, p_from, p_this );
668
669     /* here D-Bus method's names are associated to an handler */
670
671     METHOD_FUNC( "Identity",                Identity );
672     METHOD_FUNC( "MprisVersion",            MprisVersion );
673     METHOD_FUNC( "Quit",                    Quit );
674
675     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
676 }
677
678
679 DBUS_METHOD( handle_player )
680 {
681     if( dbus_message_is_method_call( p_from,
682                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
683         return handle_introspect_player( p_conn, p_from, p_this );
684
685     /* here D-Bus method's names are associated to an handler */
686
687     METHOD_FUNC( "Prev",                    Prev );
688     METHOD_FUNC( "Next",                    Next );
689     METHOD_FUNC( "Stop",                    Stop );
690     METHOD_FUNC( "Play",                    Play );
691     METHOD_FUNC( "Pause",                   Pause );
692     METHOD_FUNC( "Repeat",                  Repeat );
693     METHOD_FUNC( "VolumeSet",               VolumeSet );
694     METHOD_FUNC( "VolumeGet",               VolumeGet );
695     METHOD_FUNC( "PositionSet",             PositionSet );
696     METHOD_FUNC( "PositionGet",             PositionGet );
697     METHOD_FUNC( "GetStatus",               GetStatus );
698     METHOD_FUNC( "GetMetadata",             GetCurrentMetadata );
699     METHOD_FUNC( "GetCaps",                 GetCaps );
700
701     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
702 }
703
704 DBUS_METHOD( handle_tracklist )
705 {
706     if( dbus_message_is_method_call( p_from,
707                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
708     return handle_introspect_tracklist( p_conn, p_from, p_this );
709
710     /* here D-Bus method's names are associated to an handler */
711
712     METHOD_FUNC( "GetMetadata",             GetMetadata );
713     METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
714     METHOD_FUNC( "GetLength",               GetLength );
715     METHOD_FUNC( "AddTrack",                AddTrack );
716     METHOD_FUNC( "DelTrack",                DelTrack );
717     METHOD_FUNC( "SetLoop",                 SetLoop );
718     METHOD_FUNC( "SetRandom",               SetRandom );
719
720     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
721 }
722
723 /*****************************************************************************
724  * Open: initialize interface
725  *****************************************************************************/
726
727 static int Open( vlc_object_t *p_this )
728 { /* initialisation of the connection */
729     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
730     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
731     playlist_t      *p_playlist;
732     DBusConnection  *p_conn;
733     DBusError       error;
734
735     if( !p_sys )
736         return VLC_ENOMEM;
737
738     p_sys->b_meta_read = false;
739     p_sys->i_caps = CAPS_NONE;
740     p_sys->b_dead = false;
741
742     dbus_error_init( &error );
743
744     /* connect to the session bus */
745     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
746     if( !p_conn )
747     {
748         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
749                 error.message );
750         dbus_error_free( &error );
751         free( p_sys );
752         return VLC_EGENERIC;
753     }
754
755     /* register a well-known name on the bus */
756     dbus_bus_request_name( p_conn, VLC_MPRIS_DBUS_SERVICE, 0, &error );
757     if( dbus_error_is_set( &error ) )
758     {
759         msg_Err( p_this, "Error requesting service " VLC_MPRIS_DBUS_SERVICE
760                  ": %s", error.message );
761         dbus_error_free( &error );
762         free( p_sys );
763         return VLC_EGENERIC;
764     }
765
766     /* we register the objects */
767     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_ROOT_PATH,
768             &vlc_dbus_root_vtable, p_this );
769     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_PLAYER_PATH,
770             &vlc_dbus_player_vtable, p_this );
771     dbus_connection_register_object_path( p_conn, MPRIS_DBUS_TRACKLIST_PATH,
772             &vlc_dbus_tracklist_vtable, p_this );
773
774     dbus_connection_flush( p_conn );
775
776     p_intf->pf_run = Run;
777     p_intf->p_sys = p_sys;
778     p_sys->p_conn = p_conn;
779     p_sys->p_events = vlc_array_new();
780     vlc_mutex_init( &p_sys->lock );
781     vlc_cond_init( &p_sys->wait );
782
783     p_playlist = pl_Hold( p_intf );
784     PL_LOCK;
785     var_AddCallback( p_playlist, "item-current", AllCallback, p_intf );
786     var_AddCallback( p_playlist, "intf-change", AllCallback, p_intf );
787     var_AddCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
788     var_AddCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
789     var_AddCallback( p_playlist, "random", AllCallback, p_intf );
790     var_AddCallback( p_playlist, "repeat", AllCallback, p_intf );
791     var_AddCallback( p_playlist, "loop", AllCallback, p_intf );
792     PL_UNLOCK;
793     pl_Release( p_intf );
794
795     UpdateCaps( p_intf );
796
797     dbus_connection_set_dispatch_status_function( p_conn, dbus_dispatch,
798         (void*)p_intf, dbus_free);
799
800     return VLC_SUCCESS;
801 }
802
803 /*****************************************************************************
804  * Close: destroy interface
805  *****************************************************************************/
806
807 static void Close   ( vlc_object_t *p_this )
808 {
809     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
810     intf_sys_t      *p_sys      = p_intf->p_sys;
811     playlist_t      *p_playlist = pl_Hold( p_intf );;
812     input_thread_t  *p_input;
813
814     var_DelCallback( p_playlist, "item-current", AllCallback, p_intf );
815     var_DelCallback( p_playlist, "intf-change", AllCallback, p_intf );
816     var_DelCallback( p_playlist, "playlist-item-append", AllCallback, p_intf );
817     var_DelCallback( p_playlist, "playlist-item-deleted", AllCallback, p_intf );
818     var_DelCallback( p_playlist, "random", AllCallback, p_intf );
819     var_DelCallback( p_playlist, "repeat", AllCallback, p_intf );
820     var_DelCallback( p_playlist, "loop", AllCallback, p_intf );
821
822     p_input = playlist_CurrentInput( p_playlist );
823     if ( p_input )
824     {
825         var_DelCallback( p_input, "state", AllCallback, p_intf );
826         vlc_object_release( p_input );
827     }
828
829     pl_Release( p_intf );
830
831     dbus_connection_unref( p_sys->p_conn );
832
833     // Free the events array
834     for( int i = 0; i < vlc_array_count( p_sys->p_events ); i++ )
835     {
836         callback_info_t* info = vlc_array_item_at_index( p_sys->p_events, i );
837         free( info );
838     }
839     vlc_mutex_destroy( &p_sys->lock );
840     vlc_cond_destroy( &p_sys->wait );
841     vlc_array_destroy( p_sys->p_events );
842     free( p_sys );
843 }
844
845 /*****************************************************************************
846  * Run: main loop
847  *****************************************************************************/
848
849 static void Run          ( intf_thread_t *p_intf )
850 {
851     intf_sys_t *p_sys = p_intf->p_sys;
852
853     for(;;)
854     {
855         vlc_mutex_lock( &p_sys->lock );
856         vlc_cond_wait( &p_sys->wait, &p_sys->lock );
857         vlc_mutex_unlock( &p_sys->lock );
858
859         int canc = vlc_savecancel();
860         dbus_connection_read_write_dispatch( p_sys->p_conn, 0 );
861
862         /* Get the list of events to process
863          *
864          * We can't keep the lock on p_sys->p_events, else we risk a
865          * deadlock:
866          * The signal functions could lock mutex X while p_events is locked;
867          * While some other function in vlc (playlist) might lock mutex X
868          * and then set a variable which would call AllCallback(), which itself
869          * needs to lock p_events to add a new event.
870          */
871         vlc_mutex_lock( &p_sys->lock );
872         int i_events = vlc_array_count( p_sys->p_events );
873         callback_info_t* info[i_events];
874         for( int i = i_events - 1; i >= 0; i-- )
875         {
876             info[i] = vlc_array_item_at_index( p_sys->p_events, i );
877             vlc_array_remove( p_sys->p_events, i );
878         }
879         vlc_mutex_unlock( &p_sys->lock );
880
881         for( int i = 0; i < i_events; i++ )
882         {
883             switch( info[i]->signal )
884             {
885             case SIGNAL_ITEM_CURRENT:
886                 TrackChange( p_intf );
887                 break;
888             case SIGNAL_INTF_CHANGE:
889             case SIGNAL_PLAYLIST_ITEM_APPEND:
890             case SIGNAL_PLAYLIST_ITEM_DELETED:
891                 TrackListChangeEmit( p_intf, info[i]->signal, info[i]->i_node );
892                 break;
893             case SIGNAL_RANDOM:
894             case SIGNAL_REPEAT:
895             case SIGNAL_LOOP:
896                 StatusChangeEmit( p_intf );
897                 break;
898             case SIGNAL_STATE:
899                 StateChange( p_intf, info[i]->i_input_state );
900                 break;
901             default:
902                 assert(0);
903             }
904             free( info[i] );
905         }
906         vlc_restorecancel( canc );
907     }
908 }
909
910 static void dbus_dispatch( DBusConnection *connection,
911     DBusDispatchStatus new_status, void *data )
912 {
913     (void)connection;
914     intf_thread_t *p_intf = (intf_thread_t*) data;
915     intf_sys_t *p_sys = p_intf->p_sys;
916
917     if(new_status == DBUS_DISPATCH_COMPLETE)
918         return;
919
920     vlc_mutex_lock( &p_sys->lock );
921     vlc_cond_signal( &p_sys->wait );
922     vlc_mutex_unlock( &p_sys->lock );
923 }
924
925
926 // Get all the callbacks
927 static int AllCallback( vlc_object_t *p_this, const char *psz_var,
928                         vlc_value_t oldval, vlc_value_t newval, void *p_data )
929 {
930     (void)p_this;
931     (void)oldval;
932     intf_thread_t *p_intf = (intf_thread_t*)p_data;
933
934     callback_info_t *info = malloc( sizeof( callback_info_t ) );
935     if( !info )
936         return VLC_ENOMEM;
937
938     // Wich event is it ?
939     if( !strcmp( "item-current", psz_var ) )
940         info->signal = SIGNAL_ITEM_CURRENT;
941     else if( !strcmp( "intf-change", psz_var ) )
942         info->signal = SIGNAL_INTF_CHANGE;
943     else if( !strcmp( "playlist-item-append", psz_var ) )
944     {
945         info->signal = SIGNAL_PLAYLIST_ITEM_APPEND;
946         info->i_node = ((playlist_add_t*)newval.p_address)->i_node;
947     }
948     else if( !strcmp( "playlist-item-deleted", psz_var ) )
949         info->signal = SIGNAL_PLAYLIST_ITEM_DELETED;
950     else if( !strcmp( "random", psz_var ) )
951         info->signal = SIGNAL_RANDOM;
952     else if( !strcmp( "repeat", psz_var ) )
953         info->signal = SIGNAL_REPEAT;
954     else if( !strcmp( "loop", psz_var ) )
955         info->signal = SIGNAL_LOOP;
956     else if( !strcmp( "state", psz_var ) )
957     {
958         info->signal = SIGNAL_STATE;
959         info->i_input_state = newval.i_int;
960     }
961     else
962         assert(0);
963
964     // Append the event
965     vlc_mutex_lock( &p_intf->p_sys->lock );
966     vlc_array_append( p_intf->p_sys->p_events, info );
967     vlc_mutex_unlock( &p_intf->p_sys->lock );
968     return VLC_SUCCESS;
969 }
970
971 /******************************************************************************
972  * CapsChange: player capabilities change signal
973  *****************************************************************************/
974 DBUS_SIGNAL( CapsChangeSignal )
975 {
976     SIGNAL_INIT( "CapsChange" );
977     OUT_ARGUMENTS;
978
979     ADD_INT32( &((intf_thread_t*)p_data)->p_sys->i_caps );
980     SIGNAL_SEND;
981 }
982
983 /******************************************************************************
984  * TrackListChange: tracklist order / length change signal
985  *****************************************************************************/
986 DBUS_SIGNAL( TrackListChangeSignal )
987 { /* emit the new tracklist lengh */
988     SIGNAL_INIT("TrackListChange");
989     OUT_ARGUMENTS;
990
991     playlist_t *p_playlist = pl_Hold( (vlc_object_t*) p_data );
992     dbus_int32_t i_elements = p_playlist->current.i_size;
993     pl_Release( (vlc_object_t*) p_data );
994
995     ADD_INT32( &i_elements );
996     SIGNAL_SEND;
997 }
998
999 /*****************************************************************************
1000  * TrackListChangeEmit: Emits the TrackListChange signal
1001  *****************************************************************************/
1002 /* FIXME: It is not called on tracklist reordering */
1003 static int TrackListChangeEmit( intf_thread_t *p_intf, int signal, int i_node )
1004 {
1005     // "playlist-item-append"
1006     if( signal == SIGNAL_PLAYLIST_ITEM_APPEND )
1007     {
1008         /* don't signal when items are added/removed in p_category */
1009         playlist_t *p_playlist = pl_Hold( p_intf );
1010         PL_LOCK;
1011         playlist_item_t *p_item = playlist_ItemGetById( p_playlist, i_node );
1012         assert( p_item );
1013         while( p_item->p_parent )
1014             p_item = p_item->p_parent;
1015         if( p_item == p_playlist->p_root_category )
1016         {
1017             PL_UNLOCK;
1018             pl_Release( p_intf );
1019             return VLC_SUCCESS;
1020         }
1021         PL_UNLOCK;
1022         pl_Release( p_intf );
1023     }
1024
1025     if( p_intf->p_sys->b_dead )
1026         return VLC_SUCCESS;
1027
1028     UpdateCaps( p_intf );
1029     TrackListChangeSignal( p_intf->p_sys->p_conn, p_intf );
1030     return VLC_SUCCESS;
1031 }
1032 /*****************************************************************************
1033  * TrackChange: Playlist item change callback
1034  *****************************************************************************/
1035
1036 DBUS_SIGNAL( TrackChangeSignal )
1037 { /* emit the metadata of the new item */
1038     SIGNAL_INIT( "TrackChange" );
1039     OUT_ARGUMENTS;
1040
1041     input_item_t *p_item = (input_item_t*) p_data;
1042     GetInputMeta ( p_item, &args );
1043
1044     SIGNAL_SEND;
1045 }
1046
1047 /*****************************************************************************
1048  * StatusChange: Player status change signal
1049  *****************************************************************************/
1050
1051 DBUS_SIGNAL( StatusChangeSignal )
1052 { /* send the updated status info on the bus */
1053     SIGNAL_INIT( "StatusChange" );
1054     OUT_ARGUMENTS;
1055
1056     /* we're called from a callback of input_thread_t, so it can not be
1057      * destroyed before we return */
1058     MarshalStatus( (intf_thread_t*) p_data, &args );
1059
1060     SIGNAL_SEND;
1061 }
1062
1063 /*****************************************************************************
1064  * StateChange: callback on input "state"
1065  *****************************************************************************/
1066 //static int StateChange( vlc_object_t *p_this, const char* psz_var,
1067 //            vlc_value_t oldval, vlc_value_t newval, void *p_data )
1068 static int StateChange( intf_thread_t *p_intf, int i_input_state )
1069 {
1070     intf_sys_t          *p_sys      = p_intf->p_sys;
1071     playlist_t          *p_playlist;
1072     input_thread_t      *p_input;
1073     input_item_t        *p_item;
1074
1075     if( p_intf->p_sys->b_dead )
1076         return VLC_SUCCESS;
1077
1078     UpdateCaps( p_intf );
1079
1080     if( !p_sys->b_meta_read && i_input_state == PLAYING_S )
1081     {
1082         p_playlist = pl_Hold( p_intf );
1083         p_input = playlist_CurrentInput( p_playlist );
1084         if( p_input )
1085         {
1086             p_item = input_GetItem( p_input );
1087             if( p_item )
1088             {
1089                 p_sys->b_meta_read = true;
1090                 TrackChangeSignal( p_sys->p_conn, p_item );
1091             }
1092             vlc_object_release( p_input );
1093         }
1094         pl_Release( p_intf );
1095     }
1096
1097     if( i_input_state == PLAYING_S || i_input_state == PAUSE_S ||
1098         i_input_state == END_S )
1099     {
1100         StatusChangeSignal( p_sys->p_conn, p_intf );
1101     }
1102
1103     return VLC_SUCCESS;
1104 }
1105
1106 /*****************************************************************************
1107  * StatusChangeEmit: Emits the StatusChange signal
1108  *****************************************************************************/
1109 static int StatusChangeEmit( intf_thread_t * p_intf )
1110 {
1111     if( p_intf->p_sys->b_dead )
1112         return VLC_SUCCESS;
1113
1114     UpdateCaps( p_intf );
1115     StatusChangeSignal( p_intf->p_sys->p_conn, p_intf );
1116     return VLC_SUCCESS;
1117 }
1118
1119 /*****************************************************************************
1120  * TrackChange: callback on playlist "item-current"
1121  *****************************************************************************/
1122 static int TrackChange( intf_thread_t *p_intf )
1123 {
1124     intf_sys_t          *p_sys      = p_intf->p_sys;
1125     playlist_t          *p_playlist;
1126     input_thread_t      *p_input    = NULL;
1127     input_item_t        *p_item     = NULL;
1128
1129     if( p_intf->p_sys->b_dead )
1130         return VLC_SUCCESS;
1131
1132     p_sys->b_meta_read = false;
1133
1134     p_playlist = pl_Hold( p_intf );
1135     p_input = playlist_CurrentInput( p_playlist );
1136     if( !p_input )
1137     {
1138         pl_Release( p_intf );
1139         return VLC_SUCCESS;
1140     }
1141
1142     pl_Release( p_intf );
1143
1144     p_item = input_GetItem( p_input );
1145     if( !p_item )
1146     {
1147         vlc_object_release( p_input );
1148         return VLC_EGENERIC;
1149     }
1150
1151     if( input_item_IsPreparsed( p_item ) )
1152     {
1153         p_sys->b_meta_read = true;
1154         TrackChangeSignal( p_sys->p_conn, p_item );
1155     }
1156
1157     var_AddCallback( p_input, "state", AllCallback, p_intf );
1158
1159     vlc_object_release( p_input );
1160     return VLC_SUCCESS;
1161 }
1162
1163 /*****************************************************************************
1164  * UpdateCaps: update p_sys->i_caps
1165  * This function have to be called with the playlist unlocked
1166  ****************************************************************************/
1167 static int UpdateCaps( intf_thread_t* p_intf )
1168 {
1169     intf_sys_t* p_sys = p_intf->p_sys;
1170     dbus_int32_t i_caps = CAPS_CAN_HAS_TRACKLIST;
1171     playlist_t* p_playlist = pl_Hold( p_intf );
1172
1173     PL_LOCK;
1174     if( p_playlist->current.i_size > 0 )
1175         i_caps |= CAPS_CAN_PLAY | CAPS_CAN_GO_PREV | CAPS_CAN_GO_NEXT;
1176     PL_UNLOCK;
1177
1178     input_thread_t* p_input = playlist_CurrentInput( p_playlist );
1179     if( p_input )
1180     {
1181         /* XXX: if UpdateCaps() is called too early, these are
1182          * unconditionnaly true */
1183         if( var_GetBool( p_input, "can-pause" ) )
1184             i_caps |= CAPS_CAN_PAUSE;
1185         if( var_GetBool( p_input, "can-seek" ) )
1186             i_caps |= CAPS_CAN_SEEK;
1187         vlc_object_release( p_input );
1188     }
1189
1190     pl_Release( p_intf );
1191
1192     if( p_sys->b_meta_read )
1193         i_caps |= CAPS_CAN_PROVIDE_METADATA;
1194
1195     if( i_caps != p_intf->p_sys->i_caps )
1196     {
1197         p_sys->i_caps = i_caps;
1198         CapsChangeSignal( p_intf->p_sys->p_conn, (vlc_object_t*)p_intf );
1199     }
1200
1201     return VLC_SUCCESS;
1202 }
1203
1204 /*****************************************************************************
1205  * GetInputMeta: Fill a DBusMessage with the given input item metadata
1206  *****************************************************************************/
1207
1208 #define ADD_META( entry, type, data ) \
1209     if( data ) { \
1210         dbus_message_iter_open_container( &dict, DBUS_TYPE_DICT_ENTRY, \
1211                 NULL, &dict_entry ); \
1212         dbus_message_iter_append_basic( &dict_entry, DBUS_TYPE_STRING, \
1213                 &ppsz_meta_items[entry] ); \
1214         dbus_message_iter_open_container( &dict_entry, DBUS_TYPE_VARIANT, \
1215                 type##_AS_STRING, &variant ); \
1216         dbus_message_iter_append_basic( &variant, \
1217                 type, \
1218                 & data ); \
1219         dbus_message_iter_close_container( &dict_entry, &variant ); \
1220         dbus_message_iter_close_container( &dict, &dict_entry ); }
1221
1222 #define ADD_VLC_META_STRING( entry, item ) \
1223     { \
1224         char * psz = input_item_Get##item( p_input );\
1225         ADD_META( entry, DBUS_TYPE_STRING, \
1226                   psz ); \
1227         free( psz ); \
1228     }
1229
1230 static int GetInputMeta( input_item_t* p_input,
1231                         DBusMessageIter *args )
1232 {
1233     DBusMessageIter dict, dict_entry, variant;
1234     /* We need the track length to be expressed in milli-seconds
1235      * instead of µ-seconds */
1236     dbus_int64_t i_length = ( input_item_GetDuration( p_input ) / 1000 );
1237
1238     const char* ppsz_meta_items[] =
1239     {
1240     "title", "artist", "genre", "copyright", "album", "tracknum",
1241     "description", "rating", "date", "setting", "url", "language",
1242     "nowplaying", "publisher", "encodedby", "arturl", "trackid",
1243     "status", "location", "length", "video-codec", "audio-codec",
1244     "video-bitrate", "audio-bitrate", "audio-samplerate"
1245     };
1246
1247     dbus_message_iter_open_container( args, DBUS_TYPE_ARRAY, "{sv}", &dict );
1248
1249     ADD_VLC_META_STRING( 0,  Title );
1250     ADD_VLC_META_STRING( 1,  Artist );
1251     ADD_VLC_META_STRING( 2,  Genre );
1252     ADD_VLC_META_STRING( 3,  Copyright );
1253     ADD_VLC_META_STRING( 4,  Album );
1254     ADD_VLC_META_STRING( 5,  TrackNum );
1255     ADD_VLC_META_STRING( 6,  Description );
1256     ADD_VLC_META_STRING( 7,  Rating );
1257     ADD_VLC_META_STRING( 8,  Date );
1258     ADD_VLC_META_STRING( 9,  Setting );
1259     ADD_VLC_META_STRING( 10, URL );
1260     ADD_VLC_META_STRING( 11, Language );
1261     ADD_VLC_META_STRING( 12, NowPlaying );
1262     ADD_VLC_META_STRING( 13, Publisher );
1263     ADD_VLC_META_STRING( 14, EncodedBy );
1264     ADD_VLC_META_STRING( 15, ArtURL );
1265     ADD_VLC_META_STRING( 16, TrackID );
1266
1267     vlc_mutex_lock( &p_input->lock );
1268     if( p_input->p_meta )
1269         ADD_META( 17, DBUS_TYPE_INT32, p_input->p_meta->i_status );
1270     vlc_mutex_unlock( &p_input->lock );
1271
1272     ADD_VLC_META_STRING( 18, URI );
1273     ADD_META( 19, DBUS_TYPE_INT64, i_length );
1274
1275     dbus_message_iter_close_container( args, &dict );
1276     return VLC_SUCCESS;
1277 }
1278
1279 #undef ADD_META
1280 #undef ADD_VLC_META_STRING
1281
1282 /*****************************************************************************
1283  * MarshalStatus: Fill a DBusMessage with the current player status
1284  *****************************************************************************/
1285
1286 static int MarshalStatus( intf_thread_t* p_intf, DBusMessageIter* args )
1287 { /* This is NOT the right way to do that, it would be better to sore
1288      the status information in p_sys and update it on change, thus
1289      avoiding a long lock */
1290
1291     DBusMessageIter status;
1292     dbus_int32_t i_state, i_random, i_repeat, i_loop;
1293     vlc_value_t val;
1294     playlist_t* p_playlist = NULL;
1295     input_thread_t* p_input = NULL;
1296
1297     p_playlist = pl_Hold( p_intf );
1298
1299     i_state = 2;
1300
1301     p_input = playlist_CurrentInput( p_playlist );
1302     if( p_input )
1303     {
1304         var_Get( p_input, "state", &val );
1305         if( val.i_int >= END_S )
1306             i_state = 2;
1307         else if( val.i_int == PAUSE_S )
1308             i_state = 1;
1309         else if( val.i_int <= PLAYING_S )
1310             i_state = 0;
1311         vlc_object_release( p_input );
1312     }
1313
1314     i_random = var_CreateGetBool( p_playlist, "random" );
1315
1316     i_repeat = var_CreateGetBool( p_playlist, "repeat" );
1317
1318     i_loop = var_CreateGetBool( p_playlist, "loop" );
1319
1320     pl_Release( p_intf );
1321
1322     dbus_message_iter_open_container( args, DBUS_TYPE_STRUCT, NULL, &status );
1323     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_state );
1324     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_random );
1325     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_repeat );
1326     dbus_message_iter_append_basic( &status, DBUS_TYPE_INT32, &i_loop );
1327     dbus_message_iter_close_container( args, &status );
1328
1329     return VLC_SUCCESS;
1330 }