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