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