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