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