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