]> git.sesse.net Git - vlc/blob - src/control/media_player.c
7be73edb0e9d6732c9c0915051cfac9daa463765
[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 #ifndef WIN32
262     p_mi->drawable.xid = 0;
263 #else
264     p_mi->drawable.hwnd = NULL;
265 #endif
266     p_mi->p_libvlc_instance = p_libvlc_instance;
267     p_mi->p_input_thread = NULL;
268     /* refcount strategy:
269      * - All items created by _new start with a refcount set to 1
270      * - Accessor _release decrease the refcount by 1, if after that
271      *   operation the refcount is 0, the object is destroyed.
272      * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
273     p_mi->i_refcount = 1;
274     p_mi->b_own_its_input_thread = true;
275     /* object_lock strategy:
276      * - No lock held in constructor
277      * - Lock when accessing all variable this lock is held
278      * - Lock when attempting to destroy the object the lock is also held */
279     vlc_mutex_init( &p_mi->object_lock );
280     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
281             p_libvlc_instance, p_e );
282     if( libvlc_exception_raised( p_e ) )
283     {
284         free( p_mi );
285         return NULL;
286     }
287
288     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
289             libvlc_MediaPlayerNothingSpecial, p_e );
290     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
291             libvlc_MediaPlayerOpening, p_e );
292     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
293             libvlc_MediaPlayerBuffering, p_e );
294     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
295             libvlc_MediaPlayerPlaying, p_e );
296     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
297             libvlc_MediaPlayerPaused, p_e );
298     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
299             libvlc_MediaPlayerStopped, p_e );
300     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
301             libvlc_MediaPlayerForward, p_e );
302     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
303             libvlc_MediaPlayerBackward, p_e );
304     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
305             libvlc_MediaPlayerEndReached, p_e );
306     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
307             libvlc_MediaPlayerEncounteredError, p_e );
308
309     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
310             libvlc_MediaPlayerPositionChanged, p_e );
311     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
312             libvlc_MediaPlayerTimeChanged, p_e );
313      libvlc_event_manager_register_event_type( p_mi->p_event_manager,
314             libvlc_MediaPlayerTitleChanged, p_e );
315     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
316             libvlc_MediaPlayerSeekableChanged, p_e );
317     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
318             libvlc_MediaPlayerPausableChanged, p_e );
319
320     /* Snapshot initialization */
321     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
322            libvlc_MediaPlayerSnapshotTaken, p_e );
323     /* Attach a var callback to the global object to provide the glue between
324         vout_thread that generates the event and media_player that re-emits it
325         with its own event manager
326     */
327     var_Create( p_libvlc_instance->p_libvlc_int, "vout-snapshottaken", VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
328     var_AddCallback( p_libvlc_instance->p_libvlc_int, "vout-snapshottaken", SnapshotTakenCallback, p_mi );
329
330     return p_mi;
331 }
332
333 /**************************************************************************
334  * Create a Media Instance object with a media descriptor
335  **************************************************************************/
336 libvlc_media_player_t *
337 libvlc_media_player_new_from_media(
338                                     libvlc_media_t * p_md,
339                                     libvlc_exception_t *p_e )
340 {
341     libvlc_media_player_t * p_mi;
342     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance, p_e );
343
344     if( !p_mi )
345         return NULL;
346
347     libvlc_media_retain( p_md );
348     p_mi->p_md = p_md;
349
350     return p_mi;
351 }
352
353 /**************************************************************************
354  * Create a new media instance object from an input_thread (Libvlc Internal)
355  **************************************************************************/
356 libvlc_media_player_t * libvlc_media_player_new_from_input_thread(
357                                    struct libvlc_instance_t *p_libvlc_instance,
358                                    input_thread_t *p_input,
359                                    libvlc_exception_t *p_e )
360 {
361     libvlc_media_player_t * p_mi;
362
363     if( !p_input )
364     {
365         libvlc_exception_raise( p_e, "invalid input thread" );
366         return NULL;
367     }
368
369     p_mi = libvlc_media_player_new( p_libvlc_instance, p_e );
370
371     if( !p_mi )
372         return NULL;
373
374     p_mi->p_md = libvlc_media_new_from_input_item(
375                     p_libvlc_instance,
376                     input_GetItem( p_input ), p_e );
377
378     if( !p_mi->p_md )
379     {
380         libvlc_media_player_destroy( p_mi );
381         return NULL;
382     }
383
384     /* will be released in media_player_release() */
385     vlc_object_hold( p_input );
386
387     p_mi->p_input_thread = p_input;
388     p_mi->b_own_its_input_thread = false;
389
390     return p_mi;
391 }
392
393 /**************************************************************************
394  * Destroy a Media Instance object (libvlc internal)
395  *
396  * Warning: No lock held here, but hey, this is internal.
397  **************************************************************************/
398 void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
399 {
400     input_thread_t *p_input_thread;
401     libvlc_exception_t p_e;
402
403     libvlc_exception_init( &p_e );
404
405     if( !p_mi )
406         return;
407
408         /* Detach Callback from the main libvlc object */
409     var_DelCallback( p_mi->p_libvlc_instance->p_libvlc_int, "vout-snapshottaken", SnapshotTakenCallback, p_mi );
410
411     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
412
413     if( libvlc_exception_raised( &p_e ) )
414     {
415         libvlc_event_manager_release( p_mi->p_event_manager );
416         libvlc_exception_clear( &p_e );
417         free( p_mi );
418         return; /* no need to worry about no input thread */
419     }
420     vlc_mutex_destroy( &p_mi->object_lock );
421
422     vlc_object_release( p_input_thread );
423
424     libvlc_media_release( p_mi->p_md );
425
426     free( p_mi );
427 }
428
429 /**************************************************************************
430  * Release a Media Instance object
431  **************************************************************************/
432 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
433 {
434     if( !p_mi )
435         return;
436
437     vlc_mutex_lock( &p_mi->object_lock );
438
439     p_mi->i_refcount--;
440
441     if( p_mi->i_refcount > 0 )
442     {
443         vlc_mutex_unlock( &p_mi->object_lock );
444         return;
445     }
446     vlc_mutex_unlock( &p_mi->object_lock );
447     vlc_mutex_destroy( &p_mi->object_lock );
448
449     release_input_thread( p_mi );
450
451     libvlc_event_manager_release( p_mi->p_event_manager );
452
453     libvlc_media_release( p_mi->p_md );
454
455     free( p_mi );
456 }
457
458 /**************************************************************************
459  * Retain a Media Instance object
460  **************************************************************************/
461 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
462 {
463     if( !p_mi )
464         return;
465
466     p_mi->i_refcount++;
467 }
468
469 /**************************************************************************
470  * Set the Media descriptor associated with the instance
471  **************************************************************************/
472 void libvlc_media_player_set_media(
473                             libvlc_media_player_t *p_mi,
474                             libvlc_media_t *p_md,
475                             libvlc_exception_t *p_e )
476 {
477     VLC_UNUSED(p_e);
478
479     if( !p_mi )
480         return;
481
482     vlc_mutex_lock( &p_mi->object_lock );
483
484     release_input_thread( p_mi );
485
486     if( p_mi->p_md )
487         libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, p_e );
488
489     libvlc_media_release( p_mi->p_md );
490
491     if( !p_md )
492     {
493         p_mi->p_md = NULL;
494         vlc_mutex_unlock( &p_mi->object_lock );
495         return; /* It is ok to pass a NULL md */
496     }
497
498     libvlc_media_retain( p_md );
499     p_mi->p_md = p_md;
500
501     /* The policy here is to ignore that we were created using a different
502      * libvlc_instance, because we don't really care */
503     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
504
505     vlc_mutex_unlock( &p_mi->object_lock );
506 }
507
508 /**************************************************************************
509  * Get the Media descriptor associated with the instance
510  **************************************************************************/
511 libvlc_media_t *
512 libvlc_media_player_get_media(
513                             libvlc_media_player_t *p_mi,
514                             libvlc_exception_t *p_e )
515 {
516     VLC_UNUSED(p_e);
517
518     if( !p_mi->p_md )
519         return NULL;
520
521     libvlc_media_retain( p_mi->p_md );
522     return p_mi->p_md;
523 }
524
525 /**************************************************************************
526  * Get the event Manager
527  **************************************************************************/
528 libvlc_event_manager_t *
529 libvlc_media_player_event_manager(
530                             libvlc_media_player_t *p_mi,
531                             libvlc_exception_t *p_e )
532 {
533     VLC_UNUSED(p_e);
534
535     return p_mi->p_event_manager;
536 }
537
538 /**************************************************************************
539  * Trigger a snapshot Taken Event
540  *************************************************************************/
541 static int SnapshotTakenCallback( vlc_object_t *p_this, char const *psz_cmd,
542                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
543 {
544     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
545     VLC_UNUSED(p_this) ;
546
547     libvlc_media_player_t* p_mi = (libvlc_media_player_t*) p_data ;
548     libvlc_event_t event ;
549     event.type = libvlc_MediaPlayerSnapshotTaken ;
550     event.u.media_player_snapshot_taken.psz_filename = newval.psz_string ;
551     /* Snapshot psz data is a vlc_variable owned by libvlc object .
552          Its memmory management is taken care by the obj*/
553     msg_Dbg( p_this, "about to emit libvlc_snapshot_taken.make psz_str=0x%p (%s)",
554           event.u.media_player_snapshot_taken.psz_filename ,event.u.media_player_snapshot_taken.psz_filename );
555     libvlc_event_send( p_mi->p_event_manager, &event );
556
557     return VLC_SUCCESS;
558 }
559
560 /**************************************************************************
561  * Play
562  **************************************************************************/
563 void libvlc_media_player_play( libvlc_media_player_t *p_mi,
564                                  libvlc_exception_t *p_e )
565 {
566     input_thread_t * p_input_thread;
567
568     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
569     {
570         /* A thread already exists, send it a play message */
571         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
572         vlc_object_release( p_input_thread );
573         return;
574     }
575
576     /* Ignore previous exception */
577     libvlc_exception_clear( p_e );
578
579     vlc_mutex_lock( &p_mi->object_lock );
580
581     if( !p_mi->p_md )
582     {
583         libvlc_exception_raise( p_e, "no associated media descriptor" );
584         vlc_mutex_unlock( &p_mi->object_lock );
585         return;
586     }
587
588     p_mi->p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
589                       p_mi->p_md->p_input_item );
590
591
592     if( !p_mi->p_input_thread )
593     {
594         vlc_mutex_unlock( &p_mi->object_lock );
595         return;
596     }
597
598     p_input_thread = p_mi->p_input_thread;
599
600     var_Create( p_input_thread, "drawable-xid", VLC_VAR_INTEGER );
601     var_Create( p_input_thread, "drawable-hwnd", VLC_VAR_ADDRESS );
602
603 #ifndef WIN32
604     var_SetInteger( p_input_thread, "drawable-xid", p_mi->drawable.xid );
605 #else
606     var_SetInteger( p_input_thread, "drawable-hwnd", p_mi->drawable.hwnd );
607 #endif
608
609     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
610     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
611     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
612
613     vlc_mutex_unlock( &p_mi->object_lock );
614 }
615
616 /**************************************************************************
617  * Pause
618  **************************************************************************/
619 void libvlc_media_player_pause( libvlc_media_player_t *p_mi,
620                                   libvlc_exception_t *p_e )
621 {
622     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
623
624     if( !p_input_thread )
625         return;
626
627     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
628
629     if( state == libvlc_Playing )
630     {
631         if( libvlc_media_player_can_pause( p_mi, p_e ) )
632             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
633         else
634             libvlc_media_player_stop( p_mi, p_e );
635     }
636     else
637         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
638
639     vlc_object_release( p_input_thread );
640 }
641
642 /**************************************************************************
643  * is_playing
644  **************************************************************************/
645 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi,
646                                      libvlc_exception_t *p_e )
647 {
648     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
649
650     if( !p_input_thread )
651         return 0;
652
653     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
654
655     vlc_object_release( p_input_thread );
656     
657     if( state == libvlc_Playing )
658     {
659         return 1;
660     }
661     return 0;
662 }
663
664
665 /**************************************************************************
666  * Stop
667  **************************************************************************/
668 void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
669                                  libvlc_exception_t *p_e )
670 {
671     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
672
673     if( state == libvlc_Playing || state == libvlc_Paused )
674     {
675         /* Send a stop notification event only of we are in playing or paused states */
676         libvlc_media_set_state( p_mi->p_md, libvlc_Ended, p_e );
677
678         /* Construct and send the event */
679         libvlc_event_t event;
680         event.type = libvlc_MediaPlayerStopped;
681         libvlc_event_send( p_mi->p_event_manager, &event );
682     }
683
684     if( p_mi->b_own_its_input_thread )
685     {
686         vlc_mutex_lock( &p_mi->object_lock );
687         release_input_thread( p_mi ); /* This will stop the input thread */
688         vlc_mutex_unlock( &p_mi->object_lock );
689     }
690     else
691     {
692         input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
693
694         if( !p_input_thread )
695             return;
696
697         input_StopThread( p_input_thread );
698         vlc_object_release( p_input_thread );
699     }
700 }
701
702 /**************************************************************************
703  * Set Drawable
704  **************************************************************************/
705 void libvlc_media_player_set_drawable( libvlc_media_player_t *p_mi,
706                                        libvlc_drawable_t drawable,
707                                        libvlc_exception_t *p_e )
708 {
709     input_thread_t *p_input_thread;
710     vout_thread_t *p_vout = NULL;
711
712     p_mi->drawable.xid = drawable;
713
714     /* Allow on the fly drawable changing. This is tricky has this may
715      * not be supported by every vout. We though can't disable it
716      * because of some creepy drawable type that are not flexible enough
717      * (Win32 HWND for instance) */
718     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
719     if( !p_input_thread ) {
720         /* No input, nothing more to do, we are fine */
721         libvlc_exception_clear( p_e );
722         return;
723     }
724
725     p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD );
726     if( p_vout )
727     {
728         vout_Control( p_vout , VOUT_REPARENT, drawable);
729         vlc_object_release( p_vout );
730     }
731     vlc_object_release( p_input_thread );
732 }
733
734 /**************************************************************************
735  * Get Drawable
736  **************************************************************************/
737 libvlc_drawable_t
738 libvlc_media_player_get_drawable ( libvlc_media_player_t *p_mi, libvlc_exception_t *p_e )
739 {
740     VLC_UNUSED(p_e);
741     return p_mi->drawable.xid;
742 }
743
744 /**************************************************************************
745  * Getters for stream information
746  **************************************************************************/
747 libvlc_time_t libvlc_media_player_get_length(
748                              libvlc_media_player_t *p_mi,
749                              libvlc_exception_t *p_e )
750 {
751     input_thread_t *p_input_thread;
752     vlc_value_t val;
753
754     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
755     if( !p_input_thread )
756         return -1;
757
758     var_Get( p_input_thread, "length", &val );
759     vlc_object_release( p_input_thread );
760
761     return (val.i_time+500LL)/1000LL;
762 }
763
764 libvlc_time_t libvlc_media_player_get_time(
765                                    libvlc_media_player_t *p_mi,
766                                    libvlc_exception_t *p_e )
767 {
768     input_thread_t *p_input_thread;
769     vlc_value_t val;
770
771     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
772     if( !p_input_thread )
773         return -1;
774
775     var_Get( p_input_thread , "time", &val );
776     vlc_object_release( p_input_thread );
777     return (val.i_time+500LL)/1000LL;
778 }
779
780 void libvlc_media_player_set_time(
781                                  libvlc_media_player_t *p_mi,
782                                  libvlc_time_t time,
783                                  libvlc_exception_t *p_e )
784 {
785     input_thread_t *p_input_thread;
786     vlc_value_t value;
787
788     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
789     if( !p_input_thread )
790         return;
791
792     value.i_time = time*1000LL;
793     var_Set( p_input_thread, "time", value );
794     vlc_object_release( p_input_thread );
795 }
796
797 void libvlc_media_player_set_position(
798                                 libvlc_media_player_t *p_mi,
799                                 float position,
800                                 libvlc_exception_t *p_e )
801 {
802     input_thread_t *p_input_thread;
803     vlc_value_t val;
804     val.f_float = position;
805
806     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
807     if( !p_input_thread )
808         return;
809
810     var_Set( p_input_thread, "position", val );
811     vlc_object_release( p_input_thread );
812 }
813
814 float libvlc_media_player_get_position(
815                                  libvlc_media_player_t *p_mi,
816                                  libvlc_exception_t *p_e )
817 {
818     input_thread_t *p_input_thread;
819     vlc_value_t val;
820
821     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
822     if( !p_input_thread )
823         return -1.0;
824
825     var_Get( p_input_thread, "position", &val );
826     vlc_object_release( p_input_thread );
827
828     return val.f_float;
829 }
830
831 void libvlc_media_player_set_chapter(
832                                  libvlc_media_player_t *p_mi,
833                                  int chapter,
834                                  libvlc_exception_t *p_e )
835 {
836     input_thread_t *p_input_thread;
837     vlc_value_t val;
838     val.i_int = chapter;
839
840     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
841     if( !p_input_thread )
842         return;
843
844     var_Set( p_input_thread, "chapter", val );
845     vlc_object_release( p_input_thread );
846 }
847
848 int libvlc_media_player_get_chapter(
849                                  libvlc_media_player_t *p_mi,
850                                  libvlc_exception_t *p_e )
851 {
852     input_thread_t *p_input_thread;
853     vlc_value_t val;
854
855     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
856     if( !p_input_thread )
857         return -1;
858
859     var_Get( p_input_thread, "chapter", &val );
860     vlc_object_release( p_input_thread );
861
862     return val.i_int;
863 }
864
865 int libvlc_media_player_get_chapter_count(
866                                  libvlc_media_player_t *p_mi,
867                                  libvlc_exception_t *p_e )
868 {
869     input_thread_t *p_input_thread;
870     vlc_value_t val;
871
872     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
873     if( !p_input_thread )
874         return -1;
875
876     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
877     vlc_object_release( p_input_thread );
878
879     return val.i_int;
880 }
881
882 int libvlc_media_player_get_chapter_count_for_title(
883                                  libvlc_media_player_t *p_mi,
884                                  int i_title,
885                                  libvlc_exception_t *p_e )
886 {
887     input_thread_t *p_input_thread;
888     vlc_value_t val;
889
890     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
891     if( !p_input_thread )
892         return -1;
893
894     char *psz_name;
895     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
896     {
897         vlc_object_release( p_input_thread );
898         return -1;
899     }
900     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
901     vlc_object_release( p_input_thread );
902     free( psz_name );
903
904     return val.i_int;
905 }
906
907 void libvlc_media_player_set_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     val.i_int = i_title;
915
916     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
917     if( !p_input_thread )
918         return;
919
920     var_Set( p_input_thread, "title", val );
921     vlc_object_release( p_input_thread );
922
923     //send event
924     libvlc_event_t event;
925     event.type = libvlc_MediaPlayerTitleChanged;
926     event.u.media_player_title_changed.new_title = i_title;
927     libvlc_event_send( p_mi->p_event_manager, &event );
928 }
929
930 int libvlc_media_player_get_title(
931                                  libvlc_media_player_t *p_mi,
932                                  libvlc_exception_t *p_e )
933 {
934     input_thread_t *p_input_thread;
935     vlc_value_t val;
936
937     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
938     if( !p_input_thread )
939         return -1;
940
941     var_Get( p_input_thread, "title", &val );
942     vlc_object_release( p_input_thread );
943
944     return val.i_int;
945 }
946
947 int libvlc_media_player_get_title_count(
948                                  libvlc_media_player_t *p_mi,
949                                  libvlc_exception_t *p_e )
950 {
951     input_thread_t *p_input_thread;
952     vlc_value_t val;
953
954     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
955     if( !p_input_thread )
956         return -1;
957
958     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
959     vlc_object_release( p_input_thread );
960
961     return val.i_int;
962 }
963
964 void libvlc_media_player_next_chapter(
965                                  libvlc_media_player_t *p_mi,
966                                  libvlc_exception_t *p_e )
967 {
968     input_thread_t *p_input_thread;
969
970     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
971     if( !p_input_thread )
972         return;
973
974     int i_type = var_Type( p_input_thread, "next-chapter" );
975     vlc_value_t val;
976     val.b_bool = true;
977     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
978                             "next-chapter":"next-title", val );
979
980     vlc_object_release( p_input_thread );
981 }
982
983 void libvlc_media_player_previous_chapter(
984                                  libvlc_media_player_t *p_mi,
985                                  libvlc_exception_t *p_e )
986 {
987     input_thread_t *p_input_thread;
988
989     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
990     if( !p_input_thread )
991         return;
992
993     int i_type = var_Type( p_input_thread, "next-chapter" );
994     vlc_value_t val;
995     val.b_bool = true;
996     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
997                             "prev-chapter":"prev-title", val );
998
999     vlc_object_release( p_input_thread );
1000 }
1001
1002 float libvlc_media_player_get_fps(
1003                                  libvlc_media_player_t *p_mi,
1004                                  libvlc_exception_t *p_e)
1005 {
1006     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1007     double f_fps = 0.0;
1008
1009     if( p_input_thread )
1010     {
1011         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1012             f_fps = 0.0;
1013         vlc_object_release( p_input_thread );
1014     }
1015     return f_fps;
1016 }
1017
1018 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
1019                                      libvlc_exception_t *p_e)
1020 {
1021     input_thread_t *p_input_thread =
1022                             libvlc_get_input_thread ( p_mi, p_e);
1023     if ( !p_input_thread )
1024         return false;
1025
1026     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
1027     {
1028         vlc_object_release( p_input_thread );
1029         return true;
1030     }
1031     vlc_object_release( p_input_thread );
1032     return false;
1033 }
1034
1035 void libvlc_media_player_set_rate(
1036                                  libvlc_media_player_t *p_mi,
1037                                  float rate,
1038                                  libvlc_exception_t *p_e )
1039 {
1040     input_thread_t *p_input_thread;
1041     vlc_value_t val;
1042     bool b_can_rewind;
1043
1044     if( rate != 0 )
1045         RAISEVOID( "Rate value is invalid" );
1046
1047     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1048     if( !p_input_thread )
1049         return;
1050
1051     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1052     if( (rate < 0) && !b_can_rewind )
1053     {
1054         vlc_object_release( p_input_thread );
1055         libvlc_exception_raise( p_e, "Rate value is invalid" );
1056         return;
1057     }
1058
1059     val.i_int = 1000.0f/rate;
1060     var_Set( p_input_thread, "rate", val );
1061     vlc_object_release( p_input_thread );
1062 }
1063
1064 float libvlc_media_player_get_rate(
1065                                  libvlc_media_player_t *p_mi,
1066                                  libvlc_exception_t *p_e )
1067 {
1068     input_thread_t *p_input_thread;
1069     vlc_value_t val;
1070     bool b_can_rewind;
1071
1072     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1073     if( !p_input_thread )
1074         return 0.0;  /* rate < 0 indicates rewind */
1075
1076     var_Get( p_input_thread, "rate", &val );
1077     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1078     if( (val.i_int < 0) && !b_can_rewind )
1079     {
1080         libvlc_exception_raise( p_e, "invalid rate" );
1081         return 0.0;
1082     }
1083     vlc_object_release( p_input_thread );
1084
1085     return (float)1000.0f/val.i_int;
1086 }
1087
1088 libvlc_state_t libvlc_media_player_get_state(
1089                                  libvlc_media_player_t *p_mi,
1090                                  libvlc_exception_t *p_e )
1091 {
1092     input_thread_t *p_input_thread;
1093     vlc_value_t val;
1094
1095     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1096     if ( !p_input_thread )
1097     {
1098         /* We do return the right value, no need to throw an exception */
1099         if( libvlc_exception_raised( p_e ) )
1100             libvlc_exception_clear( p_e );
1101         return libvlc_Ended;
1102     }
1103
1104     var_Get( p_input_thread, "state", &val );
1105     vlc_object_release( p_input_thread );
1106
1107     return vlc_to_libvlc_state(val.i_int);
1108 }
1109
1110 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1111                                        libvlc_exception_t *p_e )
1112 {
1113     input_thread_t *p_input_thread;
1114     vlc_value_t val;
1115
1116     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1117     if ( !p_input_thread )
1118     {
1119         /* We do return the right value, no need to throw an exception */
1120         if( libvlc_exception_raised( p_e ) )
1121             libvlc_exception_clear( p_e );
1122         return false;
1123     }
1124     var_Get( p_input_thread, "can-seek", &val );
1125     vlc_object_release( p_input_thread );
1126
1127     return val.b_bool;
1128 }
1129
1130 /* internal function, used by audio, video */
1131 libvlc_track_description_t *
1132         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1133                                       const char *psz_variable,
1134                                       libvlc_exception_t *p_e )
1135 {
1136     input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
1137
1138     if( !p_input )
1139         return NULL;
1140
1141     vlc_value_t val_list, text_list;
1142     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1143
1144     if( val_list.p_list->i_count <= 0 ) /* no tracks */
1145         return NULL;
1146
1147     libvlc_track_description_t *p_track_description, *p_actual, *p_previous;
1148     p_track_description = ( libvlc_track_description_t * )
1149         malloc( sizeof( libvlc_track_description_t ) );
1150     if ( !p_track_description )
1151     {
1152         var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1153         vlc_object_release( p_input );
1154         libvlc_exception_raise( p_e, "no enough memory" );
1155         return NULL;
1156     }
1157     p_actual = p_track_description;
1158     p_previous = NULL;
1159     for( int i = 0; i < val_list.p_list->i_count; i++ )
1160     {
1161         if( !p_actual )
1162         {
1163             p_actual = ( libvlc_track_description_t * )
1164                 malloc( sizeof( libvlc_track_description_t ) );
1165             if ( !p_actual )
1166             {
1167                 libvlc_track_description_release( p_track_description );
1168                 var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1169                 vlc_object_release( p_input );
1170                 libvlc_exception_raise( p_e, "no enough memory" );
1171                 return NULL;
1172             }
1173         }
1174         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1175         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1176         p_actual->p_next = NULL;
1177         if( p_previous )
1178             p_previous->p_next = p_actual;
1179         p_previous = p_actual;
1180         p_actual =  NULL;
1181     }
1182     var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1183     vlc_object_release( p_input );
1184
1185     return p_track_description;
1186 }
1187
1188 void libvlc_track_description_release( libvlc_track_description_t *p_track_description )
1189 {
1190     libvlc_track_description_t *p_actual, *p_before;
1191     p_actual = p_track_description;
1192
1193     while ( p_actual )
1194     {
1195         free( p_actual->psz_name );
1196         p_before = p_actual;
1197         p_actual = p_before->p_next;
1198         free( p_before );
1199     }
1200 }
1201
1202 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1203                                      libvlc_exception_t *p_e )
1204 {
1205     input_thread_t *p_input_thread;
1206     vlc_value_t val;
1207
1208     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1209     if ( !p_input_thread )
1210     {
1211         /* We do return the right value, no need to throw an exception */
1212         if( libvlc_exception_raised( p_e ) )
1213             libvlc_exception_clear( p_e );
1214         return false;
1215     }
1216     var_Get( p_input_thread, "can-pause", &val );
1217     vlc_object_release( p_input_thread );
1218
1219     return val.b_bool;
1220 }