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