]> git.sesse.net Git - vlc/blob - src/control/media_player.c
Mostly revert "libvlc: add mouse events as libvlc_MediaPlayerEvents"
[vlc] / 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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <assert.h>
29
30 #include <vlc/libvlc.h>
31 #include <vlc/libvlc_media.h>
32 #include <vlc/libvlc_events.h>
33
34 #include <vlc_demux.h>
35 #include <vlc_input.h>
36 #include <vlc_vout.h>
37
38 #include "libvlc.h"
39
40 #include "libvlc_internal.h"
41 #include "media_internal.h" // libvlc_media_set_state()
42 #include "media_player_internal.h"
43
44 static int
45 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
46                         vlc_value_t oldval, vlc_value_t newval,
47                         void * p_userdata );
48 static int
49 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
50                         vlc_value_t oldval, vlc_value_t newval,
51                         void * p_userdata );
52 static int
53 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
54                      vlc_value_t oldval, vlc_value_t newval,
55                      void * p_userdata );
56
57 static int
58 snapshot_was_taken( vlc_object_t *p_this, char const *psz_cmd,
59                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
60
61 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
62
63 /*
64  * Shortcuts
65  */
66
67 #define register_event(a, b) __register_event(a, libvlc_MediaPlayer ## b)
68 static inline void __register_event(libvlc_media_player_t *mp, libvlc_event_type_t type)
69 {
70     libvlc_event_manager_register_event_type(mp->p_event_manager, type);
71 }
72
73 static inline void lock(libvlc_media_player_t *mp)
74 {
75     vlc_mutex_lock(&mp->object_lock);
76 }
77
78 static inline void unlock(libvlc_media_player_t *mp)
79 {
80     vlc_mutex_unlock(&mp->object_lock);
81 }
82
83 /*
84  * Release the associated input thread.
85  *
86  * Object lock is NOT held.
87  */
88 static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abort )
89 {
90     assert( p_mi );
91
92     input_thread_t *p_input_thread = p_mi->p_input_thread;
93     if( !p_input_thread )
94         return;
95
96     var_DelCallback( p_input_thread, "can-seek",
97                      input_seekable_changed, p_mi );
98     var_DelCallback( p_input_thread, "can-pause",
99                     input_pausable_changed, p_mi );
100     var_DelCallback( p_input_thread, "intf-event",
101                      input_event_changed, p_mi );
102
103     /* We owned this one */
104     input_Stop( p_input_thread, b_input_abort );
105
106     vlc_thread_join( p_input_thread );
107
108     assert( p_mi->p_input_resource == NULL );
109     assert( p_input_thread->b_dead );
110     /* Store the input resource for future use. */
111     p_mi->p_input_resource = input_DetachResource( p_input_thread );
112
113     vlc_object_release( p_input_thread );
114
115     p_mi->p_input_thread = NULL;
116 }
117
118 /*
119  * Retrieve the input thread. Be sure to release the object
120  * once you are done with it. (libvlc Internal)
121  *
122  * Function will lock the object.
123  */
124 input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi )
125 {
126     input_thread_t *p_input_thread;
127
128     assert( p_mi );
129
130     lock(p_mi);
131     p_input_thread = p_mi->p_input_thread;
132     if( p_input_thread )
133         vlc_object_hold( p_input_thread );
134     else
135         libvlc_printerr( "No active input" );
136     unlock(p_mi);
137
138     return p_input_thread;
139 }
140
141 /*
142  * Set the internal state of the media_player. (media player Internal)
143  *
144  * Function will lock the media_player.
145  */
146 static void set_state( libvlc_media_player_t *p_mi, libvlc_state_t state,
147     bool b_locked )
148 {
149     if(!b_locked)
150         lock(p_mi);
151     p_mi->state = state;
152
153     libvlc_media_t *media = p_mi->p_md;
154     if (media)
155         libvlc_media_retain(media);
156
157     if(!b_locked)
158         unlock(p_mi);
159
160     if (media)
161     {
162         // Also set the state of the corresponding media
163         // This is strictly for convenience.
164         libvlc_media_set_state(media, state);
165
166         libvlc_media_release(media);
167     }
168 }
169
170 static int
171 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
172                         vlc_value_t oldval, vlc_value_t newval,
173                         void * p_userdata )
174 {
175     VLC_UNUSED(oldval);
176     VLC_UNUSED(p_this);
177     VLC_UNUSED(psz_cmd);
178     libvlc_media_player_t * p_mi = p_userdata;
179     libvlc_event_t event;
180
181     event.type = libvlc_MediaPlayerSeekableChanged;
182     event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
183
184     libvlc_event_send( p_mi->p_event_manager, &event );
185     return VLC_SUCCESS;
186 }
187
188 static int
189 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
190                         vlc_value_t oldval, vlc_value_t newval,
191                         void * p_userdata )
192 {
193     VLC_UNUSED(oldval);
194     VLC_UNUSED(p_this);
195     VLC_UNUSED(psz_cmd);
196     libvlc_media_player_t * p_mi = p_userdata;
197     libvlc_event_t event;
198
199     event.type = libvlc_MediaPlayerPausableChanged;
200     event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
201
202     libvlc_event_send( p_mi->p_event_manager, &event );
203     return VLC_SUCCESS;
204 }
205
206 static int
207 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
208                      vlc_value_t oldval, vlc_value_t newval,
209                      void * p_userdata )
210 {
211     VLC_UNUSED(oldval);
212     input_thread_t * p_input = (input_thread_t *)p_this;
213     libvlc_media_player_t * p_mi = p_userdata;
214     libvlc_event_t event;
215
216     assert( !strcmp( psz_cmd, "intf-event" ) );
217
218     if( newval.i_int == INPUT_EVENT_STATE )
219     {
220         libvlc_state_t libvlc_state;
221
222         switch ( var_GetInteger( p_input, "state" ) )
223         {
224             case INIT_S:
225                 libvlc_state = libvlc_NothingSpecial;
226                 event.type = libvlc_MediaPlayerNothingSpecial;
227                 break;
228             case OPENING_S:
229                 libvlc_state = libvlc_Opening;
230                 event.type = libvlc_MediaPlayerOpening;
231                 break;
232             case PLAYING_S:
233                 libvlc_state = libvlc_Playing;
234                 event.type = libvlc_MediaPlayerPlaying;
235                 break;
236             case PAUSE_S:
237                 libvlc_state = libvlc_Paused;
238                 event.type = libvlc_MediaPlayerPaused;
239                 break;
240             case END_S:
241                 libvlc_state = libvlc_Ended;
242                 event.type = libvlc_MediaPlayerEndReached;
243                 break;
244             case ERROR_S:
245                 libvlc_state = libvlc_Error;
246                 event.type = libvlc_MediaPlayerEncounteredError;
247                 break;
248
249             default:
250                 return VLC_SUCCESS;
251         }
252
253         set_state( p_mi, libvlc_state, false );
254         libvlc_event_send( p_mi->p_event_manager, &event );
255     }
256     else if( newval.i_int == INPUT_EVENT_ABORT )
257     {
258         libvlc_state_t libvlc_state = libvlc_Stopped;
259         event.type = libvlc_MediaPlayerStopped;
260
261         set_state( p_mi, libvlc_state, false );
262         libvlc_event_send( p_mi->p_event_manager, &event );
263     }
264     else if( newval.i_int == INPUT_EVENT_POSITION )
265     {
266         if( var_GetInteger( p_input, "state" ) != PLAYING_S )
267             return VLC_SUCCESS; /* Don't send the position while stopped */
268
269         /* */
270         event.type = libvlc_MediaPlayerPositionChanged;
271         event.u.media_player_position_changed.new_position =
272                                           var_GetFloat( p_input, "position" );
273         libvlc_event_send( p_mi->p_event_manager, &event );
274
275         /* */
276         event.type = libvlc_MediaPlayerTimeChanged;
277         event.u.media_player_time_changed.new_time =
278            from_mtime(var_GetTime( p_input, "time" ));
279         libvlc_event_send( p_mi->p_event_manager, &event );
280     }
281     else if( newval.i_int == INPUT_EVENT_LENGTH )
282     {
283         event.type = libvlc_MediaPlayerLengthChanged;
284         event.u.media_player_length_changed.new_length =
285            from_mtime(var_GetTime( p_input, "length" ));
286         libvlc_event_send( p_mi->p_event_manager, &event );
287     }
288
289     return VLC_SUCCESS;
290
291 }
292
293 /**************************************************************************
294  * Snapshot Taken Event.
295  *
296  * FIXME: This snapshot API interface makes no sense in media_player.
297  *************************************************************************/
298 static int snapshot_was_taken(vlc_object_t *p_this, char const *psz_cmd,
299                               vlc_value_t oldval, vlc_value_t newval, void *p_data )
300 {
301     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_this);
302
303     libvlc_media_player_t *mp = p_data;
304     libvlc_event_t event;
305     event.type = libvlc_MediaPlayerSnapshotTaken;
306     event.u.media_player_snapshot_taken.psz_filename = newval.psz_string;
307     libvlc_event_send(mp->p_event_manager, &event);
308
309     return VLC_SUCCESS;
310 }
311
312 static input_thread_t *find_input (vlc_object_t *obj)
313 {
314     libvlc_media_player_t *mp = (libvlc_media_player_t *)obj;
315     input_thread_t *p_input;
316
317     lock (mp);
318     p_input = mp->p_input_thread;
319     if (p_input)
320         vlc_object_hold (p_input);
321     unlock (mp);
322     return p_input;
323 }
324
325 /* */
326 static void libvlc_media_player_destroy( libvlc_media_player_t * );
327
328
329 /**************************************************************************
330  * Create a Media Instance object.
331  *
332  * Refcount strategy:
333  * - All items created by _new start with a refcount set to 1.
334  * - Accessor _release decrease the refcount by 1, if after that
335  *   operation the refcount is 0, the object is destroyed.
336  * - Accessor _retain increase the refcount by 1 (XXX: to implement)
337  *
338  * Object locking strategy:
339  * - No lock held while in constructor.
340  * - When accessing any member variable this lock is held. (XXX who locks?)
341  * - When attempting to destroy the object the lock is also held.
342  **************************************************************************/
343 libvlc_media_player_t *
344 libvlc_media_player_new( libvlc_instance_t *instance )
345 {
346     libvlc_media_player_t * mp;
347
348     assert(instance);
349
350     mp = vlc_object_create (instance->p_libvlc_int, sizeof(*mp));
351     if (unlikely(mp == NULL))
352     {
353         libvlc_printerr("Not enough memory");
354         return NULL;
355     }
356     vlc_object_attach (mp, mp->p_libvlc);
357
358     /* Input */
359     var_Create (mp, "rate", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT);
360
361     /* Video */
362     var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
363 #ifdef WIN32
364     var_Create (mp, "drawable-hwnd", VLC_VAR_ADDRESS);
365 #endif
366 #ifdef __APPLE__
367     var_Create (mp, "drawable-agl", VLC_VAR_INTEGER);
368     var_Create (mp, "drawable-nsobject", VLC_VAR_ADDRESS);
369 #endif
370
371     var_Create (mp, "keyboard-events", VLC_VAR_BOOL);
372     var_SetBool (mp, "keyboard-events", true);
373     var_Create (mp, "mouse-events", VLC_VAR_BOOL);
374     var_SetBool (mp, "mouse-events", true);
375
376     var_Create (mp, "fullscreen", VLC_VAR_BOOL);
377     var_Create (mp, "autoscale", VLC_VAR_BOOL);
378     var_SetBool (mp, "autoscale", true);
379     var_Create (mp, "scale", VLC_VAR_FLOAT);
380     var_SetFloat (mp, "scale", 1.);
381     var_Create (mp, "aspect-ratio", VLC_VAR_STRING);
382     var_Create (mp, "crop", VLC_VAR_STRING);
383     var_Create (mp, "deinterlace", VLC_VAR_INTEGER);
384     var_Create (mp, "deinterlace-mode", VLC_VAR_STRING);
385
386     var_Create (mp, "vbi-page", VLC_VAR_INTEGER);
387
388     var_Create (mp, "marq-marquee", VLC_VAR_STRING);
389     var_Create (mp, "marq-color", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
390     var_Create (mp, "marq-opacity", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
391     var_Create (mp, "marq-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
392     var_Create (mp, "marq-refresh", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
393     var_Create (mp, "marq-size", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
394     var_Create (mp, "marq-timeout", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
395     var_Create (mp, "marq-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
396     var_Create (mp, "marq-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
397
398     var_Create (mp, "logo-file", VLC_VAR_STRING);
399     var_Create (mp, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
400     var_Create (mp, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
401     var_Create (mp, "logo-delay", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
402     var_Create (mp, "logo-repeat", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
403     var_Create (mp, "logo-opacity", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
404     var_Create (mp, "logo-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
405
406      /* Audio */
407     var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
408     var_Create (mp, "volume-muted", VLC_VAR_BOOL);
409     var_Create (mp, "saved-volume", VLC_VAR_INTEGER);
410     var_Create (mp, "volume-change", VLC_VAR_VOID);
411     var_Create (mp, "find-input-callback", VLC_VAR_ADDRESS);
412     var_SetAddress (mp, "find-input-callback", find_input);
413
414     mp->p_md = NULL;
415     mp->state = libvlc_NothingSpecial;
416     mp->p_libvlc_instance = instance;
417     mp->p_input_thread = NULL;
418     mp->p_input_resource = NULL;
419     mp->i_refcount = 1;
420     mp->p_event_manager = libvlc_event_manager_new(mp, instance);
421     if (unlikely(mp->p_event_manager == NULL))
422     {
423         vlc_object_release(mp);
424         return NULL;
425     }
426     vlc_mutex_init(&mp->object_lock);
427
428     register_event(mp, NothingSpecial);
429     register_event(mp, Opening);
430     register_event(mp, Buffering);
431     register_event(mp, Playing);
432     register_event(mp, Paused);
433     register_event(mp, Stopped);
434     register_event(mp, Forward);
435     register_event(mp, Backward);
436     register_event(mp, EndReached);
437     register_event(mp, EncounteredError);
438     register_event(mp, SeekableChanged);
439
440     register_event(mp, PositionChanged);
441     register_event(mp, TimeChanged);
442     register_event(mp, LengthChanged);
443     register_event(mp, TitleChanged);
444     register_event(mp, PausableChanged);
445
446     /* Snapshot initialization */
447     register_event(mp, SnapshotTaken);
448
449     register_event(mp, MediaChanged);
450
451     /* Attach a var callback to the global object to provide the glue between
452      * vout_thread that generates the event and media_player that re-emits it
453      * with its own event manager
454      *
455      * FIXME: It's unclear why we want to put this in public API, and why we
456      * want to expose it in such a limiting and ugly way.
457      */
458     var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
459
460     libvlc_retain(instance);
461     return mp;
462 }
463
464 /**************************************************************************
465  * Create a Media Instance object with a media descriptor.
466  **************************************************************************/
467 libvlc_media_player_t *
468 libvlc_media_player_new_from_media( libvlc_media_t * p_md )
469 {
470     libvlc_media_player_t * p_mi;
471
472     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
473     if( !p_mi )
474         return NULL;
475
476     libvlc_media_retain( p_md );
477     p_mi->p_md = p_md;
478
479     return p_mi;
480 }
481
482 /**************************************************************************
483  * Destroy a Media Instance object (libvlc internal)
484  *
485  * Warning: No lock held here, but hey, this is internal. Caller must lock.
486  **************************************************************************/
487 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
488 {
489     assert( p_mi );
490
491     /* Detach Callback from the main libvlc object */
492     var_DelCallback( p_mi->p_libvlc,
493                      "snapshot-file", snapshot_was_taken, p_mi );
494
495     /* If the input thread hasn't been already deleted it means
496      * that the owners didn't stop the thread before releasing it. */
497     assert(!p_mi->p_input_thread);
498
499     /* Fallback for those who don't use NDEBUG */
500     if(p_mi->p_input_thread )
501         release_input_thread(p_mi, true);
502
503     if( p_mi->p_input_resource )
504     {
505         input_resource_Delete( p_mi->p_input_resource );
506         p_mi->p_input_resource = NULL;
507     }
508
509     libvlc_event_manager_release( p_mi->p_event_manager );
510     libvlc_media_release( p_mi->p_md );
511     vlc_mutex_destroy( &p_mi->object_lock );
512
513     libvlc_instance_t *instance = p_mi->p_libvlc_instance;
514     vlc_object_release( p_mi );
515     libvlc_release(instance);
516 }
517
518 /**************************************************************************
519  * Release a Media Instance object.
520  *
521  * Function does the locking.
522  **************************************************************************/
523 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
524 {
525     bool destroy;
526
527     assert( p_mi );
528     lock(p_mi);
529     destroy = !--p_mi->i_refcount;
530     unlock(p_mi);
531
532     if( destroy )
533         libvlc_media_player_destroy( p_mi );
534 }
535
536 /**************************************************************************
537  * Retain a Media Instance object.
538  *
539  * Caller must hold the lock.
540  **************************************************************************/
541 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
542 {
543     assert( p_mi );
544
545     lock(p_mi);
546     p_mi->i_refcount++;
547     unlock(p_mi);
548 }
549
550 /**************************************************************************
551  * Set the Media descriptor associated with the instance.
552  *
553  * Enter without lock -- function will lock the object.
554  **************************************************************************/
555 void libvlc_media_player_set_media(
556                             libvlc_media_player_t *p_mi,
557                             libvlc_media_t *p_md )
558 {
559     lock(p_mi);
560
561     /* FIXME I am not sure if it is a user request or on die(eof/error)
562      * request here */
563     release_input_thread( p_mi,
564                           p_mi->p_input_thread &&
565                           !p_mi->p_input_thread->b_eof &&
566                           !p_mi->p_input_thread->b_error );
567
568     set_state( p_mi, libvlc_NothingSpecial, true );
569
570     libvlc_media_release( p_mi->p_md );
571
572     if( !p_md )
573     {
574         p_mi->p_md = NULL;
575         unlock(p_mi);
576         return; /* It is ok to pass a NULL md */
577     }
578
579     libvlc_media_retain( p_md );
580     p_mi->p_md = p_md;
581
582     /* The policy here is to ignore that we were created using a different
583      * libvlc_instance, because we don't really care */
584     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
585
586     unlock(p_mi);
587
588     /* Send an event for the newly available media */
589     libvlc_event_t event;
590     event.type = libvlc_MediaPlayerMediaChanged;
591     event.u.media_player_media_changed.new_media = p_md;
592     libvlc_event_send( p_mi->p_event_manager, &event );
593
594 }
595
596 /**************************************************************************
597  * Get the Media descriptor associated with the instance.
598  **************************************************************************/
599 libvlc_media_t *
600 libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
601 {
602     libvlc_media_t *p_m;
603
604     lock(p_mi);
605     p_m = p_mi->p_md;
606     if( p_m )
607         libvlc_media_retain( p_mi->p_md );
608     unlock(p_mi);
609     return p_mi->p_md;
610 }
611
612 /**************************************************************************
613  * Get the event Manager.
614  **************************************************************************/
615 libvlc_event_manager_t *
616 libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
617 {
618     return p_mi->p_event_manager;
619 }
620
621 /**************************************************************************
622  * Tell media player to start playing.
623  **************************************************************************/
624 int libvlc_media_player_play( libvlc_media_player_t *p_mi )
625 {
626     input_thread_t * p_input_thread;
627
628     if( (p_input_thread = libvlc_get_input_thread( p_mi )) )
629     {
630         /* A thread already exists, send it a play message */
631         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
632         vlc_object_release( p_input_thread );
633         return 0;
634     }
635
636     /* Ignore previous exception */
637     lock(p_mi);
638
639     if( !p_mi->p_md )
640     {
641         unlock(p_mi);
642         libvlc_printerr( "No associated media descriptor" );
643         return -1;
644     }
645
646     p_mi->p_input_thread = input_Create( p_mi,
647                                          p_mi->p_md->p_input_item, NULL,
648                                          p_mi->p_input_resource );
649     if( !p_mi->p_input_thread )
650     {
651         unlock(p_mi);
652         libvlc_printerr( "Not enough memory" );
653         return -1;
654     }
655
656     p_mi->p_input_resource = NULL;
657     p_input_thread = p_mi->p_input_thread;
658
659     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
660     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
661     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
662
663     if( input_Start( p_input_thread ) )
664     {
665         vlc_object_release( p_input_thread );
666         p_mi->p_input_thread = NULL;
667     }
668
669     unlock(p_mi);
670     return 0;
671 }
672
673 /**************************************************************************
674  * Pause.
675  **************************************************************************/
676 void libvlc_media_player_pause( libvlc_media_player_t *p_mi )
677 {
678     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
679     if( !p_input_thread )
680         return;
681
682     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
683     if( state == libvlc_Playing || state == libvlc_Buffering )
684     {
685         if( libvlc_media_player_can_pause( p_mi ) )
686             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
687         else
688             libvlc_media_player_stop( p_mi );
689     }
690     else
691         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
692
693     vlc_object_release( p_input_thread );
694 }
695
696 /**************************************************************************
697  * Tells whether the media player is currently playing.
698  *
699  * Enter with lock held.
700  **************************************************************************/
701 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
702 {
703     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
704     return (libvlc_Playing == state) || (libvlc_Buffering == state);
705 }
706
707 /**************************************************************************
708  * Stop playing.
709  **************************************************************************/
710 void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
711 {
712     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
713
714     lock(p_mi);
715     release_input_thread( p_mi, true ); /* This will stop the input thread */
716     unlock(p_mi);
717
718     /* Force to go to stopped state, in case we were in Ended, or Error
719      * state. */
720     if( state != libvlc_Stopped )
721     {
722         set_state( p_mi, libvlc_Stopped, false );
723
724         /* Construct and send the event */
725         libvlc_event_t event;
726         event.type = libvlc_MediaPlayerStopped;
727         libvlc_event_send( p_mi->p_event_manager, &event );
728     }
729 }
730
731 /**************************************************************************
732  * set_nsobject
733  **************************************************************************/
734 void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
735                                         void * drawable )
736 {
737     assert (p_mi != NULL);
738 #ifdef __APPLE__
739     var_SetAddress (p_mi, "drawable-nsobject", drawable);
740 #else
741     (void) p_mi; (void)drawable;
742 #endif
743 }
744
745 /**************************************************************************
746  * get_nsobject
747  **************************************************************************/
748 void * libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
749 {
750     assert (p_mi != NULL);
751 #ifdef __APPLE__
752     return var_GetAddress (p_mi, "drawable-nsobject");
753 #else
754     return NULL;
755 #endif
756 }
757
758 /**************************************************************************
759  * set_agl
760  **************************************************************************/
761 void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
762                                   uint32_t drawable )
763 {
764 #ifdef __APPLE__
765     var_SetInteger (p_mi, "drawable-agl", drawable);
766 #else
767     (void) p_mi; (void)drawable;
768 #endif
769 }
770
771 /**************************************************************************
772  * get_agl
773  **************************************************************************/
774 uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
775 {
776     assert (p_mi != NULL);
777 #ifdef __APPLE__
778     return var_GetInteger (p_mi, "drawable-agl");
779 #else
780     return 0;
781 #endif
782 }
783
784 /**************************************************************************
785  * set_xwindow
786  **************************************************************************/
787 void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
788                                       uint32_t drawable )
789 {
790     assert (p_mi != NULL);
791     var_SetInteger (p_mi, "drawable-xid", drawable);
792 }
793
794 /**************************************************************************
795  * get_xwindow
796  **************************************************************************/
797 uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
798 {
799     return var_GetInteger (p_mi, "drawable-xid");
800 }
801
802 /**************************************************************************
803  * set_hwnd
804  **************************************************************************/
805 void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
806                                    void *drawable )
807 {
808     assert (p_mi != NULL);
809 #ifdef WIN32
810     var_SetAddress (p_mi, "drawable-hwnd", drawable);
811 #else
812     (void) p_mi; (void) drawable;
813 #endif
814 }
815
816 /**************************************************************************
817  * get_hwnd
818  **************************************************************************/
819 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
820 {
821     assert (p_mi != NULL);
822 #ifdef WIN32
823     return var_GetAddress (p_mi, "drawable-hwnd");
824 #else
825     return NULL;
826 #endif
827 }
828
829 /**************************************************************************
830  * Getters for stream information
831  **************************************************************************/
832 libvlc_time_t libvlc_media_player_get_length(
833                              libvlc_media_player_t *p_mi )
834 {
835     input_thread_t *p_input_thread;
836     libvlc_time_t i_time;
837
838     p_input_thread = libvlc_get_input_thread ( p_mi );
839     if( !p_input_thread )
840         return -1;
841
842     i_time = from_mtime(var_GetTime( p_input_thread, "length" ));
843     vlc_object_release( p_input_thread );
844
845     return i_time;
846 }
847
848 libvlc_time_t libvlc_media_player_get_time( libvlc_media_player_t *p_mi )
849 {
850     input_thread_t *p_input_thread;
851     libvlc_time_t i_time;
852
853     p_input_thread = libvlc_get_input_thread ( p_mi );
854     if( !p_input_thread )
855         return -1;
856
857     i_time = from_mtime(var_GetTime( p_input_thread , "time" ));
858     vlc_object_release( p_input_thread );
859     return i_time;
860 }
861
862 void libvlc_media_player_set_time( libvlc_media_player_t *p_mi,
863                                    libvlc_time_t i_time )
864 {
865     input_thread_t *p_input_thread;
866
867     p_input_thread = libvlc_get_input_thread ( p_mi );
868     if( !p_input_thread )
869         return;
870
871     var_SetTime( p_input_thread, "time", to_mtime(i_time) );
872     vlc_object_release( p_input_thread );
873 }
874
875 void libvlc_media_player_set_position( libvlc_media_player_t *p_mi,
876                                        float position )
877 {
878     input_thread_t *p_input_thread;
879
880     p_input_thread = libvlc_get_input_thread ( p_mi );
881     if( !p_input_thread )
882         return;
883
884     var_SetFloat( p_input_thread, "position", position );
885     vlc_object_release( p_input_thread );
886 }
887
888 float libvlc_media_player_get_position( libvlc_media_player_t *p_mi )
889 {
890     input_thread_t *p_input_thread;
891     float f_position;
892
893     p_input_thread = libvlc_get_input_thread ( p_mi );
894     if( !p_input_thread )
895         return -1.0;
896
897     f_position = var_GetFloat( p_input_thread, "position" );
898     vlc_object_release( p_input_thread );
899
900     return f_position;
901 }
902
903 void libvlc_media_player_set_chapter( libvlc_media_player_t *p_mi,
904                                       int chapter )
905 {
906     input_thread_t *p_input_thread;
907
908     p_input_thread = libvlc_get_input_thread ( p_mi );
909     if( !p_input_thread )
910         return;
911
912     var_SetInteger( p_input_thread, "chapter", chapter );
913     vlc_object_release( p_input_thread );
914 }
915
916 int libvlc_media_player_get_chapter( libvlc_media_player_t *p_mi )
917 {
918     input_thread_t *p_input_thread;
919     int i_chapter;
920
921     p_input_thread = libvlc_get_input_thread ( p_mi );
922     if( !p_input_thread )
923         return -1;
924
925     i_chapter = var_GetInteger( p_input_thread, "chapter" );
926     vlc_object_release( p_input_thread );
927
928     return i_chapter;
929 }
930
931 int libvlc_media_player_get_chapter_count( libvlc_media_player_t *p_mi )
932 {
933     input_thread_t *p_input_thread;
934     vlc_value_t val;
935
936     p_input_thread = libvlc_get_input_thread ( p_mi );
937     if( !p_input_thread )
938         return -1;
939
940     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
941     vlc_object_release( p_input_thread );
942
943     return val.i_int;
944 }
945
946 int libvlc_media_player_get_chapter_count_for_title(
947                                  libvlc_media_player_t *p_mi,
948                                  int i_title )
949 {
950     input_thread_t *p_input_thread;
951     vlc_value_t val;
952
953     p_input_thread = libvlc_get_input_thread ( p_mi );
954     if( !p_input_thread )
955         return -1;
956
957     char *psz_name;
958     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
959     {
960         vlc_object_release( p_input_thread );
961         return -1;
962     }
963     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
964     vlc_object_release( p_input_thread );
965     free( psz_name );
966
967     return val.i_int;
968 }
969
970 void libvlc_media_player_set_title( libvlc_media_player_t *p_mi,
971                                     int i_title )
972 {
973     input_thread_t *p_input_thread;
974
975     p_input_thread = libvlc_get_input_thread ( p_mi );
976     if( !p_input_thread )
977         return;
978
979     var_SetInteger( p_input_thread, "title", i_title );
980     vlc_object_release( p_input_thread );
981
982     //send event
983     libvlc_event_t event;
984     event.type = libvlc_MediaPlayerTitleChanged;
985     event.u.media_player_title_changed.new_title = i_title;
986     libvlc_event_send( p_mi->p_event_manager, &event );
987 }
988
989 int libvlc_media_player_get_title( libvlc_media_player_t *p_mi )
990 {
991     input_thread_t *p_input_thread;
992     int i_title;
993
994     p_input_thread = libvlc_get_input_thread ( p_mi );
995     if( !p_input_thread )
996         return -1;
997
998     i_title = var_GetInteger( p_input_thread, "title" );
999     vlc_object_release( p_input_thread );
1000
1001     return i_title;
1002 }
1003
1004 int libvlc_media_player_get_title_count( libvlc_media_player_t *p_mi )
1005 {
1006     input_thread_t *p_input_thread;
1007     vlc_value_t val;
1008
1009     p_input_thread = libvlc_get_input_thread ( p_mi );
1010     if( !p_input_thread )
1011         return -1;
1012
1013     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
1014     vlc_object_release( p_input_thread );
1015
1016     return val.i_int;
1017 }
1018
1019 void libvlc_media_player_next_chapter( libvlc_media_player_t *p_mi )
1020 {
1021     input_thread_t *p_input_thread;
1022
1023     p_input_thread = libvlc_get_input_thread ( p_mi );
1024     if( !p_input_thread )
1025         return;
1026
1027     int i_type = var_Type( p_input_thread, "next-chapter" );
1028     var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1029                             "next-chapter":"next-title", true );
1030
1031     vlc_object_release( p_input_thread );
1032 }
1033
1034 void libvlc_media_player_previous_chapter( libvlc_media_player_t *p_mi )
1035 {
1036     input_thread_t *p_input_thread;
1037
1038     p_input_thread = libvlc_get_input_thread ( p_mi );
1039     if( !p_input_thread )
1040         return;
1041
1042     int i_type = var_Type( p_input_thread, "next-chapter" );
1043     var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1044                             "prev-chapter":"prev-title", true );
1045
1046     vlc_object_release( p_input_thread );
1047 }
1048
1049 float libvlc_media_player_get_fps( libvlc_media_player_t *p_mi )
1050 {
1051     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1052     double f_fps = 0.0;
1053
1054     if( p_input_thread )
1055     {
1056         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1057             f_fps = 0.0;
1058         vlc_object_release( p_input_thread );
1059     }
1060     return f_fps;
1061 }
1062
1063 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi )
1064 {
1065     bool b_will_play;
1066     input_thread_t *p_input_thread =
1067                             libvlc_get_input_thread ( p_mi );
1068     if ( !p_input_thread )
1069         return false;
1070
1071     b_will_play = !p_input_thread->b_die && !p_input_thread->b_dead;
1072     vlc_object_release( p_input_thread );
1073
1074     return b_will_play;
1075 }
1076
1077 int libvlc_media_player_set_rate( libvlc_media_player_t *p_mi, float rate )
1078 {
1079     if (rate < 0.)
1080     {
1081         libvlc_printerr ("Playing backward not supported");
1082         return -1;
1083     }
1084
1085     var_SetFloat (p_mi, "rate", rate);
1086
1087     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1088     if( !p_input_thread )
1089         return 0;
1090     var_SetFloat( p_input_thread, "rate", rate );
1091     vlc_object_release( p_input_thread );
1092     return 0;
1093 }
1094
1095 float libvlc_media_player_get_rate( libvlc_media_player_t *p_mi )
1096 {
1097     return var_GetFloat (p_mi, "rate");
1098 }
1099
1100 libvlc_state_t libvlc_media_player_get_state( libvlc_media_player_t *p_mi )
1101 {
1102     lock(p_mi);
1103     libvlc_state_t state = p_mi->state;
1104     unlock(p_mi);
1105     return state;
1106 }
1107
1108 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi )
1109 {
1110     input_thread_t *p_input_thread;
1111     bool b_seekable;
1112
1113     p_input_thread = libvlc_get_input_thread ( p_mi );
1114     if ( !p_input_thread )
1115         return false;
1116     b_seekable = var_GetBool( p_input_thread, "can-seek" );
1117     vlc_object_release( p_input_thread );
1118
1119     return b_seekable;
1120 }
1121
1122 /* internal function, used by audio, video */
1123 libvlc_track_description_t *
1124         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1125                                       const char *psz_variable )
1126 {
1127     input_thread_t *p_input = libvlc_get_input_thread( p_mi );
1128     libvlc_track_description_t *p_track_description = NULL,
1129                                *p_actual, *p_previous;
1130
1131     if( !p_input )
1132         return NULL;
1133
1134     vlc_value_t val_list, text_list;
1135     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1136
1137     /* no tracks */
1138     if( val_list.p_list->i_count <= 0 )
1139         goto end;
1140
1141     p_track_description = ( libvlc_track_description_t * )
1142         malloc( sizeof( libvlc_track_description_t ) );
1143     if ( !p_track_description )
1144     {
1145         libvlc_printerr( "Not enough memory" );
1146         goto end;
1147     }
1148     p_actual = p_track_description;
1149     p_previous = NULL;
1150     for( int i = 0; i < val_list.p_list->i_count; i++ )
1151     {
1152         if( !p_actual )
1153         {
1154             p_actual = ( libvlc_track_description_t * )
1155                 malloc( sizeof( libvlc_track_description_t ) );
1156             if ( !p_actual )
1157             {
1158                 libvlc_track_description_release( p_track_description );
1159                 libvlc_printerr( "Not enough memory" );
1160                 goto end;
1161             }
1162         }
1163         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1164         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1165         p_actual->p_next = NULL;
1166         if( p_previous )
1167             p_previous->p_next = p_actual;
1168         p_previous = p_actual;
1169         p_actual =  NULL;
1170     }
1171
1172 end:
1173     var_FreeList( &val_list, &text_list );
1174     vlc_object_release( p_input );
1175
1176     return p_track_description;
1177 }
1178
1179 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1180 {
1181     libvlc_track_description_t *p_actual, *p_before;
1182     p_actual = p_td;
1183
1184     while ( p_actual )
1185     {
1186         free( p_actual->psz_name );
1187         p_before = p_actual;
1188         p_actual = p_before->p_next;
1189         free( p_before );
1190     }
1191 }
1192
1193 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi )
1194 {
1195     input_thread_t *p_input_thread;
1196     bool b_can_pause;
1197
1198     p_input_thread = libvlc_get_input_thread ( p_mi );
1199     if ( !p_input_thread )
1200         return false;
1201     b_can_pause = var_GetBool( p_input_thread, "can-pause" );
1202     vlc_object_release( p_input_thread );
1203
1204     return b_can_pause;
1205 }
1206
1207 void libvlc_media_player_next_frame( libvlc_media_player_t *p_mi )
1208 {
1209     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1210     if( p_input_thread != NULL )
1211     {
1212         var_TriggerCallback( p_input_thread, "frame-next" );
1213         vlc_object_release( p_input_thread );
1214     }
1215 }