]> git.sesse.net Git - vlc/blob - src/control/media_player.c
Restore correct SIGCHLD handling
[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_key_t
47  */
48 static const vlc_key_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_ADDRESS);
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, "volume-muted", VLC_VAR_BOOL);
458     var_Create (mp, "saved-volume", VLC_VAR_INTEGER);
459     var_Create (mp, "volume-change", VLC_VAR_VOID);
460     var_Create (mp, "find-input-callback", VLC_VAR_ADDRESS);
461     var_SetAddress (mp, "find-input-callback", find_input);
462
463     mp->p_md = NULL;
464     mp->state = libvlc_NothingSpecial;
465     mp->p_libvlc_instance = instance;
466     mp->input.p_thread = NULL;
467     mp->input.p_resource = NULL;
468     vlc_mutex_init (&mp->input.lock);
469     mp->i_refcount = 1;
470     mp->p_event_manager = libvlc_event_manager_new(mp, instance);
471     if (unlikely(mp->p_event_manager == NULL))
472     {
473         vlc_object_release(mp);
474         return NULL;
475     }
476     vlc_mutex_init(&mp->object_lock);
477
478     register_event(mp, NothingSpecial);
479     register_event(mp, Opening);
480     register_event(mp, Buffering);
481     register_event(mp, Playing);
482     register_event(mp, Paused);
483     register_event(mp, Stopped);
484     register_event(mp, Forward);
485     register_event(mp, Backward);
486     register_event(mp, EndReached);
487     register_event(mp, EncounteredError);
488     register_event(mp, SeekableChanged);
489
490     register_event(mp, PositionChanged);
491     register_event(mp, TimeChanged);
492     register_event(mp, LengthChanged);
493     register_event(mp, TitleChanged);
494     register_event(mp, PausableChanged);
495
496     /* Snapshot initialization */
497     register_event(mp, SnapshotTaken);
498
499     register_event(mp, MediaChanged);
500
501     /* Attach a var callback to the global object to provide the glue between
502      * vout_thread that generates the event and media_player that re-emits it
503      * with its own event manager
504      *
505      * FIXME: It's unclear why we want to put this in public API, and why we
506      * want to expose it in such a limiting and ugly way.
507      */
508     var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
509
510     libvlc_retain(instance);
511     return mp;
512 }
513
514 /**************************************************************************
515  * Create a Media Instance object with a media descriptor.
516  **************************************************************************/
517 libvlc_media_player_t *
518 libvlc_media_player_new_from_media( libvlc_media_t * p_md )
519 {
520     libvlc_media_player_t * p_mi;
521
522     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
523     if( !p_mi )
524         return NULL;
525
526     libvlc_media_retain( p_md );
527     p_mi->p_md = p_md;
528
529     return p_mi;
530 }
531
532 /**************************************************************************
533  * Destroy a Media Instance object (libvlc internal)
534  *
535  * Warning: No lock held here, but hey, this is internal. Caller must lock.
536  **************************************************************************/
537 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
538 {
539     assert( p_mi );
540
541     /* Detach Callback from the main libvlc object */
542     var_DelCallback( p_mi->p_libvlc,
543                      "snapshot-file", snapshot_was_taken, p_mi );
544
545     /* No need for lock_input() because no other threads knows us anymore */
546     if( p_mi->input.p_thread )
547         release_input_thread(p_mi, true);
548     if( p_mi->input.p_resource )
549     {
550         input_resource_Terminate( p_mi->input.p_resource );
551         input_resource_Release( p_mi->input.p_resource );
552         p_mi->input.p_resource = NULL;
553     }
554     vlc_mutex_destroy( &p_mi->input.lock );
555
556     libvlc_event_manager_release( p_mi->p_event_manager );
557     libvlc_media_release( p_mi->p_md );
558     vlc_mutex_destroy( &p_mi->object_lock );
559
560     libvlc_instance_t *instance = p_mi->p_libvlc_instance;
561     vlc_object_release( p_mi );
562     libvlc_release(instance);
563 }
564
565 /**************************************************************************
566  * Release a Media Instance object.
567  *
568  * Function does the locking.
569  **************************************************************************/
570 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
571 {
572     bool destroy;
573
574     assert( p_mi );
575     lock(p_mi);
576     destroy = !--p_mi->i_refcount;
577     unlock(p_mi);
578
579     if( destroy )
580         libvlc_media_player_destroy( p_mi );
581 }
582
583 /**************************************************************************
584  * Retain a Media Instance object.
585  *
586  * Caller must hold the lock.
587  **************************************************************************/
588 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
589 {
590     assert( p_mi );
591
592     lock(p_mi);
593     p_mi->i_refcount++;
594     unlock(p_mi);
595 }
596
597 /**************************************************************************
598  * Set the Media descriptor associated with the instance.
599  *
600  * Enter without lock -- function will lock the object.
601  **************************************************************************/
602 void libvlc_media_player_set_media(
603                             libvlc_media_player_t *p_mi,
604                             libvlc_media_t *p_md )
605 {
606     lock_input(p_mi);
607
608     /* FIXME I am not sure if it is a user request or on die(eof/error)
609      * request here */
610     release_input_thread( p_mi,
611                           p_mi->input.p_thread &&
612                           !p_mi->input.p_thread->b_eof &&
613                           !p_mi->input.p_thread->b_error );
614
615     lock( p_mi );
616     set_state( p_mi, libvlc_NothingSpecial, true );
617     unlock_input( p_mi );
618
619     libvlc_media_release( p_mi->p_md );
620
621     if( !p_md )
622     {
623         p_mi->p_md = NULL;
624         unlock(p_mi);
625         return; /* It is ok to pass a NULL md */
626     }
627
628     libvlc_media_retain( p_md );
629     p_mi->p_md = p_md;
630
631     /* The policy here is to ignore that we were created using a different
632      * libvlc_instance, because we don't really care */
633     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
634
635     unlock(p_mi);
636
637     /* Send an event for the newly available media */
638     libvlc_event_t event;
639     event.type = libvlc_MediaPlayerMediaChanged;
640     event.u.media_player_media_changed.new_media = p_md;
641     libvlc_event_send( p_mi->p_event_manager, &event );
642
643 }
644
645 /**************************************************************************
646  * Get the Media descriptor associated with the instance.
647  **************************************************************************/
648 libvlc_media_t *
649 libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
650 {
651     libvlc_media_t *p_m;
652
653     lock(p_mi);
654     p_m = p_mi->p_md;
655     if( p_m )
656         libvlc_media_retain( p_mi->p_md );
657     unlock(p_mi);
658     return p_mi->p_md;
659 }
660
661 /**************************************************************************
662  * Get the event Manager.
663  **************************************************************************/
664 libvlc_event_manager_t *
665 libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
666 {
667     return p_mi->p_event_manager;
668 }
669
670 /**************************************************************************
671  * Tell media player to start playing.
672  **************************************************************************/
673 int libvlc_media_player_play( libvlc_media_player_t *p_mi )
674 {
675     lock_input( p_mi );
676
677     input_thread_t *p_input_thread = p_mi->input.p_thread;
678     if( p_input_thread )
679     {
680         /* A thread already exists, send it a play message */
681         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
682         unlock_input( p_mi );
683         return 0;
684     }
685
686     /* Ignore previous exception */
687     lock(p_mi);
688
689     if( !p_mi->p_md )
690     {
691         unlock(p_mi);
692         unlock_input( p_mi );
693         libvlc_printerr( "No associated media descriptor" );
694         return -1;
695     }
696
697     if( !p_mi->input.p_resource )
698         p_mi->input.p_resource = input_resource_New( VLC_OBJECT( p_mi ) );
699     p_input_thread = input_Create( p_mi, p_mi->p_md->p_input_item, NULL,
700                                    p_mi->input.p_resource );
701     unlock(p_mi);
702     if( !p_input_thread )
703     {
704         unlock_input(p_mi);
705         libvlc_printerr( "Not enough memory" );
706         return -1;
707     }
708
709     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
710     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
711     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
712
713     if( input_Start( p_input_thread ) )
714     {
715         unlock_input(p_mi);
716         var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
717         var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
718         var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
719         vlc_object_release( p_input_thread );
720         libvlc_printerr( "Input initialization failure" );
721         return -1;
722     }
723     p_mi->input.p_thread = p_input_thread;
724     unlock_input(p_mi);
725     return 0;
726 }
727
728 void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
729 {
730     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
731     if( !p_input_thread )
732         return;
733
734     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
735     if( state == libvlc_Playing || state == libvlc_Buffering )
736     {
737         if( paused )
738         {
739             if( libvlc_media_player_can_pause( p_mi ) )
740                 input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
741             else
742                 libvlc_media_player_stop( p_mi );
743         }
744     }
745     else
746     {
747         if( !paused )
748             input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
749     }
750
751     vlc_object_release( p_input_thread );
752 }
753
754 /**************************************************************************
755  * Toggle pause.
756  **************************************************************************/
757 void libvlc_media_player_pause( libvlc_media_player_t *p_mi )
758 {
759     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
760     bool playing = (state == libvlc_Playing || state == libvlc_Buffering);
761
762     libvlc_media_player_set_pause( p_mi, playing );
763 }
764
765 /**************************************************************************
766  * Tells whether the media player is currently playing.
767  *
768  * Enter with lock held.
769  **************************************************************************/
770 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
771 {
772     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
773     return (libvlc_Playing == state) || (libvlc_Buffering == state);
774 }
775
776 /**************************************************************************
777  * Stop playing.
778  **************************************************************************/
779 void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
780 {
781     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
782
783     lock_input(p_mi);
784     release_input_thread( p_mi, true ); /* This will stop the input thread */
785
786     /* Force to go to stopped state, in case we were in Ended, or Error
787      * state. */
788     if( state != libvlc_Stopped )
789     {
790         set_state( p_mi, libvlc_Stopped, false );
791
792         /* Construct and send the event */
793         libvlc_event_t event;
794         event.type = libvlc_MediaPlayerStopped;
795         libvlc_event_send( p_mi->p_event_manager, &event );
796     }
797
798     if( p_mi->input.p_resource != NULL )
799         input_resource_Terminate( p_mi->input.p_resource );
800     unlock_input(p_mi);
801 }
802
803
804 void libvlc_video_set_callbacks( libvlc_media_player_t *mp,
805     void *(*lock_cb) (void *, void **),
806     void (*unlock_cb) (void *, void *, void *const *),
807     void (*display_cb) (void *, void *),
808     void *opaque )
809 {
810     var_SetAddress( mp, "vmem-lock", lock_cb );
811     var_SetAddress( mp, "vmem-unlock", unlock_cb );
812     var_SetAddress( mp, "vmem-display", display_cb );
813     var_SetAddress( mp, "vmem-data", opaque );
814     var_SetString( mp, "vout", "vmem" );
815 }
816
817 void libvlc_video_set_format_callbacks( libvlc_media_player_t *mp,
818                                         libvlc_video_format_cb setup,
819                                         libvlc_video_cleanup_cb cleanup )
820 {
821     var_SetAddress( mp, "vmem-setup", setup );
822     var_SetAddress( mp, "vmem-cleanup", cleanup );
823 }
824
825 void libvlc_video_set_format( libvlc_media_player_t *mp, const char *chroma,
826                               unsigned width, unsigned height, unsigned pitch )
827 {
828     var_SetString( mp, "vmem-chroma", chroma );
829     var_SetInteger( mp, "vmem-width", width );
830     var_SetInteger( mp, "vmem-height", height );
831     var_SetInteger( mp, "vmem-pitch", pitch );
832 }
833
834 /**************************************************************************
835  * set_nsobject
836  **************************************************************************/
837 void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
838                                         void * drawable )
839 {
840     assert (p_mi != NULL);
841 #ifdef __APPLE__
842     var_SetAddress (p_mi, "drawable-nsobject", drawable);
843 #else
844     (void) p_mi; (void)drawable;
845 #endif
846 }
847
848 /**************************************************************************
849  * get_nsobject
850  **************************************************************************/
851 void * libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
852 {
853     assert (p_mi != NULL);
854 #ifdef __APPLE__
855     return var_GetAddress (p_mi, "drawable-nsobject");
856 #else
857     return NULL;
858 #endif
859 }
860
861 /**************************************************************************
862  * set_agl
863  **************************************************************************/
864 void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
865                                   uint32_t drawable )
866 {
867 #ifdef __APPLE__
868     var_SetInteger (p_mi, "drawable-agl", drawable);
869 #else
870     (void) p_mi; (void)drawable;
871 #endif
872 }
873
874 /**************************************************************************
875  * get_agl
876  **************************************************************************/
877 uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
878 {
879     assert (p_mi != NULL);
880 #ifdef __APPLE__
881     return var_GetInteger (p_mi, "drawable-agl");
882 #else
883     return 0;
884 #endif
885 }
886
887 /**************************************************************************
888  * set_xwindow
889  **************************************************************************/
890 void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
891                                       uint32_t drawable )
892 {
893     assert (p_mi != NULL);
894
895     var_SetString (p_mi, "vout", drawable ? "xid" : "any");
896     var_SetString (p_mi, "window", drawable ? "embed-xid,any" : "any");
897     var_SetInteger (p_mi, "drawable-xid", drawable);
898 }
899
900 /**************************************************************************
901  * get_xwindow
902  **************************************************************************/
903 uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
904 {
905     return var_GetInteger (p_mi, "drawable-xid");
906 }
907
908 /**************************************************************************
909  * set_hwnd
910  **************************************************************************/
911 void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
912                                    void *drawable )
913 {
914     assert (p_mi != NULL);
915 #ifdef WIN32
916     var_SetString (p_mi, "window",
917                    (drawable != NULL) ? "embed-hwnd,any" : "");
918     var_SetAddress (p_mi, "drawable-hwnd", drawable);
919 #else
920     (void) p_mi; (void) drawable;
921 #endif
922 }
923
924 /**************************************************************************
925  * get_hwnd
926  **************************************************************************/
927 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
928 {
929     assert (p_mi != NULL);
930 #ifdef WIN32
931     return var_GetAddress (p_mi, "drawable-hwnd");
932 #else
933     return NULL;
934 #endif
935 }
936
937 /**************************************************************************
938  * Getters for stream information
939  **************************************************************************/
940 libvlc_time_t libvlc_media_player_get_length(
941                              libvlc_media_player_t *p_mi )
942 {
943     input_thread_t *p_input_thread;
944     libvlc_time_t i_time;
945
946     p_input_thread = libvlc_get_input_thread ( p_mi );
947     if( !p_input_thread )
948         return -1;
949
950     i_time = from_mtime(var_GetTime( p_input_thread, "length" ));
951     vlc_object_release( p_input_thread );
952
953     return i_time;
954 }
955
956 libvlc_time_t libvlc_media_player_get_time( libvlc_media_player_t *p_mi )
957 {
958     input_thread_t *p_input_thread;
959     libvlc_time_t i_time;
960
961     p_input_thread = libvlc_get_input_thread ( p_mi );
962     if( !p_input_thread )
963         return -1;
964
965     i_time = from_mtime(var_GetTime( p_input_thread , "time" ));
966     vlc_object_release( p_input_thread );
967     return i_time;
968 }
969
970 void libvlc_media_player_set_time( libvlc_media_player_t *p_mi,
971                                    libvlc_time_t i_time )
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_SetTime( p_input_thread, "time", to_mtime(i_time) );
980     vlc_object_release( p_input_thread );
981 }
982
983 void libvlc_media_player_set_position( libvlc_media_player_t *p_mi,
984                                        float position )
985 {
986     input_thread_t *p_input_thread;
987
988     p_input_thread = libvlc_get_input_thread ( p_mi );
989     if( !p_input_thread )
990         return;
991
992     var_SetFloat( p_input_thread, "position", position );
993     vlc_object_release( p_input_thread );
994 }
995
996 float libvlc_media_player_get_position( libvlc_media_player_t *p_mi )
997 {
998     input_thread_t *p_input_thread;
999     float f_position;
1000
1001     p_input_thread = libvlc_get_input_thread ( p_mi );
1002     if( !p_input_thread )
1003         return -1.0;
1004
1005     f_position = var_GetFloat( p_input_thread, "position" );
1006     vlc_object_release( p_input_thread );
1007
1008     return f_position;
1009 }
1010
1011 void libvlc_media_player_set_chapter( libvlc_media_player_t *p_mi,
1012                                       int chapter )
1013 {
1014     input_thread_t *p_input_thread;
1015
1016     p_input_thread = libvlc_get_input_thread ( p_mi );
1017     if( !p_input_thread )
1018         return;
1019
1020     var_SetInteger( p_input_thread, "chapter", chapter );
1021     vlc_object_release( p_input_thread );
1022 }
1023
1024 int libvlc_media_player_get_chapter( libvlc_media_player_t *p_mi )
1025 {
1026     input_thread_t *p_input_thread;
1027     int i_chapter;
1028
1029     p_input_thread = libvlc_get_input_thread ( p_mi );
1030     if( !p_input_thread )
1031         return -1;
1032
1033     i_chapter = var_GetInteger( p_input_thread, "chapter" );
1034     vlc_object_release( p_input_thread );
1035
1036     return i_chapter;
1037 }
1038
1039 int libvlc_media_player_get_chapter_count( libvlc_media_player_t *p_mi )
1040 {
1041     input_thread_t *p_input_thread;
1042     vlc_value_t val;
1043
1044     p_input_thread = libvlc_get_input_thread ( p_mi );
1045     if( !p_input_thread )
1046         return -1;
1047
1048     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
1049     vlc_object_release( p_input_thread );
1050
1051     return val.i_int;
1052 }
1053
1054 int libvlc_media_player_get_chapter_count_for_title(
1055                                  libvlc_media_player_t *p_mi,
1056                                  int i_title )
1057 {
1058     input_thread_t *p_input_thread;
1059     vlc_value_t val;
1060
1061     p_input_thread = libvlc_get_input_thread ( p_mi );
1062     if( !p_input_thread )
1063         return -1;
1064
1065     char *psz_name;
1066     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
1067     {
1068         vlc_object_release( p_input_thread );
1069         return -1;
1070     }
1071     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
1072     vlc_object_release( p_input_thread );
1073     free( psz_name );
1074
1075     return val.i_int;
1076 }
1077
1078 void libvlc_media_player_set_title( libvlc_media_player_t *p_mi,
1079                                     int i_title )
1080 {
1081     input_thread_t *p_input_thread;
1082
1083     p_input_thread = libvlc_get_input_thread ( p_mi );
1084     if( !p_input_thread )
1085         return;
1086
1087     var_SetInteger( p_input_thread, "title", i_title );
1088     vlc_object_release( p_input_thread );
1089
1090     //send event
1091     libvlc_event_t event;
1092     event.type = libvlc_MediaPlayerTitleChanged;
1093     event.u.media_player_title_changed.new_title = i_title;
1094     libvlc_event_send( p_mi->p_event_manager, &event );
1095 }
1096
1097 int libvlc_media_player_get_title( libvlc_media_player_t *p_mi )
1098 {
1099     input_thread_t *p_input_thread;
1100     int i_title;
1101
1102     p_input_thread = libvlc_get_input_thread ( p_mi );
1103     if( !p_input_thread )
1104         return -1;
1105
1106     i_title = var_GetInteger( p_input_thread, "title" );
1107     vlc_object_release( p_input_thread );
1108
1109     return i_title;
1110 }
1111
1112 int libvlc_media_player_get_title_count( libvlc_media_player_t *p_mi )
1113 {
1114     input_thread_t *p_input_thread;
1115     vlc_value_t val;
1116
1117     p_input_thread = libvlc_get_input_thread ( p_mi );
1118     if( !p_input_thread )
1119         return -1;
1120
1121     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
1122     vlc_object_release( p_input_thread );
1123
1124     return val.i_int;
1125 }
1126
1127 void libvlc_media_player_next_chapter( libvlc_media_player_t *p_mi )
1128 {
1129     input_thread_t *p_input_thread;
1130
1131     p_input_thread = libvlc_get_input_thread ( p_mi );
1132     if( !p_input_thread )
1133         return;
1134
1135     int i_type = var_Type( p_input_thread, "next-chapter" );
1136     var_TriggerCallback( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1137                             "next-chapter":"next-title" );
1138
1139     vlc_object_release( p_input_thread );
1140 }
1141
1142 void libvlc_media_player_previous_chapter( libvlc_media_player_t *p_mi )
1143 {
1144     input_thread_t *p_input_thread;
1145
1146     p_input_thread = libvlc_get_input_thread ( p_mi );
1147     if( !p_input_thread )
1148         return;
1149
1150     int i_type = var_Type( p_input_thread, "next-chapter" );
1151     var_TriggerCallback( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1152                             "prev-chapter":"prev-title" );
1153
1154     vlc_object_release( p_input_thread );
1155 }
1156
1157 float libvlc_media_player_get_fps( libvlc_media_player_t *p_mi )
1158 {
1159     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1160     double f_fps = 0.0;
1161
1162     if( p_input_thread )
1163     {
1164         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1165             f_fps = 0.0;
1166         vlc_object_release( p_input_thread );
1167     }
1168     return f_fps;
1169 }
1170
1171 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi )
1172 {
1173     bool b_will_play;
1174     input_thread_t *p_input_thread =
1175                             libvlc_get_input_thread ( p_mi );
1176     if ( !p_input_thread )
1177         return false;
1178
1179     b_will_play = !p_input_thread->b_die && !p_input_thread->b_dead;
1180     vlc_object_release( p_input_thread );
1181
1182     return b_will_play;
1183 }
1184
1185 int libvlc_media_player_set_rate( libvlc_media_player_t *p_mi, float rate )
1186 {
1187     if (rate < 0.)
1188     {
1189         libvlc_printerr ("Playing backward not supported");
1190         return -1;
1191     }
1192
1193     var_SetFloat (p_mi, "rate", rate);
1194
1195     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1196     if( !p_input_thread )
1197         return 0;
1198     var_SetFloat( p_input_thread, "rate", rate );
1199     vlc_object_release( p_input_thread );
1200     return 0;
1201 }
1202
1203 float libvlc_media_player_get_rate( libvlc_media_player_t *p_mi )
1204 {
1205     return var_GetFloat (p_mi, "rate");
1206 }
1207
1208 libvlc_state_t libvlc_media_player_get_state( libvlc_media_player_t *p_mi )
1209 {
1210     lock(p_mi);
1211     libvlc_state_t state = p_mi->state;
1212     unlock(p_mi);
1213     return state;
1214 }
1215
1216 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi )
1217 {
1218     input_thread_t *p_input_thread;
1219     bool b_seekable;
1220
1221     p_input_thread = libvlc_get_input_thread ( p_mi );
1222     if ( !p_input_thread )
1223         return false;
1224     b_seekable = var_GetBool( p_input_thread, "can-seek" );
1225     vlc_object_release( p_input_thread );
1226
1227     return b_seekable;
1228 }
1229
1230 void libvlc_media_player_navigate( libvlc_media_player_t* p_mi,
1231                                    unsigned navigate )
1232 {
1233     input_thread_t *p_input_thread;
1234
1235     if ( navigate > libvlc_navigate_to_action_size)
1236       return;
1237
1238     p_input_thread = libvlc_get_input_thread ( p_mi );
1239     if ( !p_input_thread )
1240       return;
1241
1242     var_SetInteger( p_mi->p_libvlc_instance->p_libvlc_int,
1243                     "key-action", libvlc_navigate_to_action[navigate] );
1244
1245     vlc_object_release( p_input_thread );
1246 }
1247
1248 /* internal function, used by audio, video */
1249 libvlc_track_description_t *
1250         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1251                                       const char *psz_variable )
1252 {
1253     input_thread_t *p_input = libvlc_get_input_thread( p_mi );
1254     libvlc_track_description_t *p_track_description = NULL,
1255                                *p_actual, *p_previous;
1256
1257     if( !p_input )
1258         return NULL;
1259
1260     vlc_value_t val_list, text_list;
1261     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1262
1263     /* no tracks */
1264     if( val_list.p_list->i_count <= 0 )
1265         goto end;
1266
1267     p_track_description = ( libvlc_track_description_t * )
1268         malloc( sizeof( libvlc_track_description_t ) );
1269     if ( !p_track_description )
1270     {
1271         libvlc_printerr( "Not enough memory" );
1272         goto end;
1273     }
1274     p_actual = p_track_description;
1275     p_previous = NULL;
1276     for( int i = 0; i < val_list.p_list->i_count; i++ )
1277     {
1278         if( !p_actual )
1279         {
1280             p_actual = ( libvlc_track_description_t * )
1281                 malloc( sizeof( libvlc_track_description_t ) );
1282             if ( !p_actual )
1283             {
1284                 libvlc_track_description_release( p_track_description );
1285                 libvlc_printerr( "Not enough memory" );
1286                 goto end;
1287             }
1288         }
1289         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1290         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1291         p_actual->p_next = NULL;
1292         if( p_previous )
1293             p_previous->p_next = p_actual;
1294         p_previous = p_actual;
1295         p_actual =  NULL;
1296     }
1297
1298 end:
1299     var_FreeList( &val_list, &text_list );
1300     vlc_object_release( p_input );
1301
1302     return p_track_description;
1303 }
1304
1305 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1306 {
1307     libvlc_track_description_t *p_actual, *p_before;
1308     p_actual = p_td;
1309
1310     while ( p_actual )
1311     {
1312         free( p_actual->psz_name );
1313         p_before = p_actual;
1314         p_actual = p_before->p_next;
1315         free( p_before );
1316     }
1317 }
1318
1319 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi )
1320 {
1321     input_thread_t *p_input_thread;
1322     bool b_can_pause;
1323
1324     p_input_thread = libvlc_get_input_thread ( p_mi );
1325     if ( !p_input_thread )
1326         return false;
1327     b_can_pause = var_GetBool( p_input_thread, "can-pause" );
1328     vlc_object_release( p_input_thread );
1329
1330     return b_can_pause;
1331 }
1332
1333 void libvlc_media_player_next_frame( libvlc_media_player_t *p_mi )
1334 {
1335     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1336     if( p_input_thread != NULL )
1337     {
1338         var_TriggerCallback( p_input_thread, "frame-next" );
1339         vlc_object_release( p_input_thread );
1340     }
1341 }