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