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