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