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