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