b0e59cb6ed266082f8bf22b7a5d2c95ad0da3bcf
[vlc.git] / modules / control / dbus.c
1 /*****************************************************************************
2  * dbus.c : D-Bus control interface
3  *****************************************************************************
4  * Copyright (C) 2006 Rafaël Carré
5  * $Id$
6  *
7  * Author:    Rafaël Carré <funman at videolanorg>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*
25  * D-Bus Specification:
26  *      http://dbus.freedesktop.org/doc/dbus-specification.html
27  * D-Bus low-level C API (libdbus)
28  *      http://dbus.freedesktop.org/doc/dbus/api/html/index.html
29  */
30
31 /*
32  * TODO:
33  *  properties ?
34  *
35  *  macros to read incoming arguments
36  *
37  *  explore different possible types (arrays..)
38  *
39  *  what must we do if org.videolan.vlc already exist on the bus ?
40  *  ( there is more than one vlc instance )
41  */
42
43 /*****************************************************************************
44  * Preamble
45  *****************************************************************************/
46
47 #include <dbus/dbus.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 #include "dbus.h"
53
54 #include <vlc/vlc.h>
55 #include <vlc_aout.h>
56 #include <vlc_interface.h>
57 #include <vlc_meta.h>
58 #include <vlc_input.h>
59 #include <vlc_playlist.h>
60
61
62 /*****************************************************************************
63  * Local prototypes.
64  *****************************************************************************/
65
66 static int  Open    ( vlc_object_t * );
67 static void Close   ( vlc_object_t * );
68 static void Run        ( intf_thread_t * );
69
70
71 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
72                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
73
74 struct intf_sys_t
75 {
76     DBusConnection *p_conn;
77 };
78
79 /*****************************************************************************
80  * Module descriptor
81  *****************************************************************************/
82
83 vlc_module_begin();
84     set_shortname( _("dbus"));
85     set_category( CAT_INTERFACE );
86     set_subcategory( SUBCAT_INTERFACE_CONTROL );
87     set_description( _("D-Bus control interface") );
88     set_capability( "interface", 0 );
89     set_callbacks( Open, Close );
90 vlc_module_end();
91
92 /*****************************************************************************
93  * Methods
94  *****************************************************************************/
95 #if 0
96 DBUS_METHOD( PlaylistExport_XSPF )
97 { /*export playlist to an xspf file */
98
99   /* reads the filename to export to */
100   /* returns the status as int32:
101    *    0 : success
102    *    1 : error
103    *    2 : playlist empty
104    */
105     REPLY_INIT;
106     OUT_ARGUMENTS;
107
108     DBusError error; 
109     dbus_error_init( &error );
110
111     char *psz_file;
112     dbus_int32_t i_ret;
113
114     dbus_message_get_args( p_from, &error,
115             DBUS_TYPE_STRING, &psz_file,
116             DBUS_TYPE_INVALID );
117
118     if( dbus_error_is_set( &error ) )
119     {
120         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
121                 error.message );
122         dbus_error_free( &error );
123         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
124     }
125
126     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
127
128     if( ( !playlist_IsEmpty( p_playlist ) ) &&
129             ( p_playlist->p_root_category->i_children > 0 ) )
130     {
131         if( playlist_Export( p_playlist, psz_file,
132                          p_playlist->p_root_category->pp_children[0],
133                          "export-xspf" ) == VLC_SUCCESS )
134             i_ret = 0;
135         else
136             i_ret = 1;
137     }
138     else
139         i_ret = 2;
140
141     pl_Release( ((vlc_object_t*) p_this ) );
142
143     ADD_INT32( &i_ret );
144     REPLY_SEND;
145 }
146 #endif
147
148 /* Player */
149
150 DBUS_METHOD( Quit )
151 { /* exits vlc */
152     REPLY_INIT;
153     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
154     playlist_Stop( p_playlist );
155     pl_Release( ((vlc_object_t*) p_this) );
156     ((vlc_object_t*)p_this)->p_libvlc->b_die = VLC_TRUE;
157     REPLY_SEND;
158 }
159
160 DBUS_METHOD( PositionGet )
161 { /* returns position as an int in the range [0;1000] */
162     REPLY_INIT;
163     OUT_ARGUMENTS;
164     vlc_value_t position;
165     dbus_int32_t i_pos;
166
167     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
168     input_thread_t *p_input = p_playlist->p_input;
169
170     if( !p_input )
171         i_pos = 0;
172     else
173     {
174         var_Get( p_input, "position", &position );
175         i_pos = position.f_float * 1000 ;
176     }
177     ADD_INT32( &i_pos );
178     pl_Release( ((vlc_object_t*) p_this) );
179     REPLY_SEND;
180 }
181
182 DBUS_METHOD( PositionSet )
183 { /* set position from an int in the range [0;1000] */
184
185     REPLY_INIT;
186     vlc_value_t position;
187     dbus_int32_t i_pos;
188
189     DBusError error;
190     dbus_error_init( &error );
191
192     dbus_message_get_args( p_from, &error,
193             DBUS_TYPE_INT32, &i_pos,
194             DBUS_TYPE_INVALID );
195
196     if( dbus_error_is_set( &error ) )
197     {
198         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
199                 error.message );
200         dbus_error_free( &error );
201         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
202     }
203     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
204     input_thread_t *p_input = p_playlist->p_input;
205
206     if( p_input )
207     {
208         position.f_float = ((float)i_pos) / 1000;
209         var_Set( p_input, "position", position );
210     }
211     pl_Release( ((vlc_object_t*) p_this) );
212     REPLY_SEND;
213 }
214
215 DBUS_METHOD( VolumeGet )
216 { /* returns volume in percentage */
217     REPLY_INIT;
218     OUT_ARGUMENTS;
219     dbus_int32_t i_dbus_vol;
220     audio_volume_t i_vol;
221     /* 2nd argument of aout_VolumeGet is int32 */
222     aout_VolumeGet( (vlc_object_t*) p_this, &i_vol );
223     i_dbus_vol = ( 100 * i_vol ) / AOUT_VOLUME_MAX;
224     ADD_INT32( &i_dbus_vol );
225     REPLY_SEND;
226 }
227
228 DBUS_METHOD( VolumeSet )
229 { /* set volume in percentage */
230     REPLY_INIT;
231
232     DBusError error;
233     dbus_error_init( &error );
234
235     dbus_int32_t i_dbus_vol;
236     audio_volume_t i_vol;
237
238     dbus_message_get_args( p_from, &error,
239             DBUS_TYPE_INT32, &i_dbus_vol,
240             DBUS_TYPE_INVALID );
241
242     if( dbus_error_is_set( &error ) )
243     {
244         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
245                 error.message );
246         dbus_error_free( &error );
247         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
248     }
249
250     i_vol = ( AOUT_VOLUME_MAX / 100 ) *i_dbus_vol;
251     aout_VolumeSet( (vlc_object_t*) p_this, i_vol );
252
253     REPLY_SEND;
254 }
255
256 DBUS_METHOD( Next )
257 { /* next playlist item */
258     REPLY_INIT;
259     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
260     playlist_Next( p_playlist );
261     pl_Release( ((vlc_object_t*) p_this) );
262     REPLY_SEND;
263 }
264
265 DBUS_METHOD( Prev )
266 { /* previous playlist item */
267     REPLY_INIT;
268     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
269     playlist_Prev( p_playlist );
270     pl_Release( ((vlc_object_t*) p_this) );
271     REPLY_SEND;
272 }
273
274 DBUS_METHOD( Stop )
275 { /* stop playing */
276     REPLY_INIT;
277     playlist_t *p_playlist = pl_Yield( ((vlc_object_t*) p_this) );
278     playlist_Stop( p_playlist );
279     pl_Release( ((vlc_object_t*) p_this) );
280     REPLY_SEND;
281 }
282
283 DBUS_METHOD( GetStatus )
284 { /* returns an int: 0=playing 1=paused 2=stopped */
285     REPLY_INIT;
286     OUT_ARGUMENTS;
287
288     dbus_int32_t i_status;
289     vlc_value_t val;
290
291     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
292     input_thread_t *p_input = p_playlist->p_input;
293
294     i_status = 2;
295     if( p_input )
296     {
297         var_Get( p_input, "state", &val );
298         if( val.i_int == PAUSE_S )
299             i_status = 1;
300         else if( val.i_int == PLAYING_S )
301             i_status = 0;
302     }
303
304     pl_Release( p_playlist );
305
306     ADD_INT32( &i_status );
307     REPLY_SEND;
308 }
309
310 DBUS_METHOD( Pause )
311 {
312     REPLY_INIT;
313     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
314     playlist_Pause( p_playlist );
315     pl_Release( p_playlist );
316     REPLY_SEND;
317 }
318
319 DBUS_METHOD( Play )
320 {
321     REPLY_INIT;
322     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
323     playlist_Play( p_playlist );
324     pl_Release( p_playlist );
325     REPLY_SEND;
326 }
327
328 /* Media Player information */
329
330 DBUS_METHOD( Identity )
331 {
332     REPLY_INIT;
333     OUT_ARGUMENTS;
334     char *psz_identity = malloc( strlen( PACKAGE ) + strlen( VERSION ) + 1 );
335     sprintf( psz_identity, "%s %s", PACKAGE, VERSION );
336     ADD_STRING( &psz_identity );
337     free( psz_identity );
338     REPLY_SEND;
339 }
340
341 /* TrackList */
342
343 DBUS_METHOD( AddTrack )
344 { /* add the string to the playlist, and play it if the boolean is true */
345     REPLY_INIT;
346
347     DBusError error;
348     dbus_error_init( &error );
349
350     char *psz_mrl;
351     dbus_bool_t b_play;
352
353     dbus_message_get_args( p_from, &error,
354             DBUS_TYPE_STRING, &psz_mrl,
355             DBUS_TYPE_BOOLEAN, &b_play,
356             DBUS_TYPE_INVALID );
357
358     if( dbus_error_is_set( &error ) )
359     {
360         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
361                 error.message );
362         dbus_error_free( &error );
363         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
364     }
365
366     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
367     playlist_Add( p_playlist, psz_mrl, NULL, PLAYLIST_APPEND |
368             ( ( b_play == TRUE ) ? PLAYLIST_GO : 0 ) , PLAYLIST_END, VLC_TRUE );
369     pl_Release( p_playlist );
370
371     REPLY_SEND;
372 }
373
374 DBUS_METHOD( GetCurrentTrack )
375 {
376     REPLY_INIT;
377     OUT_ARGUMENTS;
378     dbus_int32_t i_position = 0;
379     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
380     playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
381     
382     while ( p_tested_item->p_input->i_id != 
383                     p_playlist->status.p_item->p_input->i_id )
384     {
385         i_position++;
386         p_tested_item = playlist_GetNextLeaf( p_playlist, 
387                         p_playlist->p_root_onelevel,
388                         p_tested_item,
389                         VLC_FALSE,
390                         VLC_FALSE );
391     }
392
393     pl_Release( p_playlist );
394
395     ADD_INT32( &i_position );
396     REPLY_SEND;
397 }
398
399 DBUS_METHOD( GetMetadata )
400 { //TODO reads int, returns a{sv}
401     REPLY_INIT;
402     OUT_ARGUMENTS;
403     DBusError error;
404     dbus_error_init( &error );
405
406     dbus_int32_t i_position;
407
408     dbus_message_get_args( p_from, &error,
409             DBUS_TYPE_INT32, &i_position,
410             DBUS_TYPE_INVALID );
411
412     if( dbus_error_is_set( &error ) )
413     {
414         msg_Err( (vlc_object_t*) p_this, "D-Bus message reading : %s\n",
415                 error.message );
416         dbus_error_free( &error );
417         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
418     }
419
420     //TODO return a{sv}
421
422     REPLY_SEND;
423 }
424
425 DBUS_METHOD( GetLength )
426
427     REPLY_INIT;
428     OUT_ARGUMENTS;
429
430     dbus_int32_t i_elements = 0;
431     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
432     playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
433     playlist_item_t* p_last_item = playlist_GetLastLeaf( p_playlist, 
434                     p_playlist->p_root_onelevel ); 
435
436     while ( p_tested_item->p_input->i_id != p_last_item->p_input->i_id )
437     {
438         i_elements++;
439         p_tested_item = playlist_GetNextLeaf( p_playlist, 
440                         p_playlist->p_root_onelevel,
441                         p_tested_item,
442                         VLC_FALSE,
443                         VLC_FALSE );
444     }
445
446     pl_Release( p_playlist );
447     
448     ADD_INT32( &i_elements );
449     REPLY_SEND;
450 }
451
452 DBUS_METHOD( DelTrack )
453 {
454     REPLY_INIT;
455
456     DBusError error;
457     dbus_error_init( &error );
458
459     dbus_int32_t i_position, i_count = 0;
460     playlist_t *p_playlist = pl_Yield( (vlc_object_t*) p_this );
461     playlist_item_t* p_tested_item = p_playlist->p_root_onelevel;
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\n",
470                 error.message );
471         dbus_error_free( &error );
472         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
473     }
474     
475     while ( i_count < i_position ) 
476     {
477         i_count++;
478         p_tested_item = playlist_GetNextLeaf( p_playlist, 
479                         p_playlist->p_root_onelevel, 
480                         p_tested_item,
481                         VLC_FALSE,
482                         VLC_FALSE );
483     }
484
485     PL_LOCK 
486     playlist_DeleteFromInput( p_playlist, 
487                     p_tested_item->p_input->i_id, 
488                     VLC_TRUE );
489     PL_UNLOCK
490
491     pl_Release( p_playlist );
492     
493     REPLY_SEND;
494 }
495
496 /*****************************************************************************
497  * Introspection method
498  *****************************************************************************/
499
500 DBUS_METHOD( handle_introspect_root )
501 { /* handles introspection of /org/videolan/vlc */
502     REPLY_INIT;
503     OUT_ARGUMENTS;
504     ADD_STRING( &psz_introspection_xml_data_root );
505     REPLY_SEND;
506 }
507
508 DBUS_METHOD( handle_introspect_player )
509 {
510     REPLY_INIT;
511     OUT_ARGUMENTS;
512     ADD_STRING( &psz_introspection_xml_data_player );
513     REPLY_SEND;
514 }
515
516 DBUS_METHOD( handle_introspect_tracklist )
517 {
518     REPLY_INIT;
519     OUT_ARGUMENTS;
520     ADD_STRING( &psz_introspection_xml_data_tracklist );
521     REPLY_SEND;
522 }
523
524 /*****************************************************************************
525  * handle_*: answer to incoming messages
526  *****************************************************************************/
527
528 #define METHOD_FUNC( method, function ) \
529     else if( dbus_message_is_method_call( p_from, VLC_DBUS_INTERFACE, method ) )\
530         return function( p_conn, p_from, p_this )
531
532 DBUS_METHOD( handle_root )
533 {
534
535     if( dbus_message_is_method_call( p_from,
536                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
537         return handle_introspect_root( p_conn, p_from, p_this );
538
539     /* here D-Bus method's names are associated to an handler */
540
541     METHOD_FUNC( "Identity",                Identity );
542
543     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
544 }
545
546
547 DBUS_METHOD( handle_player )
548 {
549     if( dbus_message_is_method_call( p_from,
550                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
551     return handle_introspect_player( p_conn, p_from, p_this );
552
553     /* here D-Bus method's names are associated to an handler */
554
555     METHOD_FUNC( "Prev",                    Prev );
556     METHOD_FUNC( "Next",                    Next );
557     METHOD_FUNC( "Quit",                    Quit );
558     METHOD_FUNC( "Stop",                    Stop );
559     METHOD_FUNC( "Play",                    Play );
560     METHOD_FUNC( "Pause",                   Pause );
561     METHOD_FUNC( "VolumeSet",               VolumeSet );
562     METHOD_FUNC( "VolumeGet",               VolumeGet );
563     METHOD_FUNC( "PositionSet",             PositionSet );
564     METHOD_FUNC( "PositionGet",             PositionGet );
565     METHOD_FUNC( "GetStatus",               GetStatus );
566
567     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
568 }
569
570 DBUS_METHOD( handle_tracklist )
571 {
572     if( dbus_message_is_method_call( p_from,
573                 DBUS_INTERFACE_INTROSPECTABLE, "Introspect" ) )
574     return handle_introspect_tracklist( p_conn, p_from, p_this );
575
576     /* here D-Bus method's names are associated to an handler */
577
578     METHOD_FUNC( "GetMetadata",             GetMetadata );
579     METHOD_FUNC( "GetCurrentTrack",         GetCurrentTrack );
580     METHOD_FUNC( "GetLength",               GetLength );
581     METHOD_FUNC( "AddTrack",                AddTrack );
582     METHOD_FUNC( "DelTrack",                DelTrack );
583
584     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
585 }
586
587 /*****************************************************************************
588  * Open: initialize interface
589  *****************************************************************************/
590
591 static int Open( vlc_object_t *p_this )
592 { /* initialisation of the connection */
593     intf_thread_t   *p_intf = (intf_thread_t*)p_this;
594     intf_sys_t      *p_sys  = malloc( sizeof( intf_sys_t ) );
595     playlist_t      *p_playlist;
596     DBusConnection  *p_conn;
597     DBusError       error;
598
599     if( !p_sys )
600         return VLC_ENOMEM;
601
602     dbus_threads_init_default();
603
604     dbus_error_init( &error );
605
606     /* connect to the session bus */
607     p_conn = dbus_bus_get( DBUS_BUS_SESSION, &error );
608     if( !p_conn )
609     {
610         msg_Err( p_this, "Failed to connect to the D-Bus session daemon: %s",
611                 error.message );
612         dbus_error_free( &error );
613         free( p_sys );
614         return VLC_EGENERIC;
615     }
616
617     /* register a well-known name on the bus */
618     dbus_bus_request_name( p_conn, "org.freedesktop.MediaPlayer", 0, &error );
619     if( dbus_error_is_set( &error ) )
620     {
621         msg_Err( p_this, "Error requesting org.freedesktop.MediaPlayer service:"                " %s\n", error.message );
622         dbus_error_free( &error );
623         free( p_sys );
624         return VLC_EGENERIC;
625     }
626
627     /* we register the objects */
628     dbus_connection_register_object_path( p_conn, VLC_DBUS_ROOT_PATH,
629             &vlc_dbus_root_vtable, p_this );
630     dbus_connection_register_object_path( p_conn, VLC_DBUS_PLAYER_PATH,
631             &vlc_dbus_player_vtable, p_this );
632     dbus_connection_register_object_path( p_conn, VLC_DBUS_TRACKLIST_PATH,
633             &vlc_dbus_tracklist_vtable, p_this );
634
635     dbus_connection_flush( p_conn );
636
637     p_playlist = pl_Yield( p_intf );
638     PL_LOCK;
639     var_AddCallback( p_playlist, "playlist-current", TrackChange, p_intf );
640     PL_UNLOCK;
641     pl_Release( p_playlist );
642
643     p_intf->pf_run = Run;
644     p_intf->p_sys = p_sys;
645     p_sys->p_conn = p_conn;
646
647     return VLC_SUCCESS;
648 }
649
650 /*****************************************************************************
651  * Close: destroy interface
652  *****************************************************************************/
653
654 static void Close   ( vlc_object_t *p_this )
655 {
656     intf_thread_t   *p_intf     = (intf_thread_t*) p_this;
657     playlist_t      *p_playlist = pl_Yield( p_intf );;
658
659     PL_LOCK;
660     var_DelCallback( p_playlist, "playlist-current", TrackChange, p_intf );
661     PL_UNLOCK;
662     pl_Release( p_playlist );
663
664     dbus_connection_unref( p_intf->p_sys->p_conn );
665
666     free( p_intf->p_sys );
667 }
668
669 /*****************************************************************************
670  * Run: main loop
671  *****************************************************************************/
672
673 static void Run          ( intf_thread_t *p_intf )
674 {
675     while( !p_intf->b_die )
676     {
677         msleep( INTF_IDLE_SLEEP );
678         dbus_connection_read_write_dispatch( p_intf->p_sys->p_conn, 0 );
679     }
680 }
681
682 /*****************************************************************************
683  * TrackChange: Playlist item change callback
684  *****************************************************************************/
685
686 DBUS_SIGNAL( TrackChangeSignal )
687 { /* emit the name of the new item */
688     SIGNAL_INIT( "TrackChange" );
689     OUT_ARGUMENTS;
690
691     input_thread_t *p_input = (input_thread_t*) p_data;
692     ADD_STRING( &input_GetItem(p_input)->psz_name );
693
694     SIGNAL_SEND;
695 }
696
697 static int TrackChange( vlc_object_t *p_this, const char *psz_var,
698             vlc_value_t oldval, vlc_value_t newval, void *p_data )
699 {
700     intf_thread_t       *p_intf     = ( intf_thread_t* ) p_data;
701     intf_sys_t          *p_sys      = p_intf->p_sys;
702     playlist_t          *p_playlist;
703     input_thread_t      *p_input    = NULL;
704     (void)p_this; (void)psz_var; (void)oldval; (void)newval;
705
706     p_playlist = pl_Yield( p_intf );
707     PL_LOCK;
708     p_input = p_playlist->p_input;
709
710     if( !p_input )
711     {
712         PL_UNLOCK;
713         pl_Release( p_playlist );
714         return VLC_SUCCESS;
715     }
716
717     vlc_object_yield( p_input );
718     PL_UNLOCK;
719     pl_Release( p_playlist );
720
721     TrackChangeSignal( p_sys->p_conn, p_input );
722
723     vlc_object_release( p_input );
724     return VLC_SUCCESS;
725 }
726