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