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