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