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