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