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