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