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