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