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