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