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