]> git.sesse.net Git - vlc/blob - src/control/media_player.c
Memory leaks
[vlc] / src / control / media_player.c
1 /*****************************************************************************
2  * media_player.c: Libvlc API Media Instance management functions
3  *****************************************************************************
4  * Copyright (C) 2005 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
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 #include "libvlc_internal.h"
25
26 #include <vlc/libvlc.h>
27 #include <vlc_demux.h>
28 #include <vlc_input.h>
29 #include <vlc_vout.h>
30 #include "libvlc.h"
31 #include <assert.h>
32
33 static int
34 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
35                         vlc_value_t oldval, vlc_value_t newval,
36                         void * p_userdata );
37 static int
38 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
39                         vlc_value_t oldval, vlc_value_t newval,
40                         void * p_userdata );
41 static int
42 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
43                      vlc_value_t oldval, vlc_value_t newval,
44                      void * p_userdata );
45
46 static int SnapshotTakenCallback( vlc_object_t *p_this, char const *psz_cmd,
47                        vlc_value_t oldval, vlc_value_t newval, void *p_data );
48
49 static const libvlc_state_t vlc_to_libvlc_state_array[] =
50 {
51     [INIT_S]        = libvlc_NothingSpecial,
52     [OPENING_S]     = libvlc_Opening,
53     [PLAYING_S]     = libvlc_Playing,
54     [PAUSE_S]       = libvlc_Paused,
55     [END_S]         = libvlc_Ended,
56     [ERROR_S]       = libvlc_Error,
57 };
58
59 static inline libvlc_state_t vlc_to_libvlc_state( int vlc_state )
60 {
61     if( vlc_state < 0 || vlc_state > 6 )
62         return libvlc_Ended;
63
64     return vlc_to_libvlc_state_array[vlc_state];
65 }
66
67 /*
68  * Release the associated input thread
69  *
70  * Object lock is NOT held.
71  */
72 static void release_input_thread( libvlc_media_player_t *p_mi )
73 {
74     input_thread_t * p_input_thread;
75
76     if( !p_mi || !p_mi->p_input_thread )
77         return;
78
79     p_input_thread = p_mi->p_input_thread;
80
81     /* No one is tracking this input_thread appart us. Destroy it */
82     if( p_mi->b_own_its_input_thread )
83     {
84         var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
85         var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
86         var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
87
88         /* We owned this one */
89         input_StopThread( p_input_thread );
90         vlc_thread_join( p_input_thread );
91
92         var_Destroy( p_input_thread, "drawable" );
93     }
94
95     vlc_object_release( p_input_thread );
96
97     p_mi->p_input_thread = NULL;
98 }
99
100 /*
101  * Retrieve the input thread. Be sure to release the object
102  * once you are done with it. (libvlc Internal)
103  *
104  * Object lock is held.
105  */
106 input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi,
107                                          libvlc_exception_t *p_e )
108 {
109     input_thread_t *p_input_thread;
110
111     if( !p_mi ) RAISENULL( "Media Instance is NULL" );
112
113     vlc_mutex_lock( &p_mi->object_lock );
114
115     if( !p_mi->p_input_thread )
116     {
117         vlc_mutex_unlock( &p_mi->object_lock );
118         RAISENULL( "Input is NULL" );
119     }
120
121     p_input_thread = p_mi->p_input_thread;
122     vlc_object_hold( p_input_thread );
123
124     vlc_mutex_unlock( &p_mi->object_lock );
125
126     return p_input_thread;
127 }
128
129 static int
130 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
131                         vlc_value_t oldval, vlc_value_t newval,
132                         void * p_userdata )
133 {
134     VLC_UNUSED(oldval);
135     VLC_UNUSED(p_this);
136     VLC_UNUSED(psz_cmd);
137     libvlc_media_player_t * p_mi = p_userdata;
138     libvlc_event_t event;
139
140     libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
141     event.type = libvlc_MediaPlayerSeekableChanged;
142     event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
143
144     libvlc_event_send( p_mi->p_event_manager, &event );
145     return VLC_SUCCESS;
146 }
147
148 static int
149 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
150                         vlc_value_t oldval, vlc_value_t newval,
151                         void * p_userdata )
152 {
153     VLC_UNUSED(oldval);
154     VLC_UNUSED(p_this);
155     VLC_UNUSED(psz_cmd);
156     libvlc_media_player_t * p_mi = p_userdata;
157     libvlc_event_t event;
158
159     libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
160     event.type = libvlc_MediaPlayerPausableChanged;
161     event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
162
163     libvlc_event_send( p_mi->p_event_manager, &event );
164     return VLC_SUCCESS;
165 }
166
167 static int
168 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
169                      vlc_value_t oldval, vlc_value_t newval,
170                      void * p_userdata )
171 {
172     VLC_UNUSED(oldval);
173     input_thread_t * p_input = (input_thread_t *)p_this;
174     libvlc_media_player_t * p_mi = p_userdata;
175     libvlc_event_t event;
176
177     assert( !strcmp( psz_cmd, "intf-event" ) );
178
179     if( newval.i_int == INPUT_EVENT_STATE )
180     {
181         libvlc_state_t libvlc_state;
182
183         switch ( var_GetInteger( p_input, "state" ) )
184         {
185             case INIT_S:
186                 libvlc_state = libvlc_NothingSpecial;
187                 event.type = libvlc_MediaPlayerNothingSpecial;
188                 break;
189             case OPENING_S:
190                 libvlc_state = libvlc_Opening;
191                 event.type = libvlc_MediaPlayerOpening;
192                 break;
193             case PLAYING_S:
194                 libvlc_state = libvlc_Playing;
195                 event.type = libvlc_MediaPlayerPlaying;
196                 break;
197             case PAUSE_S:
198                 libvlc_state = libvlc_Paused;
199                 event.type = libvlc_MediaPlayerPaused;
200                 break;
201             case END_S:
202                 libvlc_state = libvlc_Ended;
203                 event.type = libvlc_MediaPlayerEndReached;
204                 break;
205             case ERROR_S:
206                 libvlc_state = libvlc_Error;
207                 event.type = libvlc_MediaPlayerEncounteredError;
208                 break;
209
210             default:
211                 return VLC_SUCCESS;
212         }
213
214         libvlc_media_set_state( p_mi->p_md, libvlc_state, NULL);
215         libvlc_event_send( p_mi->p_event_manager, &event );
216     }
217     else if( newval.i_int == INPUT_EVENT_TIMES )
218     {
219         if( var_GetInteger( p_input, "state" ) != PLAYING_S )
220             return VLC_SUCCESS; /* Don't send the position while stopped */
221
222         /* */
223         event.type = libvlc_MediaPlayerPositionChanged;
224         event.u.media_player_position_changed.new_position = var_GetFloat( p_input, "position" );;
225         libvlc_event_send( p_mi->p_event_manager, &event );
226
227         /* */
228         event.type = libvlc_MediaPlayerTimeChanged;
229         event.u.media_player_time_changed.new_time = var_GetTime( p_input, "time" );
230         libvlc_event_send( p_mi->p_event_manager, &event );
231     }
232
233     return VLC_SUCCESS;
234
235 }
236
237
238 /**************************************************************************
239  * Create a Media Instance object
240  **************************************************************************/
241 libvlc_media_player_t *
242 libvlc_media_player_new( libvlc_instance_t * p_libvlc_instance,
243                            libvlc_exception_t * p_e )
244 {
245     libvlc_media_player_t * p_mi;
246
247     if( !p_libvlc_instance )
248     {
249         libvlc_exception_raise( p_e, "invalid libvlc instance" );
250         return NULL;
251     }
252
253     p_mi = malloc( sizeof(libvlc_media_player_t) );
254     if( !p_mi )
255     {
256         libvlc_exception_raise( p_e, "Not enough memory" );
257         return NULL;
258     }
259     p_mi->p_md = NULL;
260     p_mi->drawable = 0;
261     p_mi->p_libvlc_instance = p_libvlc_instance;
262     p_mi->p_input_thread = NULL;
263     /* refcount strategy:
264      * - All items created by _new start with a refcount set to 1
265      * - Accessor _release decrease the refcount by 1, if after that
266      *   operation the refcount is 0, the object is destroyed.
267      * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
268     p_mi->i_refcount = 1;
269     p_mi->b_own_its_input_thread = true;
270     /* object_lock strategy:
271      * - No lock held in constructor
272      * - Lock when accessing all variable this lock is held
273      * - Lock when attempting to destroy the object the lock is also held */
274     vlc_mutex_init( &p_mi->object_lock );
275     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
276             p_libvlc_instance, p_e );
277     if( libvlc_exception_raised( p_e ) )
278     {
279         free( p_mi );
280         return NULL;
281     }
282
283     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
284             libvlc_MediaPlayerNothingSpecial, p_e );
285     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
286             libvlc_MediaPlayerOpening, p_e );
287     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
288             libvlc_MediaPlayerBuffering, p_e );
289     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
290             libvlc_MediaPlayerPlaying, p_e );
291     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
292             libvlc_MediaPlayerPaused, p_e );
293     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
294             libvlc_MediaPlayerStopped, p_e );
295     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
296             libvlc_MediaPlayerForward, p_e );
297     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
298             libvlc_MediaPlayerBackward, p_e );
299     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
300             libvlc_MediaPlayerEndReached, p_e );
301     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
302             libvlc_MediaPlayerEncounteredError, p_e );
303
304     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
305             libvlc_MediaPlayerPositionChanged, p_e );
306     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
307             libvlc_MediaPlayerTimeChanged, p_e );
308      libvlc_event_manager_register_event_type( p_mi->p_event_manager,
309             libvlc_MediaPlayerTitleChanged, p_e );
310     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
311             libvlc_MediaPlayerSeekableChanged, p_e );
312     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
313             libvlc_MediaPlayerPausableChanged, p_e );
314
315     /* Snapshot initialization */
316     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
317            libvlc_MediaPlayerSnapshotTaken, p_e );
318     /* Attach a var callback to the global object to provide the glue between
319         vout_thread that generates the event and media_player that re-emits it
320         with its own event manager
321     */
322     var_Create( p_libvlc_instance->p_libvlc_int, "vout-snapshottaken", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
323     var_AddCallback( p_libvlc_instance->p_libvlc_int, "vout-snapshottaken", SnapshotTakenCallback, p_mi );
324
325     return p_mi;
326 }
327
328 /**************************************************************************
329  * Create a Media Instance object with a media descriptor
330  **************************************************************************/
331 libvlc_media_player_t *
332 libvlc_media_player_new_from_media(
333                                     libvlc_media_t * p_md,
334                                     libvlc_exception_t *p_e )
335 {
336     libvlc_media_player_t * p_mi;
337     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance, p_e );
338
339     if( !p_mi )
340         return NULL;
341
342     libvlc_media_retain( p_md );
343     p_mi->p_md = p_md;
344
345     return p_mi;
346 }
347
348 /**************************************************************************
349  * Create a new media instance object from an input_thread (Libvlc Internal)
350  **************************************************************************/
351 libvlc_media_player_t * libvlc_media_player_new_from_input_thread(
352                                    struct libvlc_instance_t *p_libvlc_instance,
353                                    input_thread_t *p_input,
354                                    libvlc_exception_t *p_e )
355 {
356     libvlc_media_player_t * p_mi;
357
358     if( !p_input )
359     {
360         libvlc_exception_raise( p_e, "invalid input thread" );
361         return NULL;
362     }
363
364     p_mi = libvlc_media_player_new( p_libvlc_instance, p_e );
365
366     if( !p_mi )
367         return NULL;
368
369     p_mi->p_md = libvlc_media_new_from_input_item(
370                     p_libvlc_instance,
371                     input_GetItem( p_input ), p_e );
372
373     if( !p_mi->p_md )
374     {
375         libvlc_media_player_destroy( p_mi );
376         return NULL;
377     }
378
379     /* will be released in media_player_release() */
380     vlc_object_hold( p_input );
381
382     p_mi->p_input_thread = p_input;
383     p_mi->b_own_its_input_thread = false;
384
385     return p_mi;
386 }
387
388 /**************************************************************************
389  * Destroy a Media Instance object (libvlc internal)
390  *
391  * Warning: No lock held here, but hey, this is internal.
392  **************************************************************************/
393 void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
394 {
395     input_thread_t *p_input_thread;
396     libvlc_exception_t p_e;
397
398     libvlc_exception_init( &p_e );
399
400     if( !p_mi )
401         return;
402
403         /* Detach Callback from the main libvlc object */
404     var_DelCallback( p_mi->p_libvlc_instance->p_libvlc_int, "vout-snapshottaken", SnapshotTakenCallback, p_mi );
405
406     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
407
408     if( libvlc_exception_raised( &p_e ) )
409     {
410         libvlc_event_manager_release( p_mi->p_event_manager );
411         libvlc_exception_clear( &p_e );
412         free( p_mi );
413         return; /* no need to worry about no input thread */
414     }
415     vlc_mutex_destroy( &p_mi->object_lock );
416
417     vlc_object_release( p_input_thread );
418
419     libvlc_media_release( p_mi->p_md );
420
421     free( p_mi );
422 }
423
424 /**************************************************************************
425  * Release a Media Instance object
426  **************************************************************************/
427 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
428 {
429     if( !p_mi )
430         return;
431
432     vlc_mutex_lock( &p_mi->object_lock );
433
434     p_mi->i_refcount--;
435
436     if( p_mi->i_refcount > 0 )
437     {
438         vlc_mutex_unlock( &p_mi->object_lock );
439         return;
440     }
441     vlc_mutex_unlock( &p_mi->object_lock );
442     vlc_mutex_destroy( &p_mi->object_lock );
443
444     release_input_thread( p_mi );
445
446     libvlc_event_manager_release( p_mi->p_event_manager );
447
448     libvlc_media_release( p_mi->p_md );
449
450     free( p_mi );
451 }
452
453 /**************************************************************************
454  * Retain a Media Instance object
455  **************************************************************************/
456 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
457 {
458     if( !p_mi )
459         return;
460
461     p_mi->i_refcount++;
462 }
463
464 /**************************************************************************
465  * Set the Media descriptor associated with the instance
466  **************************************************************************/
467 void libvlc_media_player_set_media(
468                             libvlc_media_player_t *p_mi,
469                             libvlc_media_t *p_md,
470                             libvlc_exception_t *p_e )
471 {
472     VLC_UNUSED(p_e);
473
474     if( !p_mi )
475         return;
476
477     vlc_mutex_lock( &p_mi->object_lock );
478
479     release_input_thread( p_mi );
480
481     if( p_mi->p_md )
482         libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, p_e );
483
484     libvlc_media_release( p_mi->p_md );
485
486     if( !p_md )
487     {
488         p_mi->p_md = NULL;
489         vlc_mutex_unlock( &p_mi->object_lock );
490         return; /* It is ok to pass a NULL md */
491     }
492
493     libvlc_media_retain( p_md );
494     p_mi->p_md = p_md;
495
496     /* The policy here is to ignore that we were created using a different
497      * libvlc_instance, because we don't really care */
498     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
499
500     vlc_mutex_unlock( &p_mi->object_lock );
501 }
502
503 /**************************************************************************
504  * Get the Media descriptor associated with the instance
505  **************************************************************************/
506 libvlc_media_t *
507 libvlc_media_player_get_media(
508                             libvlc_media_player_t *p_mi,
509                             libvlc_exception_t *p_e )
510 {
511     VLC_UNUSED(p_e);
512
513     if( !p_mi->p_md )
514         return NULL;
515
516     libvlc_media_retain( p_mi->p_md );
517     return p_mi->p_md;
518 }
519
520 /**************************************************************************
521  * Get the event Manager
522  **************************************************************************/
523 libvlc_event_manager_t *
524 libvlc_media_player_event_manager(
525                             libvlc_media_player_t *p_mi,
526                             libvlc_exception_t *p_e )
527 {
528     VLC_UNUSED(p_e);
529
530     return p_mi->p_event_manager;
531 }
532
533 /**************************************************************************
534  * Trigger a snapshot Taken Event
535  *************************************************************************/
536 static int SnapshotTakenCallback( vlc_object_t *p_this, char const *psz_cmd,
537                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
538 {
539     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
540     VLC_UNUSED(p_this) ;
541
542     libvlc_media_player_t* p_mi = (libvlc_media_player_t*) p_data ;
543     libvlc_event_t event ;
544     event.type = libvlc_MediaPlayerSnapshotTaken ;
545     event.u.media_player_snapshot_taken.psz_filename = newval.psz_string ;
546     /* Snapshot psz data is a vlc_variable owned by libvlc object .
547          Its memmory management is taken care by the obj*/
548     msg_Dbg( p_this, "about to emit libvlc_snapshot_taken.make psz_str=0x%x (%s)",
549           event.u.media_player_snapshot_taken.psz_filename ,event.u.media_player_snapshot_taken.psz_filename );
550     libvlc_event_send( p_mi->p_event_manager, &event );
551
552     return VLC_SUCCESS;
553 }
554
555 /**************************************************************************
556  * Play
557  **************************************************************************/
558 void libvlc_media_player_play( libvlc_media_player_t *p_mi,
559                                  libvlc_exception_t *p_e )
560 {
561     input_thread_t * p_input_thread;
562
563     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
564     {
565         /* A thread already exists, send it a play message */
566         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
567         vlc_object_release( p_input_thread );
568         return;
569     }
570
571     /* Ignore previous exception */
572     libvlc_exception_clear( p_e );
573
574     vlc_mutex_lock( &p_mi->object_lock );
575
576     if( !p_mi->p_md )
577     {
578         libvlc_exception_raise( p_e, "no associated media descriptor" );
579         vlc_mutex_unlock( &p_mi->object_lock );
580         return;
581     }
582
583     p_mi->p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
584                       p_mi->p_md->p_input_item );
585
586
587     if( !p_mi->p_input_thread )
588     {
589         vlc_mutex_unlock( &p_mi->object_lock );
590         return;
591     }
592
593     p_input_thread = p_mi->p_input_thread;
594
595     if( p_mi->drawable )
596     {
597         vlc_value_t val;
598         val.i_int = p_mi->drawable;
599         var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
600         var_Set( p_input_thread, "drawable", val );
601     }
602
603     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
604     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
605     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
606
607     vlc_mutex_unlock( &p_mi->object_lock );
608 }
609
610 /**************************************************************************
611  * Pause
612  **************************************************************************/
613 void libvlc_media_player_pause( libvlc_media_player_t *p_mi,
614                                   libvlc_exception_t *p_e )
615 {
616     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
617
618     if( !p_input_thread )
619         return;
620
621     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
622
623     if( state == libvlc_Playing )
624     {
625         if( libvlc_media_player_can_pause( p_mi, p_e ) )
626             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
627         else
628             libvlc_media_player_stop( p_mi, p_e );
629     }
630     else
631         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
632
633     vlc_object_release( p_input_thread );
634 }
635
636 /**************************************************************************
637  * is_playing
638  **************************************************************************/
639 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi,
640                                      libvlc_exception_t *p_e )
641 {
642     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
643
644     if( !p_input_thread )
645         return 0;
646
647     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
648
649     vlc_object_release( p_input_thread );
650     
651     if( state == libvlc_Playing )
652     {
653         return 1;
654     }
655     return 0;
656 }
657
658
659 /**************************************************************************
660  * Stop
661  **************************************************************************/
662 void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
663                                  libvlc_exception_t *p_e )
664 {
665     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
666
667     if( state == libvlc_Playing || state == libvlc_Paused )
668     {
669         /* Send a stop notification event only of we are in playing or paused states */
670         libvlc_media_set_state( p_mi->p_md, libvlc_Ended, p_e );
671
672         /* Construct and send the event */
673         libvlc_event_t event;
674         event.type = libvlc_MediaPlayerStopped;
675         libvlc_event_send( p_mi->p_event_manager, &event );
676     }
677
678     if( p_mi->b_own_its_input_thread )
679     {
680         vlc_mutex_lock( &p_mi->object_lock );
681         release_input_thread( p_mi ); /* This will stop the input thread */
682         vlc_mutex_unlock( &p_mi->object_lock );
683     }
684     else
685     {
686         input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
687
688         if( !p_input_thread )
689             return;
690
691         input_StopThread( p_input_thread );
692         vlc_object_release( p_input_thread );
693     }
694 }
695
696 /**************************************************************************
697  * Set Drawable
698  **************************************************************************/
699 void libvlc_media_player_set_drawable( libvlc_media_player_t *p_mi,
700                                        libvlc_drawable_t drawable,
701                                        libvlc_exception_t *p_e )
702 {
703     input_thread_t *p_input_thread;
704     vout_thread_t *p_vout = NULL;
705
706     p_mi->drawable = drawable;
707
708     /* Allow on the fly drawable changing. This is tricky has this may
709      * not be supported by every vout. We though can't disable it
710      * because of some creepy drawable type that are not flexible enough
711      * (Win32 HWND for instance) */
712     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
713     if( !p_input_thread ) {
714         /* No input, nothing more to do, we are fine */
715         libvlc_exception_clear( p_e );
716         return;
717     }
718
719     p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD );
720     if( p_vout )
721     {
722         vout_Control( p_vout , VOUT_REPARENT, drawable);
723         vlc_object_release( p_vout );
724     }
725     vlc_object_release( p_input_thread );
726 }
727
728 /**************************************************************************
729  * Get Drawable
730  **************************************************************************/
731 libvlc_drawable_t
732 libvlc_media_player_get_drawable ( libvlc_media_player_t *p_mi, libvlc_exception_t *p_e )
733 {
734     VLC_UNUSED(p_e);
735     return p_mi->drawable;
736 }
737
738 /**************************************************************************
739  * Getters for stream information
740  **************************************************************************/
741 libvlc_time_t libvlc_media_player_get_length(
742                              libvlc_media_player_t *p_mi,
743                              libvlc_exception_t *p_e )
744 {
745     input_thread_t *p_input_thread;
746     vlc_value_t val;
747
748     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
749     if( !p_input_thread )
750         return -1;
751
752     var_Get( p_input_thread, "length", &val );
753     vlc_object_release( p_input_thread );
754
755     return (val.i_time+500LL)/1000LL;
756 }
757
758 libvlc_time_t libvlc_media_player_get_time(
759                                    libvlc_media_player_t *p_mi,
760                                    libvlc_exception_t *p_e )
761 {
762     input_thread_t *p_input_thread;
763     vlc_value_t val;
764
765     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
766     if( !p_input_thread )
767         return -1;
768
769     var_Get( p_input_thread , "time", &val );
770     vlc_object_release( p_input_thread );
771     return (val.i_time+500LL)/1000LL;
772 }
773
774 void libvlc_media_player_set_time(
775                                  libvlc_media_player_t *p_mi,
776                                  libvlc_time_t time,
777                                  libvlc_exception_t *p_e )
778 {
779     input_thread_t *p_input_thread;
780     vlc_value_t value;
781
782     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
783     if( !p_input_thread )
784         return;
785
786     value.i_time = time*1000LL;
787     var_Set( p_input_thread, "time", value );
788     vlc_object_release( p_input_thread );
789 }
790
791 void libvlc_media_player_set_position(
792                                 libvlc_media_player_t *p_mi,
793                                 float position,
794                                 libvlc_exception_t *p_e )
795 {
796     input_thread_t *p_input_thread;
797     vlc_value_t val;
798     val.f_float = position;
799
800     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
801     if( !p_input_thread )
802         return;
803
804     var_Set( p_input_thread, "position", val );
805     vlc_object_release( p_input_thread );
806 }
807
808 float libvlc_media_player_get_position(
809                                  libvlc_media_player_t *p_mi,
810                                  libvlc_exception_t *p_e )
811 {
812     input_thread_t *p_input_thread;
813     vlc_value_t val;
814
815     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
816     if( !p_input_thread )
817         return -1.0;
818
819     var_Get( p_input_thread, "position", &val );
820     vlc_object_release( p_input_thread );
821
822     return val.f_float;
823 }
824
825 void libvlc_media_player_set_chapter(
826                                  libvlc_media_player_t *p_mi,
827                                  int chapter,
828                                  libvlc_exception_t *p_e )
829 {
830     input_thread_t *p_input_thread;
831     vlc_value_t val;
832     val.i_int = chapter;
833
834     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
835     if( !p_input_thread )
836         return;
837
838     var_Set( p_input_thread, "chapter", val );
839     vlc_object_release( p_input_thread );
840 }
841
842 int libvlc_media_player_get_chapter(
843                                  libvlc_media_player_t *p_mi,
844                                  libvlc_exception_t *p_e )
845 {
846     input_thread_t *p_input_thread;
847     vlc_value_t val;
848
849     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
850     if( !p_input_thread )
851         return -1;
852
853     var_Get( p_input_thread, "chapter", &val );
854     vlc_object_release( p_input_thread );
855
856     return val.i_int;
857 }
858
859 int libvlc_media_player_get_chapter_count(
860                                  libvlc_media_player_t *p_mi,
861                                  libvlc_exception_t *p_e )
862 {
863     input_thread_t *p_input_thread;
864     vlc_value_t val;
865
866     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
867     if( !p_input_thread )
868         return -1;
869
870     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
871     vlc_object_release( p_input_thread );
872
873     return val.i_int;
874 }
875
876 int libvlc_media_player_get_chapter_count_for_title(
877                                  libvlc_media_player_t *p_mi,
878                                  int i_title,
879                                  libvlc_exception_t *p_e )
880 {
881     input_thread_t *p_input_thread;
882     vlc_value_t val;
883
884     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
885     if( !p_input_thread )
886         return -1;
887
888     char *psz_name;
889     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
890     {
891         vlc_object_release( p_input_thread );
892         return -1;
893     }
894     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
895     vlc_object_release( p_input_thread );
896     free( psz_name );
897
898     return val.i_int;
899 }
900
901 void libvlc_media_player_set_title(
902                                  libvlc_media_player_t *p_mi,
903                                  int i_title,
904                                  libvlc_exception_t *p_e )
905 {
906     input_thread_t *p_input_thread;
907     vlc_value_t val;
908     val.i_int = i_title;
909
910     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
911     if( !p_input_thread )
912         return;
913
914     var_Set( p_input_thread, "title", val );
915     vlc_object_release( p_input_thread );
916
917     //send event
918     libvlc_event_t event;
919     event.type = libvlc_MediaPlayerTitleChanged;
920     event.u.media_player_title_changed.new_title = i_title;
921     libvlc_event_send( p_mi->p_event_manager, &event );
922 }
923
924 int libvlc_media_player_get_title(
925                                  libvlc_media_player_t *p_mi,
926                                  libvlc_exception_t *p_e )
927 {
928     input_thread_t *p_input_thread;
929     vlc_value_t val;
930
931     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
932     if( !p_input_thread )
933         return -1;
934
935     var_Get( p_input_thread, "title", &val );
936     vlc_object_release( p_input_thread );
937
938     return val.i_int;
939 }
940
941 int libvlc_media_player_get_title_count(
942                                  libvlc_media_player_t *p_mi,
943                                  libvlc_exception_t *p_e )
944 {
945     input_thread_t *p_input_thread;
946     vlc_value_t val;
947
948     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
949     if( !p_input_thread )
950         return -1;
951
952     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
953     vlc_object_release( p_input_thread );
954
955     return val.i_int;
956 }
957
958 void libvlc_media_player_next_chapter(
959                                  libvlc_media_player_t *p_mi,
960                                  libvlc_exception_t *p_e )
961 {
962     input_thread_t *p_input_thread;
963
964     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
965     if( !p_input_thread )
966         return;
967
968     int i_type = var_Type( p_input_thread, "next-chapter" );
969     vlc_value_t val;
970     val.b_bool = true;
971     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
972                             "next-chapter":"next-title", val );
973
974     vlc_object_release( p_input_thread );
975 }
976
977 void libvlc_media_player_previous_chapter(
978                                  libvlc_media_player_t *p_mi,
979                                  libvlc_exception_t *p_e )
980 {
981     input_thread_t *p_input_thread;
982
983     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
984     if( !p_input_thread )
985         return;
986
987     int i_type = var_Type( p_input_thread, "next-chapter" );
988     vlc_value_t val;
989     val.b_bool = true;
990     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
991                             "prev-chapter":"prev-title", val );
992
993     vlc_object_release( p_input_thread );
994 }
995
996 float libvlc_media_player_get_fps(
997                                  libvlc_media_player_t *p_mi,
998                                  libvlc_exception_t *p_e)
999 {
1000     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1001     double f_fps = 0.0;
1002
1003     if( p_input_thread )
1004     {
1005         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1006             f_fps = 0.0;
1007         vlc_object_release( p_input_thread );
1008     }
1009     return f_fps;
1010 }
1011
1012 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
1013                                      libvlc_exception_t *p_e)
1014 {
1015     input_thread_t *p_input_thread =
1016                             libvlc_get_input_thread ( p_mi, p_e);
1017     if ( !p_input_thread )
1018         return false;
1019
1020     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
1021     {
1022         vlc_object_release( p_input_thread );
1023         return true;
1024     }
1025     vlc_object_release( p_input_thread );
1026     return false;
1027 }
1028
1029 void libvlc_media_player_set_rate(
1030                                  libvlc_media_player_t *p_mi,
1031                                  float rate,
1032                                  libvlc_exception_t *p_e )
1033 {
1034     input_thread_t *p_input_thread;
1035     vlc_value_t val;
1036     bool b_can_rewind;
1037
1038     if( rate != 0 )
1039         RAISEVOID( "Rate value is invalid" );
1040
1041     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1042     if( !p_input_thread )
1043         return;
1044
1045     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1046     if( (rate < 0) && !b_can_rewind )
1047     {
1048         vlc_object_release( p_input_thread );
1049         libvlc_exception_raise( p_e, "Rate value is invalid" );
1050         return;
1051     }
1052
1053     val.i_int = 1000.0f/rate;
1054     var_Set( p_input_thread, "rate", val );
1055     vlc_object_release( p_input_thread );
1056 }
1057
1058 float libvlc_media_player_get_rate(
1059                                  libvlc_media_player_t *p_mi,
1060                                  libvlc_exception_t *p_e )
1061 {
1062     input_thread_t *p_input_thread;
1063     vlc_value_t val;
1064     bool b_can_rewind;
1065
1066     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1067     if( !p_input_thread )
1068         return 0.0;  /* rate < 0 indicates rewind */
1069
1070     var_Get( p_input_thread, "rate", &val );
1071     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1072     if( (val.i_int < 0) && !b_can_rewind )
1073     {
1074         libvlc_exception_raise( p_e, "invalid rate" );
1075         return 0.0;
1076     }
1077     vlc_object_release( p_input_thread );
1078
1079     return (float)1000.0f/val.i_int;
1080 }
1081
1082 libvlc_state_t libvlc_media_player_get_state(
1083                                  libvlc_media_player_t *p_mi,
1084                                  libvlc_exception_t *p_e )
1085 {
1086     input_thread_t *p_input_thread;
1087     vlc_value_t val;
1088
1089     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1090     if ( !p_input_thread )
1091     {
1092         /* We do return the right value, no need to throw an exception */
1093         if( libvlc_exception_raised( p_e ) )
1094             libvlc_exception_clear( p_e );
1095         return libvlc_Ended;
1096     }
1097
1098     var_Get( p_input_thread, "state", &val );
1099     vlc_object_release( p_input_thread );
1100
1101     return vlc_to_libvlc_state(val.i_int);
1102 }
1103
1104 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1105                                        libvlc_exception_t *p_e )
1106 {
1107     input_thread_t *p_input_thread;
1108     vlc_value_t val;
1109
1110     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1111     if ( !p_input_thread )
1112     {
1113         /* We do return the right value, no need to throw an exception */
1114         if( libvlc_exception_raised( p_e ) )
1115             libvlc_exception_clear( p_e );
1116         return false;
1117     }
1118     var_Get( p_input_thread, "can-seek", &val );
1119     vlc_object_release( p_input_thread );
1120
1121     return val.b_bool;
1122 }
1123
1124 /* internal function, used by audio, video */
1125 libvlc_track_description_t *
1126         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1127                                       const char *psz_variable,
1128                                       libvlc_exception_t *p_e )
1129 {
1130     input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
1131
1132     if( !p_input )
1133         return NULL;
1134
1135     vlc_value_t val_list, text_list;
1136     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1137
1138     if( val_list.p_list->i_count <= 0 ) /* no tracks */
1139         return NULL;
1140
1141     libvlc_track_description_t *p_track_description, *p_actual, *p_previous;
1142     p_track_description = ( libvlc_track_description_t * )
1143         malloc( sizeof( libvlc_track_description_t ) );
1144     if ( !p_track_description )
1145     {
1146         var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1147         vlc_object_release( p_input );
1148         libvlc_exception_raise( p_e, "no enough memory" );
1149         return NULL;
1150     }
1151     p_actual = p_track_description;
1152     p_previous = NULL;
1153     for( int i = 0; i < val_list.p_list->i_count; i++ )
1154     {
1155         if( !p_actual )
1156         {
1157             p_actual = ( libvlc_track_description_t * )
1158                 malloc( sizeof( libvlc_track_description_t ) );
1159             if ( !p_actual )
1160             {
1161                 libvlc_track_description_release( p_track_description );
1162                 var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1163                 vlc_object_release( p_input );
1164                 libvlc_exception_raise( p_e, "no enough memory" );
1165                 return NULL;
1166             }
1167         }
1168         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1169         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1170         p_actual->p_next = NULL;
1171         if( p_previous )
1172             p_previous->p_next = p_actual;
1173         p_previous = p_actual;
1174         p_actual =  NULL;
1175     }
1176     var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1177     vlc_object_release( p_input );
1178
1179     return p_track_description;
1180 }
1181
1182 void libvlc_track_description_release( libvlc_track_description_t *p_track_description )
1183 {
1184     libvlc_track_description_t *p_actual, *p_before;
1185     p_actual = p_track_description;
1186
1187     while ( p_actual )
1188     {
1189         free( p_actual->psz_name );
1190         p_before = p_actual;
1191         p_actual = p_before->p_next;
1192         free( p_before );
1193     }
1194 }
1195
1196 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1197                                      libvlc_exception_t *p_e )
1198 {
1199     input_thread_t *p_input_thread;
1200     vlc_value_t val;
1201
1202     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1203     if ( !p_input_thread )
1204     {
1205         /* We do return the right value, no need to throw an exception */
1206         if( libvlc_exception_raised( p_e ) )
1207             libvlc_exception_clear( p_e );
1208         return false;
1209     }
1210     var_Get( p_input_thread, "can-pause", &val );
1211     vlc_object_release( p_input_thread );
1212
1213     return val.b_bool;
1214 }