Use var_CountChoices instead of var_Change(VLC_VAR_GETCHOICES) when applicable.
[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     if( p_mi->p_md )
395         libvlc_media_release( p_mi->p_md );
396     vlc_mutex_destroy( &p_mi->object_lock );
397     free( p_mi );
398 }
399
400 /**************************************************************************
401  * Release a Media Instance object.
402  *
403  * Function does the locking.
404  **************************************************************************/
405 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
406 {
407     bool destroy;
408
409     assert( p_mi );
410     vlc_mutex_lock( &p_mi->object_lock );
411     destroy = !--p_mi->i_refcount;
412     vlc_mutex_unlock( &p_mi->object_lock );
413
414     if( destroy )
415         libvlc_media_player_destroy( p_mi );
416 }
417
418 /**************************************************************************
419  * Retain a Media Instance object.
420  *
421  * Caller must hold the lock.
422  **************************************************************************/
423 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
424 {
425     assert( p_mi );
426
427     vlc_mutex_lock( &p_mi->object_lock );
428     p_mi->i_refcount++;
429     vlc_mutex_unlock( &p_mi->object_lock );
430 }
431
432 /**************************************************************************
433  * Set the Media descriptor associated with the instance.
434  *
435  * Enter without lock -- function will lock the object.
436  **************************************************************************/
437 void libvlc_media_player_set_media(
438                             libvlc_media_player_t *p_mi,
439                             libvlc_media_t *p_md,
440                             libvlc_exception_t *p_e )
441 {
442     VLC_UNUSED(p_e);
443
444     if( !p_mi )
445         return;
446
447     vlc_mutex_lock( &p_mi->object_lock );
448
449     /* FIXME I am not sure if it is a user request or on die(eof/error)
450      * request here */
451     release_input_thread( p_mi,
452                           p_mi->p_input_thread &&
453                           !p_mi->p_input_thread->b_eof &&
454                           !p_mi->p_input_thread->b_error );
455
456     if( p_mi->p_md )
457         libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, p_e );
458
459     libvlc_media_release( p_mi->p_md );
460
461     if( !p_md )
462     {
463         p_mi->p_md = NULL;
464         vlc_mutex_unlock( &p_mi->object_lock );
465         return; /* It is ok to pass a NULL md */
466     }
467
468     libvlc_media_retain( p_md );
469     p_mi->p_md = p_md;
470
471     /* The policy here is to ignore that we were created using a different
472      * libvlc_instance, because we don't really care */
473     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
474
475     vlc_mutex_unlock( &p_mi->object_lock );
476 }
477
478 /**************************************************************************
479  * Get the Media descriptor associated with the instance.
480  **************************************************************************/
481 libvlc_media_t *
482 libvlc_media_player_get_media(
483                             libvlc_media_player_t *p_mi,
484                             libvlc_exception_t *p_e )
485 {
486     VLC_UNUSED(p_e);
487
488     if( !p_mi->p_md )
489         return NULL;
490
491     libvlc_media_retain( p_mi->p_md );
492     return p_mi->p_md;
493 }
494
495 /**************************************************************************
496  * Get the event Manager.
497  **************************************************************************/
498 libvlc_event_manager_t *
499 libvlc_media_player_event_manager(
500                             libvlc_media_player_t *p_mi,
501                             libvlc_exception_t *p_e )
502 {
503     VLC_UNUSED(p_e);
504
505     return p_mi->p_event_manager;
506 }
507
508 /**************************************************************************
509  * Trigger a snapshot Taken Event.
510  *************************************************************************/
511 static int SnapshotTakenCallback( vlc_object_t *p_this, char const *psz_cmd,
512                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
513 {
514     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval);
515     VLC_UNUSED(p_this) ;
516
517     libvlc_media_player_t* p_mi = (libvlc_media_player_t*) p_data ;
518     libvlc_event_t event ;
519     event.type = libvlc_MediaPlayerSnapshotTaken ;
520     event.u.media_player_snapshot_taken.psz_filename = newval.psz_string ;
521     /* Snapshot psz data is a vlc_variable owned by libvlc object .
522          Its memmory management is taken care by the obj*/
523     msg_Dbg( p_this, "about to emit libvlc_snapshot_taken.make psz_str=0x%p"
524              " (%s)", event.u.media_player_snapshot_taken.psz_filename,
525              event.u.media_player_snapshot_taken.psz_filename );
526     libvlc_event_send( p_mi->p_event_manager, &event );
527
528     return VLC_SUCCESS;
529 }
530
531 /**************************************************************************
532  * Tell media player to start playing.
533  **************************************************************************/
534 void libvlc_media_player_play( libvlc_media_player_t *p_mi,
535                                  libvlc_exception_t *p_e )
536 {
537     input_thread_t * p_input_thread;
538
539     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
540     {
541         /* A thread already exists, send it a play message */
542         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
543         vlc_object_release( p_input_thread );
544         return;
545     }
546
547     /* Ignore previous exception */
548     libvlc_exception_clear( p_e );
549
550     vlc_mutex_lock( &p_mi->object_lock );
551
552     if( !p_mi->p_md )
553     {
554         libvlc_exception_raise( p_e, "no associated media descriptor" );
555         vlc_mutex_unlock( &p_mi->object_lock );
556         return;
557     }
558
559     p_mi->p_input_thread = input_Create( p_mi->p_libvlc_instance->p_libvlc_int,
560                                          p_mi->p_md->p_input_item, NULL, NULL );
561
562     if( !p_mi->p_input_thread )
563     {
564         vlc_mutex_unlock( &p_mi->object_lock );
565         return;
566     }
567
568     p_input_thread = p_mi->p_input_thread;
569
570     var_Create( p_input_thread, "drawable-agl", VLC_VAR_INTEGER );
571     if( p_mi->drawable.agl )
572         var_SetInteger( p_input_thread, "drawable-agl", p_mi->drawable.agl );
573
574     var_Create( p_input_thread, "drawable-xid", VLC_VAR_INTEGER );
575     if( p_mi->drawable.xid )
576         var_SetInteger( p_input_thread, "drawable-xid", p_mi->drawable.xid );
577
578     var_Create( p_input_thread, "drawable-hwnd", VLC_VAR_ADDRESS );
579     if( p_mi->drawable.hwnd != NULL )
580     {
581         vlc_value_t val = { .p_address = p_mi->drawable.hwnd };
582         var_Set( p_input_thread, "drawable-hwnd", val );
583     }
584
585         var_Create( p_input_thread, "drawable-nsobject", VLC_VAR_ADDRESS );
586     if( p_mi->drawable.nsobject != NULL )
587     {
588         vlc_value_t val = { .p_address = p_mi->drawable.nsobject };
589         var_Set( p_input_thread, "drawable-nsobject", val );
590     }
591         
592     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
593     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
594     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
595
596     if( input_Start( p_input_thread ) )
597     {
598         vlc_object_release( p_input_thread );
599         p_mi->p_input_thread = NULL;
600     }
601
602     vlc_mutex_unlock( &p_mi->object_lock );
603 }
604
605 /**************************************************************************
606  * Pause.
607  **************************************************************************/
608 void libvlc_media_player_pause( libvlc_media_player_t *p_mi,
609                                   libvlc_exception_t *p_e )
610 {
611     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
612     if( !p_input_thread )
613         return;
614
615     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
616     if( state == libvlc_Playing || state == libvlc_Buffering )
617     {
618         if( libvlc_media_player_can_pause( p_mi, p_e ) )
619             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
620         else
621             libvlc_media_player_stop( p_mi, p_e );
622     }
623     else
624         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
625
626     vlc_object_release( p_input_thread );
627 }
628
629 /**************************************************************************
630  * Tells whether the media player is currently playing.
631  *
632  * Enter with lock held.
633  **************************************************************************/
634 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi,
635                                      libvlc_exception_t *p_e )
636 {
637     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
638     return (libvlc_Playing == state) || (libvlc_Buffering == state);
639 }
640
641 /**************************************************************************
642  * Stop playing.
643  **************************************************************************/
644 void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
645                                  libvlc_exception_t *p_e )
646 {
647     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
648
649     if( state == libvlc_Playing ||
650         state == libvlc_Paused ||
651         state == libvlc_Buffering )
652     {
653         /* Send a stop notification event only if we are in playing,
654          * buffering or paused states */
655         libvlc_media_set_state( p_mi->p_md, libvlc_Ended, p_e );
656
657         /* Construct and send the event */
658         libvlc_event_t event;
659         event.type = libvlc_MediaPlayerStopped;
660         libvlc_event_send( p_mi->p_event_manager, &event );
661     }
662
663     vlc_mutex_lock( &p_mi->object_lock );
664     release_input_thread( p_mi, true ); /* This will stop the input thread */
665     vlc_mutex_unlock( &p_mi->object_lock );
666 }
667
668 /**************************************************************************
669  * set_nsobject
670  **************************************************************************/
671 void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
672                                         void * drawable,
673                                         libvlc_exception_t *p_e )
674 {
675     (void) p_e;
676     p_mi->drawable.nsobject = drawable;
677 }
678
679 /**************************************************************************
680  * get_nsobject
681  **************************************************************************/
682 uint32_t libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
683 {
684     return p_mi->drawable.nsobject;
685 }
686
687 /**************************************************************************
688  * set_agl
689  **************************************************************************/
690 void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
691                                       uint32_t drawable,
692                                       libvlc_exception_t *p_e )
693 {
694     (void) p_e;
695     p_mi->drawable.agl = drawable;
696 }
697
698 /**************************************************************************
699  * get_agl
700  **************************************************************************/
701 uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
702 {
703     return p_mi->drawable.agl;
704 }
705
706 /**************************************************************************
707  * set_xwindow
708  **************************************************************************/
709 void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
710                                       uint32_t drawable,
711                                       libvlc_exception_t *p_e )
712 {
713     (void) p_e;
714     p_mi->drawable.xid = drawable;
715 }
716
717 /**************************************************************************
718  * get_xwindow
719  **************************************************************************/
720 uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
721 {
722     return p_mi->drawable.xid;
723 }
724
725 /**************************************************************************
726  * set_hwnd
727  **************************************************************************/
728 void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
729                                    void *drawable,
730                                    libvlc_exception_t *p_e )
731 {
732     (void) p_e;
733     p_mi->drawable.hwnd = drawable;
734 }
735
736 /**************************************************************************
737  * get_hwnd
738  **************************************************************************/
739 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
740 {
741     return p_mi->drawable.hwnd;
742 }
743
744 /**************************************************************************
745  * Set Drawable
746  **************************************************************************/
747 void libvlc_media_player_set_drawable( libvlc_media_player_t *p_mi,
748                                        libvlc_drawable_t drawable,
749                                        libvlc_exception_t *p_e )
750 {
751 #ifdef WIN32
752     if (sizeof (HWND) <= sizeof (libvlc_drawable_t))
753         p_mi->drawable.hwnd = (HWND)drawable;
754     else
755         libvlc_exception_raise(p_e, "Operation not supported");
756 #elif defined(__APPLE__)
757     p_mi->drawable.agl = drawable;
758     (void) p_e;
759 #else
760     p_mi->drawable.xid = drawable;
761     (void) p_e;
762 #endif
763 }
764
765 /**************************************************************************
766  * Get Drawable
767  **************************************************************************/
768 libvlc_drawable_t
769 libvlc_media_player_get_drawable ( libvlc_media_player_t *p_mi,
770                                    libvlc_exception_t *p_e )
771 {
772     VLC_UNUSED(p_e);
773
774 #ifdef WIN32
775     if (sizeof (HWND) <= sizeof (libvlc_drawable_t))
776         return (libvlc_drawable_t)p_mi->drawable.hwnd;
777     else
778         return 0;
779 #elif defined(__APPLE__)
780     return p_mi->drawable.agl;
781 #else
782     return p_mi->drawable.xid;
783 #endif
784 }
785
786 /**************************************************************************
787  * Getters for stream information
788  **************************************************************************/
789 libvlc_time_t libvlc_media_player_get_length(
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, "length", &val );
801     vlc_object_release( p_input_thread );
802
803     return (val.i_time+500LL)/1000LL;
804 }
805
806 libvlc_time_t libvlc_media_player_get_time(
807                                    libvlc_media_player_t *p_mi,
808                                    libvlc_exception_t *p_e )
809 {
810     input_thread_t *p_input_thread;
811     vlc_value_t val;
812
813     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
814     if( !p_input_thread )
815         return -1;
816
817     var_Get( p_input_thread , "time", &val );
818     vlc_object_release( p_input_thread );
819     return (val.i_time+500LL)/1000LL;
820 }
821
822 void libvlc_media_player_set_time(
823                                  libvlc_media_player_t *p_mi,
824                                  libvlc_time_t time,
825                                  libvlc_exception_t *p_e )
826 {
827     input_thread_t *p_input_thread;
828
829     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
830     if( !p_input_thread )
831         return;
832
833     var_SetTime( p_input_thread, "time", time*1000LL );
834     vlc_object_release( p_input_thread );
835 }
836
837 void libvlc_media_player_set_position(
838                                 libvlc_media_player_t *p_mi,
839                                 float position,
840                                 libvlc_exception_t *p_e )
841 {
842     input_thread_t *p_input_thread;
843
844     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
845     if( !p_input_thread )
846         return;
847
848     var_SetFloat( p_input_thread, "position", position );
849     vlc_object_release( p_input_thread );
850 }
851
852 float libvlc_media_player_get_position(
853                                  libvlc_media_player_t *p_mi,
854                                  libvlc_exception_t *p_e )
855 {
856     input_thread_t *p_input_thread;
857     vlc_value_t val;
858
859     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
860     if( !p_input_thread )
861         return -1.0;
862
863     var_Get( p_input_thread, "position", &val );
864     vlc_object_release( p_input_thread );
865
866     return val.f_float;
867 }
868
869 void libvlc_media_player_set_chapter(
870                                  libvlc_media_player_t *p_mi,
871                                  int chapter,
872                                  libvlc_exception_t *p_e )
873 {
874     input_thread_t *p_input_thread;
875
876     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
877     if( !p_input_thread )
878         return;
879
880     var_SetInteger( p_input_thread, "chapter", chapter );
881     vlc_object_release( p_input_thread );
882 }
883
884 int libvlc_media_player_get_chapter(
885                                  libvlc_media_player_t *p_mi,
886                                  libvlc_exception_t *p_e )
887 {
888     input_thread_t *p_input_thread;
889     vlc_value_t val;
890
891     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
892     if( !p_input_thread )
893         return -1;
894
895     var_Get( p_input_thread, "chapter", &val );
896     vlc_object_release( p_input_thread );
897
898     return val.i_int;
899 }
900
901 int libvlc_media_player_get_chapter_count(
902                                  libvlc_media_player_t *p_mi,
903                                  libvlc_exception_t *p_e )
904 {
905     input_thread_t *p_input_thread;
906     vlc_value_t val;
907
908     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
909     if( !p_input_thread )
910         return -1;
911
912     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
913     vlc_object_release( p_input_thread );
914
915     return val.i_int;
916 }
917
918 int libvlc_media_player_get_chapter_count_for_title(
919                                  libvlc_media_player_t *p_mi,
920                                  int i_title,
921                                  libvlc_exception_t *p_e )
922 {
923     input_thread_t *p_input_thread;
924     vlc_value_t val;
925
926     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
927     if( !p_input_thread )
928         return -1;
929
930     char *psz_name;
931     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
932     {
933         vlc_object_release( p_input_thread );
934         return -1;
935     }
936     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
937     vlc_object_release( p_input_thread );
938     free( psz_name );
939
940     return val.i_int;
941 }
942
943 void libvlc_media_player_set_title(
944                                  libvlc_media_player_t *p_mi,
945                                  int i_title,
946                                  libvlc_exception_t *p_e )
947 {
948     input_thread_t *p_input_thread;
949
950     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
951     if( !p_input_thread )
952         return;
953
954     var_SetInteger( p_input_thread, "title", i_title );
955     vlc_object_release( p_input_thread );
956
957     //send event
958     libvlc_event_t event;
959     event.type = libvlc_MediaPlayerTitleChanged;
960     event.u.media_player_title_changed.new_title = i_title;
961     libvlc_event_send( p_mi->p_event_manager, &event );
962 }
963
964 int libvlc_media_player_get_title(
965                                  libvlc_media_player_t *p_mi,
966                                  libvlc_exception_t *p_e )
967 {
968     input_thread_t *p_input_thread;
969     vlc_value_t val;
970
971     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
972     if( !p_input_thread )
973         return -1;
974
975     var_Get( p_input_thread, "title", &val );
976     vlc_object_release( p_input_thread );
977
978     return val.i_int;
979 }
980
981 int libvlc_media_player_get_title_count(
982                                  libvlc_media_player_t *p_mi,
983                                  libvlc_exception_t *p_e )
984 {
985     input_thread_t *p_input_thread;
986     vlc_value_t val;
987
988     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
989     if( !p_input_thread )
990         return -1;
991
992     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
993     vlc_object_release( p_input_thread );
994
995     return val.i_int;
996 }
997
998 void libvlc_media_player_next_chapter(
999                                  libvlc_media_player_t *p_mi,
1000                                  libvlc_exception_t *p_e )
1001 {
1002     input_thread_t *p_input_thread;
1003
1004     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1005     if( !p_input_thread )
1006         return;
1007
1008     int i_type = var_Type( p_input_thread, "next-chapter" );
1009     var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1010                             "next-chapter":"next-title", true );
1011
1012     vlc_object_release( p_input_thread );
1013 }
1014
1015 void libvlc_media_player_previous_chapter(
1016                                  libvlc_media_player_t *p_mi,
1017                                  libvlc_exception_t *p_e )
1018 {
1019     input_thread_t *p_input_thread;
1020
1021     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1022     if( !p_input_thread )
1023         return;
1024
1025     int i_type = var_Type( p_input_thread, "next-chapter" );
1026     var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1027                             "prev-chapter":"prev-title", true );
1028
1029     vlc_object_release( p_input_thread );
1030 }
1031
1032 float libvlc_media_player_get_fps(
1033                                  libvlc_media_player_t *p_mi,
1034                                  libvlc_exception_t *p_e)
1035 {
1036     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1037     double f_fps = 0.0;
1038
1039     if( p_input_thread )
1040     {
1041         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1042             f_fps = 0.0;
1043         vlc_object_release( p_input_thread );
1044     }
1045     return f_fps;
1046 }
1047
1048 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
1049                                      libvlc_exception_t *p_e)
1050 {
1051     input_thread_t *p_input_thread =
1052                             libvlc_get_input_thread ( p_mi, p_e);
1053     if ( !p_input_thread )
1054         return false;
1055
1056     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
1057     {
1058         vlc_object_release( p_input_thread );
1059         return true;
1060     }
1061     vlc_object_release( p_input_thread );
1062     return false;
1063 }
1064
1065 void libvlc_media_player_set_rate(
1066                                  libvlc_media_player_t *p_mi,
1067                                  float rate,
1068                                  libvlc_exception_t *p_e )
1069 {
1070     input_thread_t *p_input_thread;
1071     bool b_can_rewind;
1072
1073     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1074     if( !p_input_thread )
1075         return;
1076
1077     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1078     if( (rate < 0.0) && !b_can_rewind )
1079     {
1080         vlc_object_release( p_input_thread );
1081         libvlc_exception_raise( p_e, "Rate value is invalid" );
1082         return;
1083     }
1084
1085     var_SetInteger( p_input_thread, "rate", 1000.0f/rate );
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     libvlc_state_t state = libvlc_Ended;
1119     vlc_value_t val;
1120
1121     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1122     if( !p_input_thread )
1123     {
1124         /* We do return the right value, no need to throw an exception */
1125         if( libvlc_exception_raised( p_e ) )
1126             libvlc_exception_clear( p_e );
1127         return state;
1128     }
1129
1130     var_Get( p_input_thread, "state", &val );
1131     state = vlc_to_libvlc_state(val.i_int);
1132
1133     if( state == libvlc_Playing )
1134     {
1135         float caching;
1136         caching = var_GetFloat( p_input_thread, "cache" );
1137         if( caching > 0.0 && caching < 1.0 )
1138             state = libvlc_Buffering;
1139     }
1140     vlc_object_release( p_input_thread );
1141     return state;
1142 }
1143
1144 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1145                                        libvlc_exception_t *p_e )
1146 {
1147     input_thread_t *p_input_thread;
1148     vlc_value_t val;
1149
1150     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1151     if ( !p_input_thread )
1152     {
1153         /* We do return the right value, no need to throw an exception */
1154         if( libvlc_exception_raised( p_e ) )
1155             libvlc_exception_clear( p_e );
1156         return false;
1157     }
1158     var_Get( p_input_thread, "can-seek", &val );
1159     vlc_object_release( p_input_thread );
1160
1161     return val.b_bool;
1162 }
1163
1164 /* internal function, used by audio, video */
1165 libvlc_track_description_t *
1166         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1167                                       const char *psz_variable,
1168                                       libvlc_exception_t *p_e )
1169 {
1170     input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
1171
1172     if( !p_input )
1173         return NULL;
1174
1175     vlc_value_t val_list, text_list;
1176     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1177
1178     if( val_list.p_list->i_count <= 0 ) /* no tracks */
1179     {
1180         var_FreeList( &val_list, &text_list);
1181         return NULL;
1182     }
1183
1184     libvlc_track_description_t *p_track_description, *p_actual, *p_previous;
1185     p_track_description = ( libvlc_track_description_t * )
1186         malloc( sizeof( libvlc_track_description_t ) );
1187     if ( !p_track_description )
1188     {
1189         var_FreeList( &val_list, &text_list);
1190         vlc_object_release( p_input );
1191         libvlc_exception_raise( p_e, "no enough memory" );
1192         return NULL;
1193     }
1194     p_actual = p_track_description;
1195     p_previous = NULL;
1196     for( int i = 0; i < val_list.p_list->i_count; i++ )
1197     {
1198         if( !p_actual )
1199         {
1200             p_actual = ( libvlc_track_description_t * )
1201                 malloc( sizeof( libvlc_track_description_t ) );
1202             if ( !p_actual )
1203             {
1204                 libvlc_track_description_release( p_track_description );
1205                 var_FreeList( &val_list, &text_list);
1206                 vlc_object_release( p_input );
1207                 libvlc_exception_raise( p_e, "no enough memory" );
1208                 return NULL;
1209             }
1210         }
1211         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1212         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1213         p_actual->p_next = NULL;
1214         if( p_previous )
1215             p_previous->p_next = p_actual;
1216         p_previous = p_actual;
1217         p_actual =  NULL;
1218     }
1219     var_FreeList( &val_list, &text_list);
1220     vlc_object_release( p_input );
1221
1222     return p_track_description;
1223 }
1224
1225 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1226 {
1227     libvlc_track_description_t *p_actual, *p_before;
1228     p_actual = p_td;
1229
1230     while ( p_actual )
1231     {
1232         free( p_actual->psz_name );
1233         p_before = p_actual;
1234         p_actual = p_before->p_next;
1235         free( p_before );
1236     }
1237 }
1238
1239 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1240                                      libvlc_exception_t *p_e )
1241 {
1242     input_thread_t *p_input_thread;
1243     vlc_value_t val;
1244
1245     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1246     if ( !p_input_thread )
1247     {
1248         /* We do return the right value, no need to throw an exception */
1249         if( libvlc_exception_raised( p_e ) )
1250             libvlc_exception_clear( p_e );
1251         return false;
1252     }
1253     var_Get( p_input_thread, "can-pause", &val );
1254     vlc_object_release( p_input_thread );
1255
1256     return val.b_bool;
1257 }