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