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