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