]> git.sesse.net Git - vlc/blob - src/control/media_player.c
aebe7d9901149ea4e56fff2b2d5f6f07031bb71d
[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-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_xwindow( 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 uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
710 {
711     return p_mi->drawable.xid;
712 }
713
714 void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
715                                    void *drawable,
716                                    libvlc_exception_t *p_e )
717 {
718     (void) p_e;
719     p_mi->drawable.hwnd = drawable;
720 }
721
722 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
723 {
724     return p_mi->drawable.hwnd;
725 }
726
727 /**************************************************************************
728  * Set Drawable
729  **************************************************************************/
730 void libvlc_media_player_set_drawable( libvlc_media_player_t *p_mi,
731                                        libvlc_drawable_t drawable,
732                                        libvlc_exception_t *p_e )
733 {
734     input_thread_t *p_input_thread;
735     vout_thread_t *p_vout = NULL;
736
737     p_mi->drawable.xid = drawable;
738
739     /* Allow on the fly drawable changing. This is tricky has this may
740      * not be supported by every vout. We though can't disable it
741      * because of some creepy drawable type that are not flexible enough
742      * (Win32 HWND for instance) */
743     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
744     if( !p_input_thread ) {
745         /* No input, nothing more to do, we are fine */
746         libvlc_exception_clear( p_e );
747         return;
748     }
749
750     p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD );
751     if( p_vout )
752     {
753         vout_Control( p_vout , VOUT_REPARENT, drawable);
754         vlc_object_release( p_vout );
755     }
756     vlc_object_release( p_input_thread );
757 }
758
759 /**************************************************************************
760  * Get Drawable
761  **************************************************************************/
762 libvlc_drawable_t
763 libvlc_media_player_get_drawable ( libvlc_media_player_t *p_mi, libvlc_exception_t *p_e )
764 {
765     VLC_UNUSED(p_e);
766     return p_mi->drawable.xid;
767 }
768
769 /**************************************************************************
770  * Getters for stream information
771  **************************************************************************/
772 libvlc_time_t libvlc_media_player_get_length(
773                              libvlc_media_player_t *p_mi,
774                              libvlc_exception_t *p_e )
775 {
776     input_thread_t *p_input_thread;
777     vlc_value_t val;
778
779     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
780     if( !p_input_thread )
781         return -1;
782
783     var_Get( p_input_thread, "length", &val );
784     vlc_object_release( p_input_thread );
785
786     return (val.i_time+500LL)/1000LL;
787 }
788
789 libvlc_time_t libvlc_media_player_get_time(
790                                    libvlc_media_player_t *p_mi,
791                                    libvlc_exception_t *p_e )
792 {
793     input_thread_t *p_input_thread;
794     vlc_value_t val;
795
796     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
797     if( !p_input_thread )
798         return -1;
799
800     var_Get( p_input_thread , "time", &val );
801     vlc_object_release( p_input_thread );
802     return (val.i_time+500LL)/1000LL;
803 }
804
805 void libvlc_media_player_set_time(
806                                  libvlc_media_player_t *p_mi,
807                                  libvlc_time_t time,
808                                  libvlc_exception_t *p_e )
809 {
810     input_thread_t *p_input_thread;
811     vlc_value_t value;
812
813     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
814     if( !p_input_thread )
815         return;
816
817     value.i_time = time*1000LL;
818     var_Set( p_input_thread, "time", value );
819     vlc_object_release( p_input_thread );
820 }
821
822 void libvlc_media_player_set_position(
823                                 libvlc_media_player_t *p_mi,
824                                 float position,
825                                 libvlc_exception_t *p_e )
826 {
827     input_thread_t *p_input_thread;
828     vlc_value_t val;
829     val.f_float = position;
830
831     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
832     if( !p_input_thread )
833         return;
834
835     var_Set( p_input_thread, "position", val );
836     vlc_object_release( p_input_thread );
837 }
838
839 float libvlc_media_player_get_position(
840                                  libvlc_media_player_t *p_mi,
841                                  libvlc_exception_t *p_e )
842 {
843     input_thread_t *p_input_thread;
844     vlc_value_t val;
845
846     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
847     if( !p_input_thread )
848         return -1.0;
849
850     var_Get( p_input_thread, "position", &val );
851     vlc_object_release( p_input_thread );
852
853     return val.f_float;
854 }
855
856 void libvlc_media_player_set_chapter(
857                                  libvlc_media_player_t *p_mi,
858                                  int chapter,
859                                  libvlc_exception_t *p_e )
860 {
861     input_thread_t *p_input_thread;
862     vlc_value_t val;
863     val.i_int = chapter;
864
865     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
866     if( !p_input_thread )
867         return;
868
869     var_Set( p_input_thread, "chapter", val );
870     vlc_object_release( p_input_thread );
871 }
872
873 int libvlc_media_player_get_chapter(
874                                  libvlc_media_player_t *p_mi,
875                                  libvlc_exception_t *p_e )
876 {
877     input_thread_t *p_input_thread;
878     vlc_value_t val;
879
880     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
881     if( !p_input_thread )
882         return -1;
883
884     var_Get( p_input_thread, "chapter", &val );
885     vlc_object_release( p_input_thread );
886
887     return val.i_int;
888 }
889
890 int libvlc_media_player_get_chapter_count(
891                                  libvlc_media_player_t *p_mi,
892                                  libvlc_exception_t *p_e )
893 {
894     input_thread_t *p_input_thread;
895     vlc_value_t val;
896
897     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
898     if( !p_input_thread )
899         return -1;
900
901     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
902     vlc_object_release( p_input_thread );
903
904     return val.i_int;
905 }
906
907 int libvlc_media_player_get_chapter_count_for_title(
908                                  libvlc_media_player_t *p_mi,
909                                  int i_title,
910                                  libvlc_exception_t *p_e )
911 {
912     input_thread_t *p_input_thread;
913     vlc_value_t val;
914
915     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
916     if( !p_input_thread )
917         return -1;
918
919     char *psz_name;
920     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
921     {
922         vlc_object_release( p_input_thread );
923         return -1;
924     }
925     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
926     vlc_object_release( p_input_thread );
927     free( psz_name );
928
929     return val.i_int;
930 }
931
932 void libvlc_media_player_set_title(
933                                  libvlc_media_player_t *p_mi,
934                                  int i_title,
935                                  libvlc_exception_t *p_e )
936 {
937     input_thread_t *p_input_thread;
938     vlc_value_t val;
939     val.i_int = i_title;
940
941     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
942     if( !p_input_thread )
943         return;
944
945     var_Set( p_input_thread, "title", val );
946     vlc_object_release( p_input_thread );
947
948     //send event
949     libvlc_event_t event;
950     event.type = libvlc_MediaPlayerTitleChanged;
951     event.u.media_player_title_changed.new_title = i_title;
952     libvlc_event_send( p_mi->p_event_manager, &event );
953 }
954
955 int libvlc_media_player_get_title(
956                                  libvlc_media_player_t *p_mi,
957                                  libvlc_exception_t *p_e )
958 {
959     input_thread_t *p_input_thread;
960     vlc_value_t val;
961
962     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
963     if( !p_input_thread )
964         return -1;
965
966     var_Get( p_input_thread, "title", &val );
967     vlc_object_release( p_input_thread );
968
969     return val.i_int;
970 }
971
972 int libvlc_media_player_get_title_count(
973                                  libvlc_media_player_t *p_mi,
974                                  libvlc_exception_t *p_e )
975 {
976     input_thread_t *p_input_thread;
977     vlc_value_t val;
978
979     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
980     if( !p_input_thread )
981         return -1;
982
983     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
984     vlc_object_release( p_input_thread );
985
986     return val.i_int;
987 }
988
989 void libvlc_media_player_next_chapter(
990                                  libvlc_media_player_t *p_mi,
991                                  libvlc_exception_t *p_e )
992 {
993     input_thread_t *p_input_thread;
994
995     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
996     if( !p_input_thread )
997         return;
998
999     int i_type = var_Type( p_input_thread, "next-chapter" );
1000     vlc_value_t val;
1001     val.b_bool = true;
1002     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1003                             "next-chapter":"next-title", val );
1004
1005     vlc_object_release( p_input_thread );
1006 }
1007
1008 void libvlc_media_player_previous_chapter(
1009                                  libvlc_media_player_t *p_mi,
1010                                  libvlc_exception_t *p_e )
1011 {
1012     input_thread_t *p_input_thread;
1013
1014     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1015     if( !p_input_thread )
1016         return;
1017
1018     int i_type = var_Type( p_input_thread, "next-chapter" );
1019     vlc_value_t val;
1020     val.b_bool = true;
1021     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1022                             "prev-chapter":"prev-title", val );
1023
1024     vlc_object_release( p_input_thread );
1025 }
1026
1027 float libvlc_media_player_get_fps(
1028                                  libvlc_media_player_t *p_mi,
1029                                  libvlc_exception_t *p_e)
1030 {
1031     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1032     double f_fps = 0.0;
1033
1034     if( p_input_thread )
1035     {
1036         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1037             f_fps = 0.0;
1038         vlc_object_release( p_input_thread );
1039     }
1040     return f_fps;
1041 }
1042
1043 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
1044                                      libvlc_exception_t *p_e)
1045 {
1046     input_thread_t *p_input_thread =
1047                             libvlc_get_input_thread ( p_mi, p_e);
1048     if ( !p_input_thread )
1049         return false;
1050
1051     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
1052     {
1053         vlc_object_release( p_input_thread );
1054         return true;
1055     }
1056     vlc_object_release( p_input_thread );
1057     return false;
1058 }
1059
1060 void libvlc_media_player_set_rate(
1061                                  libvlc_media_player_t *p_mi,
1062                                  float rate,
1063                                  libvlc_exception_t *p_e )
1064 {
1065     input_thread_t *p_input_thread;
1066     vlc_value_t val;
1067     bool b_can_rewind;
1068
1069     if( rate != 0 )
1070         RAISEVOID( "Rate value is invalid" );
1071
1072     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1073     if( !p_input_thread )
1074         return;
1075
1076     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1077     if( (rate < 0) && !b_can_rewind )
1078     {
1079         vlc_object_release( p_input_thread );
1080         libvlc_exception_raise( p_e, "Rate value is invalid" );
1081         return;
1082     }
1083
1084     val.i_int = 1000.0f/rate;
1085     var_Set( p_input_thread, "rate", val );
1086     vlc_object_release( p_input_thread );
1087 }
1088
1089 float libvlc_media_player_get_rate(
1090                                  libvlc_media_player_t *p_mi,
1091                                  libvlc_exception_t *p_e )
1092 {
1093     input_thread_t *p_input_thread;
1094     vlc_value_t val;
1095     bool b_can_rewind;
1096
1097     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1098     if( !p_input_thread )
1099         return 0.0;  /* rate < 0 indicates rewind */
1100
1101     var_Get( p_input_thread, "rate", &val );
1102     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1103     if( (val.i_int < 0) && !b_can_rewind )
1104     {
1105         libvlc_exception_raise( p_e, "invalid rate" );
1106         return 0.0;
1107     }
1108     vlc_object_release( p_input_thread );
1109
1110     return (float)1000.0f/val.i_int;
1111 }
1112
1113 libvlc_state_t libvlc_media_player_get_state(
1114                                  libvlc_media_player_t *p_mi,
1115                                  libvlc_exception_t *p_e )
1116 {
1117     input_thread_t *p_input_thread;
1118     vlc_value_t val;
1119
1120     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1121     if ( !p_input_thread )
1122     {
1123         /* We do return the right value, no need to throw an exception */
1124         if( libvlc_exception_raised( p_e ) )
1125             libvlc_exception_clear( p_e );
1126         return libvlc_Ended;
1127     }
1128
1129     var_Get( p_input_thread, "state", &val );
1130     vlc_object_release( p_input_thread );
1131
1132     return vlc_to_libvlc_state(val.i_int);
1133 }
1134
1135 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1136                                        libvlc_exception_t *p_e )
1137 {
1138     input_thread_t *p_input_thread;
1139     vlc_value_t val;
1140
1141     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1142     if ( !p_input_thread )
1143     {
1144         /* We do return the right value, no need to throw an exception */
1145         if( libvlc_exception_raised( p_e ) )
1146             libvlc_exception_clear( p_e );
1147         return false;
1148     }
1149     var_Get( p_input_thread, "can-seek", &val );
1150     vlc_object_release( p_input_thread );
1151
1152     return val.b_bool;
1153 }
1154
1155 /* internal function, used by audio, video */
1156 libvlc_track_description_t *
1157         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1158                                       const char *psz_variable,
1159                                       libvlc_exception_t *p_e )
1160 {
1161     input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
1162
1163     if( !p_input )
1164         return NULL;
1165
1166     vlc_value_t val_list, text_list;
1167     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1168
1169     if( val_list.p_list->i_count <= 0 ) /* no tracks */
1170         return NULL;
1171
1172     libvlc_track_description_t *p_track_description, *p_actual, *p_previous;
1173     p_track_description = ( libvlc_track_description_t * )
1174         malloc( sizeof( libvlc_track_description_t ) );
1175     if ( !p_track_description )
1176     {
1177         var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1178         vlc_object_release( p_input );
1179         libvlc_exception_raise( p_e, "no enough memory" );
1180         return NULL;
1181     }
1182     p_actual = p_track_description;
1183     p_previous = NULL;
1184     for( int i = 0; i < val_list.p_list->i_count; i++ )
1185     {
1186         if( !p_actual )
1187         {
1188             p_actual = ( libvlc_track_description_t * )
1189                 malloc( sizeof( libvlc_track_description_t ) );
1190             if ( !p_actual )
1191             {
1192                 libvlc_track_description_release( p_track_description );
1193                 var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1194                 vlc_object_release( p_input );
1195                 libvlc_exception_raise( p_e, "no enough memory" );
1196                 return NULL;
1197             }
1198         }
1199         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1200         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1201         p_actual->p_next = NULL;
1202         if( p_previous )
1203             p_previous->p_next = p_actual;
1204         p_previous = p_actual;
1205         p_actual =  NULL;
1206     }
1207     var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1208     vlc_object_release( p_input );
1209
1210     return p_track_description;
1211 }
1212
1213 void libvlc_track_description_release( libvlc_track_description_t *p_track_description )
1214 {
1215     libvlc_track_description_t *p_actual, *p_before;
1216     p_actual = p_track_description;
1217
1218     while ( p_actual )
1219     {
1220         free( p_actual->psz_name );
1221         p_before = p_actual;
1222         p_actual = p_before->p_next;
1223         free( p_before );
1224     }
1225 }
1226
1227 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1228                                      libvlc_exception_t *p_e )
1229 {
1230     input_thread_t *p_input_thread;
1231     vlc_value_t val;
1232
1233     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1234     if ( !p_input_thread )
1235     {
1236         /* We do return the right value, no need to throw an exception */
1237         if( libvlc_exception_raised( p_e ) )
1238             libvlc_exception_clear( p_e );
1239         return false;
1240     }
1241     var_Get( p_input_thread, "can-pause", &val );
1242     vlc_object_release( p_input_thread );
1243
1244     return val.b_bool;
1245 }