]> git.sesse.net Git - vlc/blob - src/control/media_player.c
66fe1f31e10e8c7c727dd72d6afa06c4962a86f4
[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     input_thread_t *p_input_thread;
767     vout_thread_t *p_vout = NULL;
768
769     p_mi->drawable.xid = drawable;
770
771     /* Allow on the fly drawable changing. This is tricky has this may
772      * not be supported by every vout. We though can't disable it
773      * because of some creepy drawable type that are not flexible enough
774      * (Win32 HWND for instance) */
775     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
776     if( !p_input_thread ) {
777         /* No input, nothing more to do, we are fine */
778         libvlc_exception_clear( p_e );
779         return;
780     }
781
782     p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD );
783     if( p_vout )
784     {
785         vout_Control( p_vout , VOUT_REPARENT, drawable);
786         vlc_object_release( p_vout );
787     }
788     vlc_object_release( p_input_thread );
789 }
790
791 /**************************************************************************
792  * Get Drawable
793  **************************************************************************/
794 libvlc_drawable_t
795 libvlc_media_player_get_drawable ( libvlc_media_player_t *p_mi,
796                                    libvlc_exception_t *p_e )
797 {
798     VLC_UNUSED(p_e);
799     return p_mi->drawable.xid;
800 }
801
802 /**************************************************************************
803  * Getters for stream information
804  **************************************************************************/
805 libvlc_time_t libvlc_media_player_get_length(
806                              libvlc_media_player_t *p_mi,
807                              libvlc_exception_t *p_e )
808 {
809     input_thread_t *p_input_thread;
810     vlc_value_t val;
811
812     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
813     if( !p_input_thread )
814         return -1;
815
816     var_Get( p_input_thread, "length", &val );
817     vlc_object_release( p_input_thread );
818
819     return (val.i_time+500LL)/1000LL;
820 }
821
822 libvlc_time_t libvlc_media_player_get_time(
823                                    libvlc_media_player_t *p_mi,
824                                    libvlc_exception_t *p_e )
825 {
826     input_thread_t *p_input_thread;
827     vlc_value_t val;
828
829     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
830     if( !p_input_thread )
831         return -1;
832
833     var_Get( p_input_thread , "time", &val );
834     vlc_object_release( p_input_thread );
835     return (val.i_time+500LL)/1000LL;
836 }
837
838 void libvlc_media_player_set_time(
839                                  libvlc_media_player_t *p_mi,
840                                  libvlc_time_t time,
841                                  libvlc_exception_t *p_e )
842 {
843     input_thread_t *p_input_thread;
844     vlc_value_t value;
845
846     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
847     if( !p_input_thread )
848         return;
849
850     value.i_time = time*1000LL;
851     var_Set( p_input_thread, "time", value );
852     vlc_object_release( p_input_thread );
853 }
854
855 void libvlc_media_player_set_position(
856                                 libvlc_media_player_t *p_mi,
857                                 float position,
858                                 libvlc_exception_t *p_e )
859 {
860     input_thread_t *p_input_thread;
861     vlc_value_t val;
862     val.f_float = position;
863
864     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
865     if( !p_input_thread )
866         return;
867
868     var_Set( p_input_thread, "position", val );
869     vlc_object_release( p_input_thread );
870 }
871
872 float libvlc_media_player_get_position(
873                                  libvlc_media_player_t *p_mi,
874                                  libvlc_exception_t *p_e )
875 {
876     input_thread_t *p_input_thread;
877     vlc_value_t val;
878
879     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
880     if( !p_input_thread )
881         return -1.0;
882
883     var_Get( p_input_thread, "position", &val );
884     vlc_object_release( p_input_thread );
885
886     return val.f_float;
887 }
888
889 void libvlc_media_player_set_chapter(
890                                  libvlc_media_player_t *p_mi,
891                                  int chapter,
892                                  libvlc_exception_t *p_e )
893 {
894     input_thread_t *p_input_thread;
895     vlc_value_t val;
896     val.i_int = chapter;
897
898     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
899     if( !p_input_thread )
900         return;
901
902     var_Set( p_input_thread, "chapter", val );
903     vlc_object_release( p_input_thread );
904 }
905
906 int libvlc_media_player_get_chapter(
907                                  libvlc_media_player_t *p_mi,
908                                  libvlc_exception_t *p_e )
909 {
910     input_thread_t *p_input_thread;
911     vlc_value_t val;
912
913     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
914     if( !p_input_thread )
915         return -1;
916
917     var_Get( p_input_thread, "chapter", &val );
918     vlc_object_release( p_input_thread );
919
920     return val.i_int;
921 }
922
923 int libvlc_media_player_get_chapter_count(
924                                  libvlc_media_player_t *p_mi,
925                                  libvlc_exception_t *p_e )
926 {
927     input_thread_t *p_input_thread;
928     vlc_value_t val;
929
930     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
931     if( !p_input_thread )
932         return -1;
933
934     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
935     vlc_object_release( p_input_thread );
936
937     return val.i_int;
938 }
939
940 int libvlc_media_player_get_chapter_count_for_title(
941                                  libvlc_media_player_t *p_mi,
942                                  int i_title,
943                                  libvlc_exception_t *p_e )
944 {
945     input_thread_t *p_input_thread;
946     vlc_value_t val;
947
948     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
949     if( !p_input_thread )
950         return -1;
951
952     char *psz_name;
953     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
954     {
955         vlc_object_release( p_input_thread );
956         return -1;
957     }
958     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
959     vlc_object_release( p_input_thread );
960     free( psz_name );
961
962     return val.i_int;
963 }
964
965 void libvlc_media_player_set_title(
966                                  libvlc_media_player_t *p_mi,
967                                  int i_title,
968                                  libvlc_exception_t *p_e )
969 {
970     input_thread_t *p_input_thread;
971     vlc_value_t val;
972     val.i_int = i_title;
973
974     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
975     if( !p_input_thread )
976         return;
977
978     var_Set( p_input_thread, "title", val );
979     vlc_object_release( p_input_thread );
980
981     //send event
982     libvlc_event_t event;
983     event.type = libvlc_MediaPlayerTitleChanged;
984     event.u.media_player_title_changed.new_title = i_title;
985     libvlc_event_send( p_mi->p_event_manager, &event );
986 }
987
988 int libvlc_media_player_get_title(
989                                  libvlc_media_player_t *p_mi,
990                                  libvlc_exception_t *p_e )
991 {
992     input_thread_t *p_input_thread;
993     vlc_value_t val;
994
995     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
996     if( !p_input_thread )
997         return -1;
998
999     var_Get( p_input_thread, "title", &val );
1000     vlc_object_release( p_input_thread );
1001
1002     return val.i_int;
1003 }
1004
1005 int libvlc_media_player_get_title_count(
1006                                  libvlc_media_player_t *p_mi,
1007                                  libvlc_exception_t *p_e )
1008 {
1009     input_thread_t *p_input_thread;
1010     vlc_value_t val;
1011
1012     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1013     if( !p_input_thread )
1014         return -1;
1015
1016     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
1017     vlc_object_release( p_input_thread );
1018
1019     return val.i_int;
1020 }
1021
1022 void libvlc_media_player_next_chapter(
1023                                  libvlc_media_player_t *p_mi,
1024                                  libvlc_exception_t *p_e )
1025 {
1026     input_thread_t *p_input_thread;
1027
1028     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1029     if( !p_input_thread )
1030         return;
1031
1032     int i_type = var_Type( p_input_thread, "next-chapter" );
1033     vlc_value_t val;
1034     val.b_bool = true;
1035     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1036                             "next-chapter":"next-title", val );
1037
1038     vlc_object_release( p_input_thread );
1039 }
1040
1041 void libvlc_media_player_previous_chapter(
1042                                  libvlc_media_player_t *p_mi,
1043                                  libvlc_exception_t *p_e )
1044 {
1045     input_thread_t *p_input_thread;
1046
1047     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1048     if( !p_input_thread )
1049         return;
1050
1051     int i_type = var_Type( p_input_thread, "next-chapter" );
1052     vlc_value_t val;
1053     val.b_bool = true;
1054     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1055                             "prev-chapter":"prev-title", val );
1056
1057     vlc_object_release( p_input_thread );
1058 }
1059
1060 float libvlc_media_player_get_fps(
1061                                  libvlc_media_player_t *p_mi,
1062                                  libvlc_exception_t *p_e)
1063 {
1064     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1065     double f_fps = 0.0;
1066
1067     if( p_input_thread )
1068     {
1069         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1070             f_fps = 0.0;
1071         vlc_object_release( p_input_thread );
1072     }
1073     return f_fps;
1074 }
1075
1076 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
1077                                      libvlc_exception_t *p_e)
1078 {
1079     input_thread_t *p_input_thread =
1080                             libvlc_get_input_thread ( p_mi, p_e);
1081     if ( !p_input_thread )
1082         return false;
1083
1084     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
1085     {
1086         vlc_object_release( p_input_thread );
1087         return true;
1088     }
1089     vlc_object_release( p_input_thread );
1090     return false;
1091 }
1092
1093 void libvlc_media_player_set_rate(
1094                                  libvlc_media_player_t *p_mi,
1095                                  float rate,
1096                                  libvlc_exception_t *p_e )
1097 {
1098     input_thread_t *p_input_thread;
1099     vlc_value_t val;
1100     bool b_can_rewind;
1101
1102     if( rate != 0 )
1103         RAISEVOID( "Rate value is invalid" );
1104
1105     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1106     if( !p_input_thread )
1107         return;
1108
1109     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1110     if( (rate < 0) && !b_can_rewind )
1111     {
1112         vlc_object_release( p_input_thread );
1113         libvlc_exception_raise( p_e, "Rate value is invalid" );
1114         return;
1115     }
1116
1117     val.i_int = 1000.0f/rate;
1118     var_Set( p_input_thread, "rate", val );
1119     vlc_object_release( p_input_thread );
1120 }
1121
1122 float libvlc_media_player_get_rate(
1123                                  libvlc_media_player_t *p_mi,
1124                                  libvlc_exception_t *p_e )
1125 {
1126     input_thread_t *p_input_thread;
1127     vlc_value_t val;
1128     bool b_can_rewind;
1129
1130     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1131     if( !p_input_thread )
1132         return 0.0;  /* rate < 0 indicates rewind */
1133
1134     var_Get( p_input_thread, "rate", &val );
1135     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1136     if( (val.i_int < 0) && !b_can_rewind )
1137     {
1138         libvlc_exception_raise( p_e, "invalid rate" );
1139         return 0.0;
1140     }
1141     vlc_object_release( p_input_thread );
1142
1143     return (float)1000.0f/val.i_int;
1144 }
1145
1146 libvlc_state_t libvlc_media_player_get_state(
1147                                  libvlc_media_player_t *p_mi,
1148                                  libvlc_exception_t *p_e )
1149 {
1150     input_thread_t *p_input_thread;
1151     vlc_value_t val;
1152
1153     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1154     if ( !p_input_thread )
1155     {
1156         /* We do return the right value, no need to throw an exception */
1157         if( libvlc_exception_raised( p_e ) )
1158             libvlc_exception_clear( p_e );
1159         return libvlc_Ended;
1160     }
1161
1162     var_Get( p_input_thread, "state", &val );
1163     vlc_object_release( p_input_thread );
1164
1165     return vlc_to_libvlc_state(val.i_int);
1166 }
1167
1168 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1169                                        libvlc_exception_t *p_e )
1170 {
1171     input_thread_t *p_input_thread;
1172     vlc_value_t val;
1173
1174     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1175     if ( !p_input_thread )
1176     {
1177         /* We do return the right value, no need to throw an exception */
1178         if( libvlc_exception_raised( p_e ) )
1179             libvlc_exception_clear( p_e );
1180         return false;
1181     }
1182     var_Get( p_input_thread, "can-seek", &val );
1183     vlc_object_release( p_input_thread );
1184
1185     return val.b_bool;
1186 }
1187
1188 /* internal function, used by audio, video */
1189 libvlc_track_description_t *
1190         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1191                                       const char *psz_variable,
1192                                       libvlc_exception_t *p_e )
1193 {
1194     input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
1195
1196     if( !p_input )
1197         return NULL;
1198
1199     vlc_value_t val_list, text_list;
1200     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1201
1202     if( val_list.p_list->i_count <= 0 ) /* no tracks */
1203         return NULL;
1204
1205     libvlc_track_description_t *p_track_description, *p_actual, *p_previous;
1206     p_track_description = ( libvlc_track_description_t * )
1207         malloc( sizeof( libvlc_track_description_t ) );
1208     if ( !p_track_description )
1209     {
1210         var_Change( p_input, psz_variable, VLC_VAR_FREELIST,
1211                     &val_list, &text_list);
1212         vlc_object_release( p_input );
1213         libvlc_exception_raise( p_e, "no enough memory" );
1214         return NULL;
1215     }
1216     p_actual = p_track_description;
1217     p_previous = NULL;
1218     for( int i = 0; i < val_list.p_list->i_count; i++ )
1219     {
1220         if( !p_actual )
1221         {
1222             p_actual = ( libvlc_track_description_t * )
1223                 malloc( sizeof( libvlc_track_description_t ) );
1224             if ( !p_actual )
1225             {
1226                 libvlc_track_description_release( p_track_description );
1227                 var_Change( p_input, psz_variable, VLC_VAR_FREELIST,
1228                             &val_list, &text_list);
1229                 vlc_object_release( p_input );
1230                 libvlc_exception_raise( p_e, "no enough memory" );
1231                 return NULL;
1232             }
1233         }
1234         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1235         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1236         p_actual->p_next = NULL;
1237         if( p_previous )
1238             p_previous->p_next = p_actual;
1239         p_previous = p_actual;
1240         p_actual =  NULL;
1241     }
1242     var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1243     vlc_object_release( p_input );
1244
1245     return p_track_description;
1246 }
1247
1248 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1249 {
1250     libvlc_track_description_t *p_actual, *p_before;
1251     p_actual = p_td;
1252
1253     while ( p_actual )
1254     {
1255         free( p_actual->psz_name );
1256         p_before = p_actual;
1257         p_actual = p_before->p_next;
1258         free( p_before );
1259     }
1260 }
1261
1262 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1263                                      libvlc_exception_t *p_e )
1264 {
1265     input_thread_t *p_input_thread;
1266     vlc_value_t val;
1267
1268     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1269     if ( !p_input_thread )
1270     {
1271         /* We do return the right value, no need to throw an exception */
1272         if( libvlc_exception_raised( p_e ) )
1273             libvlc_exception_clear( p_e );
1274         return false;
1275     }
1276     var_Get( p_input_thread, "can-pause", &val );
1277     vlc_object_release( p_input_thread );
1278
1279     return val.b_bool;
1280 }