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