]> git.sesse.net Git - vlc/blob - src/control/media_player.c
Revert "Remove enums from public APIs"
[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 const libvlc_state_t vlc_to_libvlc_state_array[] =
61 {
62     [INIT_S]        = libvlc_NothingSpecial,
63     [OPENING_S]     = libvlc_Opening,
64     [PLAYING_S]     = libvlc_Playing,
65     [PAUSE_S]       = libvlc_Paused,
66     [END_S]         = libvlc_Ended,
67     [ERROR_S]       = libvlc_Error,
68 };
69
70 static inline libvlc_state_t vlc_to_libvlc_state( int vlc_state )
71 {
72     if( vlc_state < 0 || vlc_state > 6 )
73         return libvlc_Ended;
74
75     return vlc_to_libvlc_state_array[vlc_state];
76 }
77
78 /*
79  * Release the associated input thread.
80  *
81  * Object lock is NOT held.
82  */
83 static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abort )
84 {
85     input_thread_t * p_input_thread;
86
87     if( !p_mi || !p_mi->p_input_thread )
88         return;
89
90     p_input_thread = p_mi->p_input_thread;
91
92     var_DelCallback( p_input_thread, "can-seek",
93                      input_seekable_changed, p_mi );
94     var_DelCallback( p_input_thread, "can-pause",
95                     input_pausable_changed, p_mi );
96     var_DelCallback( p_input_thread, "intf-event",
97                      input_event_changed, p_mi );
98
99     /* We owned this one */
100     input_Stop( p_input_thread, b_input_abort );
101     vlc_thread_join( p_input_thread );
102
103     var_Destroy( p_input_thread, "drawable-hwnd" );
104     var_Destroy( p_input_thread, "drawable-xid" );
105     var_Destroy( p_input_thread, "drawable-agl" );
106
107     vlc_object_release( p_input_thread );
108
109     p_mi->p_input_thread = NULL;
110 }
111
112 /*
113  * Retrieve the input thread. Be sure to release the object
114  * once you are done with it. (libvlc Internal)
115  *
116  * Function will lock the object.
117  */
118 input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi,
119                                          libvlc_exception_t *p_e )
120 {
121     input_thread_t *p_input_thread;
122
123     if( !p_mi ) RAISENULL( "Media Instance is NULL" );
124
125     vlc_mutex_lock( &p_mi->object_lock );
126
127     if( !p_mi->p_input_thread )
128     {
129         vlc_mutex_unlock( &p_mi->object_lock );
130         RAISENULL( "Input is NULL" );
131     }
132
133     p_input_thread = p_mi->p_input_thread;
134     vlc_object_hold( p_input_thread );
135
136     vlc_mutex_unlock( &p_mi->object_lock );
137
138     return p_input_thread;
139 }
140
141 static int
142 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
143                         vlc_value_t oldval, vlc_value_t newval,
144                         void * p_userdata )
145 {
146     VLC_UNUSED(oldval);
147     VLC_UNUSED(p_this);
148     VLC_UNUSED(psz_cmd);
149     libvlc_media_player_t * p_mi = p_userdata;
150     libvlc_event_t event;
151
152     libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
153     event.type = libvlc_MediaPlayerSeekableChanged;
154     event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
155
156     libvlc_event_send( p_mi->p_event_manager, &event );
157     return VLC_SUCCESS;
158 }
159
160 static int
161 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
162                         vlc_value_t oldval, vlc_value_t newval,
163                         void * p_userdata )
164 {
165     VLC_UNUSED(oldval);
166     VLC_UNUSED(p_this);
167     VLC_UNUSED(psz_cmd);
168     libvlc_media_player_t * p_mi = p_userdata;
169     libvlc_event_t event;
170
171     libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
172     event.type = libvlc_MediaPlayerPausableChanged;
173     event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
174
175     libvlc_event_send( p_mi->p_event_manager, &event );
176     return VLC_SUCCESS;
177 }
178
179 static int
180 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
181                      vlc_value_t oldval, vlc_value_t newval,
182                      void * p_userdata )
183 {
184     VLC_UNUSED(oldval);
185     input_thread_t * p_input = (input_thread_t *)p_this;
186     libvlc_media_player_t * p_mi = p_userdata;
187     libvlc_event_t event;
188
189     assert( !strcmp( psz_cmd, "intf-event" ) );
190
191     if( newval.i_int == INPUT_EVENT_STATE )
192     {
193         libvlc_state_t libvlc_state;
194
195         switch ( var_GetInteger( p_input, "state" ) )
196         {
197             case INIT_S:
198                 libvlc_state = libvlc_NothingSpecial;
199                 event.type = libvlc_MediaPlayerNothingSpecial;
200                 break;
201             case OPENING_S:
202                 libvlc_state = libvlc_Opening;
203                 event.type = libvlc_MediaPlayerOpening;
204                 break;
205             case PLAYING_S:
206                 libvlc_state = libvlc_Playing;
207                 event.type = libvlc_MediaPlayerPlaying;
208                 break;
209             case PAUSE_S:
210                 libvlc_state = libvlc_Paused;
211                 event.type = libvlc_MediaPlayerPaused;
212                 break;
213             case END_S:
214                 libvlc_state = libvlc_Ended;
215                 event.type = libvlc_MediaPlayerEndReached;
216                 break;
217             case ERROR_S:
218                 libvlc_state = libvlc_Error;
219                 event.type = libvlc_MediaPlayerEncounteredError;
220                 break;
221
222             default:
223                 return VLC_SUCCESS;
224         }
225
226         libvlc_media_set_state( p_mi->p_md, libvlc_state, NULL );
227         libvlc_event_send( p_mi->p_event_manager, &event );
228     }
229     else if( newval.i_int == INPUT_EVENT_POSITION )
230     {
231         if( var_GetInteger( p_input, "state" ) != PLAYING_S )
232             return VLC_SUCCESS; /* Don't send the position while stopped */
233
234         /* */
235         event.type = libvlc_MediaPlayerPositionChanged;
236         event.u.media_player_position_changed.new_position =
237                                           var_GetFloat( p_input, "position" );
238         libvlc_event_send( p_mi->p_event_manager, &event );
239
240         /* */
241         event.type = libvlc_MediaPlayerTimeChanged;
242         event.u.media_player_time_changed.new_time =
243                                                var_GetTime( p_input, "time" );
244         libvlc_event_send( p_mi->p_event_manager, &event );
245     }
246     else if( newval.i_int == INPUT_EVENT_LENGTH )
247     {
248         event.type = libvlc_MediaPlayerLengthChanged;
249         event.u.media_player_length_changed.new_length =
250                                                var_GetTime( p_input, "length" );
251         libvlc_event_send( p_mi->p_event_manager, &event );
252     }
253
254     return VLC_SUCCESS;
255
256 }
257
258 static void libvlc_media_player_destroy( libvlc_media_player_t * );
259
260 /**************************************************************************
261  * Create a Media Instance object.
262  *
263  * Refcount strategy:
264  * - All items created by _new start with a refcount set to 1.
265  * - Accessor _release decrease the refcount by 1, if after that
266  *   operation the refcount is 0, the object is destroyed.
267  * - Accessor _retain increase the refcount by 1 (XXX: to implement)
268  *
269  * Object locking strategy:
270  * - No lock held while in constructor.
271  * - When accessing any member variable this lock is held. (XXX who locks?)
272  * - When attempting to destroy the object the lock is also held.
273  **************************************************************************/
274 libvlc_media_player_t *
275 libvlc_media_player_new( libvlc_instance_t * p_libvlc_instance,
276                            libvlc_exception_t * p_e )
277 {
278     libvlc_media_player_t * p_mi;
279
280     if( !p_libvlc_instance )
281     {
282         libvlc_exception_raise( p_e, "invalid libvlc instance" );
283         return NULL;
284     }
285
286     p_mi = malloc( sizeof(libvlc_media_player_t) );
287     if( !p_mi )
288     {
289         libvlc_exception_raise( p_e, "not enough memory" );
290         return NULL;
291     }
292     p_mi->p_md = NULL;
293     p_mi->drawable.agl = 0;
294     p_mi->drawable.xid = 0;
295     p_mi->drawable.hwnd = NULL;
296     p_mi->drawable.nsobject = NULL;
297     p_mi->p_libvlc_instance = p_libvlc_instance;
298     p_mi->p_input_thread = NULL;
299     p_mi->i_refcount = 1;
300     vlc_mutex_init( &p_mi->object_lock );
301     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
302             p_libvlc_instance, p_e );
303     if( libvlc_exception_raised( p_e ) )
304     {
305         vlc_mutex_destroy( &p_mi->object_lock );
306         free( p_mi );
307         return NULL;
308     }
309
310     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
311             libvlc_MediaPlayerNothingSpecial, p_e );
312     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
313             libvlc_MediaPlayerOpening, p_e );
314     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
315             libvlc_MediaPlayerBuffering, p_e );
316     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
317             libvlc_MediaPlayerPlaying, p_e );
318     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
319             libvlc_MediaPlayerPaused, p_e );
320     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
321             libvlc_MediaPlayerStopped, p_e );
322     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
323             libvlc_MediaPlayerForward, p_e );
324     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
325             libvlc_MediaPlayerBackward, p_e );
326     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
327             libvlc_MediaPlayerEndReached, p_e );
328     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
329             libvlc_MediaPlayerEncounteredError, p_e );
330
331     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
332             libvlc_MediaPlayerPositionChanged, p_e );
333     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
334             libvlc_MediaPlayerTimeChanged, p_e );
335     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
336             libvlc_MediaPlayerLengthChanged, p_e );
337     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
338             libvlc_MediaPlayerTitleChanged, p_e );
339     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
340             libvlc_MediaPlayerSeekableChanged, p_e );
341     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
342             libvlc_MediaPlayerPausableChanged, p_e );
343
344     /* Snapshot initialization */
345     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
346            libvlc_MediaPlayerSnapshotTaken, p_e );
347
348     /* Attach a var callback to the global object to provide the glue between
349         vout_thread that generates the event and media_player that re-emits it
350         with its own event manager
351     */
352     var_Create( p_libvlc_instance->p_libvlc_int, "vout-snapshottaken",
353                 VLC_VAR_STRING | VLC_VAR_ISCOMMAND );
354     var_AddCallback( p_libvlc_instance->p_libvlc_int, "vout-snapshottaken",
355                      SnapshotTakenCallback, p_mi );
356
357     return p_mi;
358 }
359
360 /**************************************************************************
361  * Create a Media Instance object with a media descriptor.
362  **************************************************************************/
363 libvlc_media_player_t *
364 libvlc_media_player_new_from_media(
365                                     libvlc_media_t * p_md,
366                                     libvlc_exception_t *p_e )
367 {
368     libvlc_media_player_t * p_mi;
369
370     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance, p_e );
371     if( !p_mi )
372         return NULL;
373
374     libvlc_media_retain( p_md );
375     p_mi->p_md = p_md;
376
377     return p_mi;
378 }
379
380 /**************************************************************************
381  * Destroy a Media Instance object (libvlc internal)
382  *
383  * Warning: No lock held here, but hey, this is internal. Caller must lock.
384  **************************************************************************/
385 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
386 {
387     input_thread_t *p_input_thread;
388     libvlc_exception_t p_e;
389
390     assert( p_mi );
391
392     /* Detach Callback from the main libvlc object */
393     var_DelCallback( p_mi->p_libvlc_instance->p_libvlc_int,
394                      "vout-snapshottaken", SnapshotTakenCallback, p_mi );
395
396     libvlc_exception_init( &p_e );
397     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
398
399     if( libvlc_exception_raised( &p_e ) )
400         /* no need to worry about no input thread */
401         libvlc_exception_clear( &p_e );
402     else
403         release_input_thread( p_mi, true );
404
405     libvlc_event_manager_release( p_mi->p_event_manager );
406     libvlc_media_release( p_mi->p_md );
407     vlc_mutex_destroy( &p_mi->object_lock );
408     free( p_mi );
409 }
410
411 /**************************************************************************
412  * Release a Media Instance object.
413  *
414  * Function does the locking.
415  **************************************************************************/
416 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
417 {
418     bool destroy;
419
420     assert( p_mi );
421     vlc_mutex_lock( &p_mi->object_lock );
422     destroy = !--p_mi->i_refcount;
423     vlc_mutex_unlock( &p_mi->object_lock );
424
425     if( destroy )
426         libvlc_media_player_destroy( p_mi );
427 }
428
429 /**************************************************************************
430  * Retain a Media Instance object.
431  *
432  * Caller must hold the lock.
433  **************************************************************************/
434 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
435 {
436     assert( p_mi );
437
438     vlc_mutex_lock( &p_mi->object_lock );
439     p_mi->i_refcount++;
440     vlc_mutex_unlock( &p_mi->object_lock );
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 void * 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     libvlc_track_description_t *p_track_description = NULL,
1183                                *p_actual, *p_previous;
1184
1185     if( !p_input )
1186         return NULL;
1187
1188     vlc_value_t val_list, text_list;
1189     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1190
1191     /* no tracks */
1192     if( val_list.p_list->i_count <= 0 )
1193         goto end;
1194
1195     p_track_description = ( libvlc_track_description_t * )
1196         malloc( sizeof( libvlc_track_description_t ) );
1197     if ( !p_track_description )
1198     {
1199         libvlc_exception_raise( p_e, "not enough memory" );
1200         goto end;
1201     }
1202     p_actual = p_track_description;
1203     p_previous = NULL;
1204     for( int i = 0; i < val_list.p_list->i_count; i++ )
1205     {
1206         if( !p_actual )
1207         {
1208             p_actual = ( libvlc_track_description_t * )
1209                 malloc( sizeof( libvlc_track_description_t ) );
1210             if ( !p_actual )
1211             {
1212                 libvlc_track_description_release( p_track_description );
1213                 libvlc_exception_raise( p_e, "not enough memory" );
1214                 goto end;
1215             }
1216         }
1217         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1218         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1219         p_actual->p_next = NULL;
1220         if( p_previous )
1221             p_previous->p_next = p_actual;
1222         p_previous = p_actual;
1223         p_actual =  NULL;
1224     }
1225
1226 end:
1227     var_FreeList( &val_list, &text_list );
1228     vlc_object_release( p_input );
1229
1230     return p_track_description;
1231 }
1232
1233 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1234 {
1235     libvlc_track_description_t *p_actual, *p_before;
1236     p_actual = p_td;
1237
1238     while ( p_actual )
1239     {
1240         free( p_actual->psz_name );
1241         p_before = p_actual;
1242         p_actual = p_before->p_next;
1243         free( p_before );
1244     }
1245 }
1246
1247 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1248                                      libvlc_exception_t *p_e )
1249 {
1250     input_thread_t *p_input_thread;
1251     vlc_value_t val;
1252
1253     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1254     if ( !p_input_thread )
1255     {
1256         /* We do return the right value, no need to throw an exception */
1257         if( libvlc_exception_raised( p_e ) )
1258             libvlc_exception_clear( p_e );
1259         return false;
1260     }
1261     var_Get( p_input_thread, "can-pause", &val );
1262     vlc_object_release( p_input_thread );
1263
1264     return val.b_bool;
1265 }