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