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