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