]> git.sesse.net Git - vlc/blob - src/control/media_player.c
19dd794043008563943341bc7788a9eb3f678678
[vlc] / src / control / media_player.c
1 /*****************************************************************************
2  * media_player.c: Libvlc API Media Instance management functions
3  *****************************************************************************
4  * Copyright (C) 2005-2009 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #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_Stop( 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  * 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.agl = 0;
278     p_mi->drawable.xid = 0;
279     p_mi->drawable.hwnd = NULL;
280     p_mi->p_libvlc_instance = p_libvlc_instance;
281     p_mi->p_input_thread = NULL;
282     p_mi->i_refcount = 1;
283     p_mi->b_own_its_input_thread = true;
284     vlc_mutex_init( &p_mi->object_lock );
285     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
286             p_libvlc_instance, p_e );
287     if( libvlc_exception_raised( p_e ) )
288     {
289         vlc_mutex_destroy( &p_mi->object_lock );
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_Create( p_mi->p_libvlc_instance->p_libvlc_int,
610                                          p_mi->p_md->p_input_item, NULL, NULL );
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_Create( p_input_thread, "drawable-nsobject", VLC_VAR_ADDRESS );
636     if( p_mi->drawable.nsobject != NULL )
637     {
638         vlc_value_t val = { .p_address = p_mi->drawable.nsobject };
639         var_Set( p_input_thread, "drawable-nsobject", val );
640     }
641         
642     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
643     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
644     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
645
646     if( input_Start( p_input_thread ) )
647     {
648         vlc_object_release( p_input_thread );
649         p_mi->p_input_thread = NULL;
650     }
651
652     vlc_mutex_unlock( &p_mi->object_lock );
653 }
654
655 /**************************************************************************
656  * Pause.
657  **************************************************************************/
658 void libvlc_media_player_pause( libvlc_media_player_t *p_mi,
659                                   libvlc_exception_t *p_e )
660 {
661     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
662     if( !p_input_thread )
663         return;
664
665     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
666     if( state == libvlc_Playing || state == libvlc_Buffering )
667     {
668         if( libvlc_media_player_can_pause( p_mi, p_e ) )
669             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
670         else
671             libvlc_media_player_stop( p_mi, p_e );
672     }
673     else
674         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
675
676     vlc_object_release( p_input_thread );
677 }
678
679 /**************************************************************************
680  * Tells whether the media player is currently playing.
681  *
682  * Enter with lock held.
683  **************************************************************************/
684 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi,
685                                      libvlc_exception_t *p_e )
686 {
687     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
688     return (libvlc_Playing == state) || (libvlc_Buffering == state);
689 }
690
691 /**************************************************************************
692  * Stop playing.
693  **************************************************************************/
694 void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
695                                  libvlc_exception_t *p_e )
696 {
697     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
698
699     if( state == libvlc_Playing ||
700         state == libvlc_Paused ||
701         state == libvlc_Buffering )
702     {
703         /* Send a stop notification event only if we are in playing,
704          * buffering or paused states */
705         libvlc_media_set_state( p_mi->p_md, libvlc_Ended, p_e );
706
707         /* Construct and send the event */
708         libvlc_event_t event;
709         event.type = libvlc_MediaPlayerStopped;
710         libvlc_event_send( p_mi->p_event_manager, &event );
711     }
712
713     if( p_mi->b_own_its_input_thread )
714     {
715         vlc_mutex_lock( &p_mi->object_lock );
716         release_input_thread( p_mi, true ); /* This will stop the input thread */
717         vlc_mutex_unlock( &p_mi->object_lock );
718     }
719     else
720     {
721         input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
722
723         if( !p_input_thread )
724             return;
725
726         input_Stop( p_input_thread, true );
727         vlc_object_release( p_input_thread );
728         p_mi->p_input_thread = NULL;
729     }
730 }
731
732 /**************************************************************************
733  * set_nsobject
734  **************************************************************************/
735 void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
736                                                                  void * drawable,
737                                                                  libvlc_exception_t *p_e )
738 {
739     (void) p_e;
740     p_mi->drawable.nsobject = drawable;
741 }
742
743 /**************************************************************************
744  * get_nsobject
745  **************************************************************************/
746 uint32_t libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
747 {
748     return p_mi->drawable.nsobject;
749 }
750
751 /**************************************************************************
752  * set_agl
753  **************************************************************************/
754 void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
755                                       uint32_t drawable,
756                                       libvlc_exception_t *p_e )
757 {
758     (void) p_e;
759     p_mi->drawable.agl = drawable;
760 }
761
762 /**************************************************************************
763  * get_agl
764  **************************************************************************/
765 uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
766 {
767     return p_mi->drawable.agl;
768 }
769
770 /**************************************************************************
771  * set_xwindow
772  **************************************************************************/
773 void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
774                                       uint32_t drawable,
775                                       libvlc_exception_t *p_e )
776 {
777     (void) p_e;
778     p_mi->drawable.xid = drawable;
779 }
780
781 /**************************************************************************
782  * get_xwindow
783  **************************************************************************/
784 uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
785 {
786     return p_mi->drawable.xid;
787 }
788
789 /**************************************************************************
790  * set_hwnd
791  **************************************************************************/
792 void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
793                                    void *drawable,
794                                    libvlc_exception_t *p_e )
795 {
796     (void) p_e;
797     p_mi->drawable.hwnd = drawable;
798 }
799
800 /**************************************************************************
801  * get_hwnd
802  **************************************************************************/
803 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
804 {
805     return p_mi->drawable.hwnd;
806 }
807
808 /**************************************************************************
809  * Set Drawable
810  **************************************************************************/
811 void libvlc_media_player_set_drawable( libvlc_media_player_t *p_mi,
812                                        libvlc_drawable_t drawable,
813                                        libvlc_exception_t *p_e )
814 {
815 #ifdef WIN32
816     if (sizeof (HWND) <= sizeof (libvlc_drawable_t))
817         p_mi->drawable.hwnd = (HWND)drawable;
818     else
819         libvlc_exception_raise(p_e, "Operation not supported");
820 #elif defined(__APPLE__)
821     p_mi->drawable.agl = drawable;
822     (void) p_e;
823 #else
824     p_mi->drawable.xid = drawable;
825     (void) p_e;
826 #endif
827 }
828
829 /**************************************************************************
830  * Get Drawable
831  **************************************************************************/
832 libvlc_drawable_t
833 libvlc_media_player_get_drawable ( libvlc_media_player_t *p_mi,
834                                    libvlc_exception_t *p_e )
835 {
836     VLC_UNUSED(p_e);
837
838 #ifdef WIN32
839     if (sizeof (HWND) <= sizeof (libvlc_drawable_t))
840         return (libvlc_drawable_t)p_mi->drawable.hwnd;
841     else
842         return 0;
843 #elif defined(__APPLE__)
844     return p_mi->drawable.agl;
845 #else
846     return p_mi->drawable.xid;
847 #endif
848 }
849
850 /**************************************************************************
851  * Getters for stream information
852  **************************************************************************/
853 libvlc_time_t libvlc_media_player_get_length(
854                              libvlc_media_player_t *p_mi,
855                              libvlc_exception_t *p_e )
856 {
857     input_thread_t *p_input_thread;
858     vlc_value_t val;
859
860     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
861     if( !p_input_thread )
862         return -1;
863
864     var_Get( p_input_thread, "length", &val );
865     vlc_object_release( p_input_thread );
866
867     return (val.i_time+500LL)/1000LL;
868 }
869
870 libvlc_time_t libvlc_media_player_get_time(
871                                    libvlc_media_player_t *p_mi,
872                                    libvlc_exception_t *p_e )
873 {
874     input_thread_t *p_input_thread;
875     vlc_value_t val;
876
877     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
878     if( !p_input_thread )
879         return -1;
880
881     var_Get( p_input_thread , "time", &val );
882     vlc_object_release( p_input_thread );
883     return (val.i_time+500LL)/1000LL;
884 }
885
886 void libvlc_media_player_set_time(
887                                  libvlc_media_player_t *p_mi,
888                                  libvlc_time_t time,
889                                  libvlc_exception_t *p_e )
890 {
891     input_thread_t *p_input_thread;
892
893     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
894     if( !p_input_thread )
895         return;
896
897     var_SetTime( p_input_thread, "time", time*1000LL );
898     vlc_object_release( p_input_thread );
899 }
900
901 void libvlc_media_player_set_position(
902                                 libvlc_media_player_t *p_mi,
903                                 float position,
904                                 libvlc_exception_t *p_e )
905 {
906     input_thread_t *p_input_thread;
907
908     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
909     if( !p_input_thread )
910         return;
911
912     var_SetFloat( p_input_thread, "position", position );
913     vlc_object_release( p_input_thread );
914 }
915
916 float libvlc_media_player_get_position(
917                                  libvlc_media_player_t *p_mi,
918                                  libvlc_exception_t *p_e )
919 {
920     input_thread_t *p_input_thread;
921     vlc_value_t val;
922
923     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
924     if( !p_input_thread )
925         return -1.0;
926
927     var_Get( p_input_thread, "position", &val );
928     vlc_object_release( p_input_thread );
929
930     return val.f_float;
931 }
932
933 void libvlc_media_player_set_chapter(
934                                  libvlc_media_player_t *p_mi,
935                                  int chapter,
936                                  libvlc_exception_t *p_e )
937 {
938     input_thread_t *p_input_thread;
939
940     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
941     if( !p_input_thread )
942         return;
943
944     var_SetInteger( p_input_thread, "chapter", chapter );
945     vlc_object_release( p_input_thread );
946 }
947
948 int libvlc_media_player_get_chapter(
949                                  libvlc_media_player_t *p_mi,
950                                  libvlc_exception_t *p_e )
951 {
952     input_thread_t *p_input_thread;
953     vlc_value_t val;
954
955     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
956     if( !p_input_thread )
957         return -1;
958
959     var_Get( p_input_thread, "chapter", &val );
960     vlc_object_release( p_input_thread );
961
962     return val.i_int;
963 }
964
965 int libvlc_media_player_get_chapter_count(
966                                  libvlc_media_player_t *p_mi,
967                                  libvlc_exception_t *p_e )
968 {
969     input_thread_t *p_input_thread;
970     vlc_value_t val;
971
972     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
973     if( !p_input_thread )
974         return -1;
975
976     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
977     vlc_object_release( p_input_thread );
978
979     return val.i_int;
980 }
981
982 int libvlc_media_player_get_chapter_count_for_title(
983                                  libvlc_media_player_t *p_mi,
984                                  int i_title,
985                                  libvlc_exception_t *p_e )
986 {
987     input_thread_t *p_input_thread;
988     vlc_value_t val;
989
990     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
991     if( !p_input_thread )
992         return -1;
993
994     char *psz_name;
995     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
996     {
997         vlc_object_release( p_input_thread );
998         return -1;
999     }
1000     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
1001     vlc_object_release( p_input_thread );
1002     free( psz_name );
1003
1004     return val.i_int;
1005 }
1006
1007 void libvlc_media_player_set_title(
1008                                  libvlc_media_player_t *p_mi,
1009                                  int i_title,
1010                                  libvlc_exception_t *p_e )
1011 {
1012     input_thread_t *p_input_thread;
1013
1014     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1015     if( !p_input_thread )
1016         return;
1017
1018     var_SetInteger( p_input_thread, "title", i_title );
1019     vlc_object_release( p_input_thread );
1020
1021     //send event
1022     libvlc_event_t event;
1023     event.type = libvlc_MediaPlayerTitleChanged;
1024     event.u.media_player_title_changed.new_title = i_title;
1025     libvlc_event_send( p_mi->p_event_manager, &event );
1026 }
1027
1028 int libvlc_media_player_get_title(
1029                                  libvlc_media_player_t *p_mi,
1030                                  libvlc_exception_t *p_e )
1031 {
1032     input_thread_t *p_input_thread;
1033     vlc_value_t val;
1034
1035     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1036     if( !p_input_thread )
1037         return -1;
1038
1039     var_Get( p_input_thread, "title", &val );
1040     vlc_object_release( p_input_thread );
1041
1042     return val.i_int;
1043 }
1044
1045 int libvlc_media_player_get_title_count(
1046                                  libvlc_media_player_t *p_mi,
1047                                  libvlc_exception_t *p_e )
1048 {
1049     input_thread_t *p_input_thread;
1050     vlc_value_t val;
1051
1052     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1053     if( !p_input_thread )
1054         return -1;
1055
1056     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
1057     vlc_object_release( p_input_thread );
1058
1059     return val.i_int;
1060 }
1061
1062 void libvlc_media_player_next_chapter(
1063                                  libvlc_media_player_t *p_mi,
1064                                  libvlc_exception_t *p_e )
1065 {
1066     input_thread_t *p_input_thread;
1067
1068     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1069     if( !p_input_thread )
1070         return;
1071
1072     int i_type = var_Type( p_input_thread, "next-chapter" );
1073     var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1074                             "next-chapter":"next-title", true );
1075
1076     vlc_object_release( p_input_thread );
1077 }
1078
1079 void libvlc_media_player_previous_chapter(
1080                                  libvlc_media_player_t *p_mi,
1081                                  libvlc_exception_t *p_e )
1082 {
1083     input_thread_t *p_input_thread;
1084
1085     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
1086     if( !p_input_thread )
1087         return;
1088
1089     int i_type = var_Type( p_input_thread, "next-chapter" );
1090     var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1091                             "prev-chapter":"prev-title", true );
1092
1093     vlc_object_release( p_input_thread );
1094 }
1095
1096 float libvlc_media_player_get_fps(
1097                                  libvlc_media_player_t *p_mi,
1098                                  libvlc_exception_t *p_e)
1099 {
1100     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1101     double f_fps = 0.0;
1102
1103     if( p_input_thread )
1104     {
1105         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1106             f_fps = 0.0;
1107         vlc_object_release( p_input_thread );
1108     }
1109     return f_fps;
1110 }
1111
1112 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
1113                                      libvlc_exception_t *p_e)
1114 {
1115     input_thread_t *p_input_thread =
1116                             libvlc_get_input_thread ( p_mi, p_e);
1117     if ( !p_input_thread )
1118         return false;
1119
1120     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
1121     {
1122         vlc_object_release( p_input_thread );
1123         return true;
1124     }
1125     vlc_object_release( p_input_thread );
1126     return false;
1127 }
1128
1129 void libvlc_media_player_set_rate(
1130                                  libvlc_media_player_t *p_mi,
1131                                  float rate,
1132                                  libvlc_exception_t *p_e )
1133 {
1134     input_thread_t *p_input_thread;
1135     bool b_can_rewind;
1136
1137     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1138     if( !p_input_thread )
1139         return;
1140
1141     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1142     if( (rate < 0.0) && !b_can_rewind )
1143     {
1144         vlc_object_release( p_input_thread );
1145         libvlc_exception_raise( p_e, "Rate value is invalid" );
1146         return;
1147     }
1148
1149     var_SetInteger( p_input_thread, "rate", 1000.0f/rate );
1150     vlc_object_release( p_input_thread );
1151 }
1152
1153 float libvlc_media_player_get_rate(
1154                                  libvlc_media_player_t *p_mi,
1155                                  libvlc_exception_t *p_e )
1156 {
1157     input_thread_t *p_input_thread;
1158     vlc_value_t val;
1159     bool b_can_rewind;
1160
1161     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1162     if( !p_input_thread )
1163         return 0.0;  /* rate < 0 indicates rewind */
1164
1165     var_Get( p_input_thread, "rate", &val );
1166     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1167     if( (val.i_int < 0) && !b_can_rewind )
1168     {
1169         libvlc_exception_raise( p_e, "invalid rate" );
1170         return 0.0;
1171     }
1172     vlc_object_release( p_input_thread );
1173
1174     return (float)1000.0f/val.i_int;
1175 }
1176
1177 libvlc_state_t libvlc_media_player_get_state(
1178                                  libvlc_media_player_t *p_mi,
1179                                  libvlc_exception_t *p_e )
1180 {
1181     input_thread_t *p_input_thread;
1182     libvlc_state_t state = libvlc_Ended;
1183     vlc_value_t val;
1184
1185     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1186     if( !p_input_thread )
1187     {
1188         /* We do return the right value, no need to throw an exception */
1189         if( libvlc_exception_raised( p_e ) )
1190             libvlc_exception_clear( p_e );
1191         return state;
1192     }
1193
1194     var_Get( p_input_thread, "state", &val );
1195     state = vlc_to_libvlc_state(val.i_int);
1196
1197     if( state == libvlc_Playing )
1198     {
1199         float caching;
1200         caching = var_GetFloat( p_input_thread, "cache" );
1201         if( caching > 0.0 && caching < 1.0 )
1202             state = libvlc_Buffering;
1203     }
1204     vlc_object_release( p_input_thread );
1205     return state;
1206 }
1207
1208 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1209                                        libvlc_exception_t *p_e )
1210 {
1211     input_thread_t *p_input_thread;
1212     vlc_value_t val;
1213
1214     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1215     if ( !p_input_thread )
1216     {
1217         /* We do return the right value, no need to throw an exception */
1218         if( libvlc_exception_raised( p_e ) )
1219             libvlc_exception_clear( p_e );
1220         return false;
1221     }
1222     var_Get( p_input_thread, "can-seek", &val );
1223     vlc_object_release( p_input_thread );
1224
1225     return val.b_bool;
1226 }
1227
1228 /* internal function, used by audio, video */
1229 libvlc_track_description_t *
1230         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1231                                       const char *psz_variable,
1232                                       libvlc_exception_t *p_e )
1233 {
1234     input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
1235
1236     if( !p_input )
1237         return NULL;
1238
1239     vlc_value_t val_list, text_list;
1240     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1241
1242     if( val_list.p_list->i_count <= 0 ) /* no tracks */
1243         return NULL;
1244
1245     libvlc_track_description_t *p_track_description, *p_actual, *p_previous;
1246     p_track_description = ( libvlc_track_description_t * )
1247         malloc( sizeof( libvlc_track_description_t ) );
1248     if ( !p_track_description )
1249     {
1250         var_Change( p_input, psz_variable, VLC_VAR_FREELIST,
1251                     &val_list, &text_list);
1252         vlc_object_release( p_input );
1253         libvlc_exception_raise( p_e, "no enough memory" );
1254         return NULL;
1255     }
1256     p_actual = p_track_description;
1257     p_previous = NULL;
1258     for( int i = 0; i < val_list.p_list->i_count; i++ )
1259     {
1260         if( !p_actual )
1261         {
1262             p_actual = ( libvlc_track_description_t * )
1263                 malloc( sizeof( libvlc_track_description_t ) );
1264             if ( !p_actual )
1265             {
1266                 libvlc_track_description_release( p_track_description );
1267                 var_Change( p_input, psz_variable, VLC_VAR_FREELIST,
1268                             &val_list, &text_list);
1269                 vlc_object_release( p_input );
1270                 libvlc_exception_raise( p_e, "no enough memory" );
1271                 return NULL;
1272             }
1273         }
1274         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1275         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1276         p_actual->p_next = NULL;
1277         if( p_previous )
1278             p_previous->p_next = p_actual;
1279         p_previous = p_actual;
1280         p_actual =  NULL;
1281     }
1282     var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1283     vlc_object_release( p_input );
1284
1285     return p_track_description;
1286 }
1287
1288 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1289 {
1290     libvlc_track_description_t *p_actual, *p_before;
1291     p_actual = p_td;
1292
1293     while ( p_actual )
1294     {
1295         free( p_actual->psz_name );
1296         p_before = p_actual;
1297         p_actual = p_before->p_next;
1298         free( p_before );
1299     }
1300 }
1301
1302 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1303                                      libvlc_exception_t *p_e )
1304 {
1305     input_thread_t *p_input_thread;
1306     vlc_value_t val;
1307
1308     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1309     if ( !p_input_thread )
1310     {
1311         /* We do return the right value, no need to throw an exception */
1312         if( libvlc_exception_raised( p_e ) )
1313             libvlc_exception_clear( p_e );
1314         return false;
1315     }
1316     var_Get( p_input_thread, "can-pause", &val );
1317     vlc_object_release( p_input_thread );
1318
1319     return val.b_bool;
1320 }