]> git.sesse.net Git - vlc/blob - src/control/media_player.c
Removed obsolete BUFFERING_S input state.
[vlc] / src / control / media_player.c
1 /*****************************************************************************
2  * media_player.c: Libvlc API Media Instance management functions
3  *****************************************************************************
4  * Copyright (C) 2005 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
32 static void
33 input_state_changed( const vlc_event_t * event, void * p_userdata );
34
35 static int
36 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
37                         vlc_value_t oldval, vlc_value_t newval,
38                         void * p_userdata );
39 static int
40 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
41                         vlc_value_t oldval, vlc_value_t newval,
42                         void * p_userdata );
43 static int
44 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
45                      vlc_value_t oldval, vlc_value_t newval,
46                      void * p_userdata );
47
48 static const libvlc_state_t vlc_to_libvlc_state_array[] =
49 {
50     [INIT_S]        = libvlc_NothingSpecial,
51     [OPENING_S]     = libvlc_Opening,
52     [PLAYING_S]     = libvlc_Playing,
53     [PAUSE_S]       = libvlc_Paused,
54     [END_S]         = libvlc_Ended,
55     [ERROR_S]       = libvlc_Error,
56 };
57
58 static inline libvlc_state_t vlc_to_libvlc_state( int vlc_state )
59 {
60     if( vlc_state < 0 || vlc_state > 6 )
61         return libvlc_Ended;
62
63     return vlc_to_libvlc_state_array[vlc_state];
64 }
65
66 /*
67  * Release the associated input thread
68  *
69  * Object lock is NOT held.
70  */
71 static void release_input_thread( libvlc_media_player_t *p_mi )
72 {
73     input_thread_t * p_input_thread;
74
75     if( !p_mi || !p_mi->p_input_thread )
76         return;
77
78     p_input_thread = p_mi->p_input_thread;
79
80     /* No one is tracking this input_thread appart us. Destroy it */
81     if( p_mi->b_own_its_input_thread )
82     {
83         vlc_event_manager_t * p_em = input_get_event_manager( p_input_thread );
84         vlc_event_detach( p_em, vlc_InputStateChanged, input_state_changed, p_mi );
85         var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
86         var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
87         var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
88
89         /* We owned this one */
90         input_StopThread( p_input_thread );
91         vlc_thread_join( p_input_thread );
92
93         var_Destroy( p_input_thread, "drawable" );
94     }
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  * Object lock is held.
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 /*
131  * input_state_changed (Private) (vlc_InputStateChanged callback)
132  */
133 static void
134 input_state_changed( const vlc_event_t * event, void * p_userdata )
135 {
136     libvlc_media_player_t * p_mi = p_userdata;
137     libvlc_event_t forwarded_event;
138     libvlc_event_type_t type = event->u.input_state_changed.new_state;
139
140     switch ( type )
141     {
142         case INIT_S:
143             libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
144             forwarded_event.type = libvlc_MediaPlayerNothingSpecial;
145             break;
146         case OPENING_S:
147             libvlc_media_set_state( p_mi->p_md, libvlc_Opening, NULL);
148             forwarded_event.type = libvlc_MediaPlayerOpening;
149             break;
150         case PLAYING_S:
151             libvlc_media_set_state( p_mi->p_md, libvlc_Playing, NULL);
152             forwarded_event.type = libvlc_MediaPlayerPlaying;
153             break;
154         case PAUSE_S:
155             libvlc_media_set_state( p_mi->p_md, libvlc_Paused, NULL);
156             forwarded_event.type = libvlc_MediaPlayerPaused;
157             break;
158         case END_S:
159             libvlc_media_set_state( p_mi->p_md, libvlc_Ended, NULL);
160             forwarded_event.type = libvlc_MediaPlayerEndReached;
161             break;
162         case ERROR_S:
163             libvlc_media_set_state( p_mi->p_md, libvlc_Error, NULL);
164             forwarded_event.type = libvlc_MediaPlayerEncounteredError;
165             break;
166
167         default:
168             return;
169     }
170
171     libvlc_event_send( p_mi->p_event_manager, &forwarded_event );
172     return;
173 }
174
175 static int
176 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
177                         vlc_value_t oldval, vlc_value_t newval,
178                         void * p_userdata )
179 {
180     VLC_UNUSED(oldval);
181     VLC_UNUSED(p_this);
182     VLC_UNUSED(psz_cmd);
183     libvlc_media_player_t * p_mi = p_userdata;
184     libvlc_event_t event;
185
186     libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
187     event.type = libvlc_MediaPlayerSeekableChanged;
188     event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
189
190     libvlc_event_send( p_mi->p_event_manager, &event );
191     return VLC_SUCCESS;
192 }
193
194 static int
195 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
196                         vlc_value_t oldval, vlc_value_t newval,
197                         void * p_userdata )
198 {
199     VLC_UNUSED(oldval);
200     VLC_UNUSED(p_this);
201     VLC_UNUSED(psz_cmd);
202     libvlc_media_player_t * p_mi = p_userdata;
203     libvlc_event_t event;
204
205     libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
206     event.type = libvlc_MediaPlayerPausableChanged;
207     event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
208
209     libvlc_event_send( p_mi->p_event_manager, &event );
210     return VLC_SUCCESS;
211 }
212
213 static int
214 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
215                      vlc_value_t oldval, vlc_value_t newval,
216                      void * p_userdata )
217 {
218     VLC_UNUSED(oldval);
219     input_thread_t * p_input = (input_thread_t *)p_this;
220     libvlc_media_player_t * p_mi = p_userdata;
221     libvlc_event_t event;
222
223     if( newval.i_int != INPUT_EVENT_TIMES )
224         return VLC_EGENERIC;
225
226     assert( !strcmp(psz_cmd, "intf-event" ) );
227
228     if( var_GetInteger( p_input, "state" ) != PLAYING_S )
229         return VLC_SUCCESS; /* Don't send the position while stopped */
230
231     /* */
232     event.type = libvlc_MediaPlayerPositionChanged;
233     event.u.media_player_position_changed.new_position = var_GetFloat( p_input, "position" );;
234     libvlc_event_send( p_mi->p_event_manager, &event );
235
236     /* */
237     event.type = libvlc_MediaPlayerTimeChanged;
238     event.u.media_player_time_changed.new_time = var_GetTime( p_input, "time" );
239     libvlc_event_send( p_mi->p_event_manager, &event );
240
241     return VLC_SUCCESS;
242
243 }
244
245
246 /**************************************************************************
247  * Create a Media Instance object
248  **************************************************************************/
249 libvlc_media_player_t *
250 libvlc_media_player_new( libvlc_instance_t * p_libvlc_instance,
251                            libvlc_exception_t * p_e )
252 {
253     libvlc_media_player_t * p_mi;
254
255     if( !p_libvlc_instance )
256     {
257         libvlc_exception_raise( p_e, "invalid libvlc instance" );
258         return NULL;
259     }
260
261     p_mi = malloc( sizeof(libvlc_media_player_t) );
262     if( !p_mi )
263     {
264         libvlc_exception_raise( p_e, "Not enough memory" );
265         return NULL;
266     }
267     p_mi->p_md = NULL;
268     p_mi->drawable = 0;
269     p_mi->p_libvlc_instance = p_libvlc_instance;
270     p_mi->p_input_thread = NULL;
271     /* refcount strategy:
272      * - All items created by _new start with a refcount set to 1
273      * - Accessor _release decrease the refcount by 1, if after that
274      *   operation the refcount is 0, the object is destroyed.
275      * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
276     p_mi->i_refcount = 1;
277     p_mi->b_own_its_input_thread = true;
278     /* object_lock strategy:
279      * - No lock held in constructor
280      * - Lock when accessing all variable this lock is held
281      * - Lock when attempting to destroy the object the lock is also held */
282     vlc_mutex_init( &p_mi->object_lock );
283     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
284             p_libvlc_instance, p_e );
285     if( libvlc_exception_raised( p_e ) )
286     {
287         free( p_mi );
288         return NULL;
289     }
290
291     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
292             libvlc_MediaPlayerNothingSpecial, p_e );
293     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
294             libvlc_MediaPlayerOpening, p_e );
295     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
296             libvlc_MediaPlayerBuffering, p_e );
297     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
298             libvlc_MediaPlayerPlaying, p_e );
299     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
300             libvlc_MediaPlayerPaused, p_e );
301     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
302             libvlc_MediaPlayerStopped, p_e );
303     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
304             libvlc_MediaPlayerForward, p_e );
305     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
306             libvlc_MediaPlayerBackward, p_e );
307     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
308             libvlc_MediaPlayerEndReached, p_e );
309     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
310             libvlc_MediaPlayerEncounteredError, p_e );
311
312     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
313             libvlc_MediaPlayerPositionChanged, p_e );
314     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
315             libvlc_MediaPlayerTimeChanged, p_e );
316      libvlc_event_manager_register_event_type( p_mi->p_event_manager,
317             libvlc_MediaPlayerTitleChanged, p_e );
318     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
319             libvlc_MediaPlayerSeekableChanged, p_e );
320     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
321             libvlc_MediaPlayerPausableChanged, p_e );
322
323     return p_mi;
324 }
325
326 /**************************************************************************
327  * Create a Media Instance object with a media descriptor
328  **************************************************************************/
329 libvlc_media_player_t *
330 libvlc_media_player_new_from_media(
331                                     libvlc_media_t * p_md,
332                                     libvlc_exception_t *p_e )
333 {
334     libvlc_media_player_t * p_mi;
335     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance, p_e );
336
337     if( !p_mi )
338         return NULL;
339
340     libvlc_media_retain( p_md );
341     p_mi->p_md = p_md;
342
343     return p_mi;
344 }
345
346 /**************************************************************************
347  * Create a new media instance object from an input_thread (Libvlc Internal)
348  **************************************************************************/
349 libvlc_media_player_t * libvlc_media_player_new_from_input_thread(
350                                    struct libvlc_instance_t *p_libvlc_instance,
351                                    input_thread_t *p_input,
352                                    libvlc_exception_t *p_e )
353 {
354     libvlc_media_player_t * p_mi;
355
356     if( !p_input )
357     {
358         libvlc_exception_raise( p_e, "invalid input thread" );
359         return NULL;
360     }
361
362     p_mi = libvlc_media_player_new( p_libvlc_instance, p_e );
363
364     if( !p_mi )
365         return NULL;
366
367     p_mi->p_md = libvlc_media_new_from_input_item(
368                     p_libvlc_instance,
369                     input_GetItem( p_input ), p_e );
370
371     if( !p_mi->p_md )
372     {
373         libvlc_media_player_destroy( p_mi );
374         return NULL;
375     }
376
377     /* will be released in media_player_release() */
378     vlc_object_hold( p_input );
379
380     p_mi->p_input_thread = p_input;
381     p_mi->b_own_its_input_thread = false;
382
383     return p_mi;
384 }
385
386 /**************************************************************************
387  * Destroy a Media Instance object (libvlc internal)
388  *
389  * Warning: No lock held here, but hey, this is internal.
390  **************************************************************************/
391 void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
392 {
393     input_thread_t *p_input_thread;
394     libvlc_exception_t p_e;
395
396     libvlc_exception_init( &p_e );
397
398     if( !p_mi )
399         return;
400
401     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
402
403     if( libvlc_exception_raised( &p_e ) )
404     {
405         libvlc_event_manager_release( p_mi->p_event_manager );
406         libvlc_exception_clear( &p_e );
407         free( p_mi );
408         return; /* no need to worry about no input thread */
409     }
410     vlc_mutex_destroy( &p_mi->object_lock );
411
412     vlc_object_release( p_input_thread );
413
414     libvlc_media_release( p_mi->p_md );
415
416     free( p_mi );
417 }
418
419 /**************************************************************************
420  * Release a Media Instance object
421  **************************************************************************/
422 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
423 {
424     if( !p_mi )
425         return;
426
427     vlc_mutex_lock( &p_mi->object_lock );
428
429     p_mi->i_refcount--;
430
431     if( p_mi->i_refcount > 0 )
432     {
433         vlc_mutex_unlock( &p_mi->object_lock );
434         return;
435     }
436     vlc_mutex_unlock( &p_mi->object_lock );
437     vlc_mutex_destroy( &p_mi->object_lock );
438
439     release_input_thread( p_mi );
440
441     libvlc_event_manager_release( p_mi->p_event_manager );
442
443     libvlc_media_release( p_mi->p_md );
444
445     free( p_mi );
446 }
447
448 /**************************************************************************
449  * Retain a Media Instance object
450  **************************************************************************/
451 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
452 {
453     if( !p_mi )
454         return;
455
456     p_mi->i_refcount++;
457 }
458
459 /**************************************************************************
460  * Set the Media descriptor associated with the instance
461  **************************************************************************/
462 void libvlc_media_player_set_media(
463                             libvlc_media_player_t *p_mi,
464                             libvlc_media_t *p_md,
465                             libvlc_exception_t *p_e )
466 {
467     VLC_UNUSED(p_e);
468
469     if( !p_mi )
470         return;
471
472     vlc_mutex_lock( &p_mi->object_lock );
473
474     release_input_thread( p_mi );
475
476     if( p_mi->p_md )
477         libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial, p_e );
478
479     libvlc_media_release( p_mi->p_md );
480
481     if( !p_md )
482     {
483         p_mi->p_md = NULL;
484         vlc_mutex_unlock( &p_mi->object_lock );
485         return; /* It is ok to pass a NULL md */
486     }
487
488     libvlc_media_retain( p_md );
489     p_mi->p_md = p_md;
490
491     /* The policy here is to ignore that we were created using a different
492      * libvlc_instance, because we don't really care */
493     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
494
495     vlc_mutex_unlock( &p_mi->object_lock );
496 }
497
498 /**************************************************************************
499  * Get the Media descriptor associated with the instance
500  **************************************************************************/
501 libvlc_media_t *
502 libvlc_media_player_get_media(
503                             libvlc_media_player_t *p_mi,
504                             libvlc_exception_t *p_e )
505 {
506     VLC_UNUSED(p_e);
507
508     if( !p_mi->p_md )
509         return NULL;
510
511     libvlc_media_retain( p_mi->p_md );
512     return p_mi->p_md;
513 }
514
515 /**************************************************************************
516  * Get the event Manager
517  **************************************************************************/
518 libvlc_event_manager_t *
519 libvlc_media_player_event_manager(
520                             libvlc_media_player_t *p_mi,
521                             libvlc_exception_t *p_e )
522 {
523     VLC_UNUSED(p_e);
524
525     return p_mi->p_event_manager;
526 }
527
528 /**************************************************************************
529  * Play
530  **************************************************************************/
531 void libvlc_media_player_play( libvlc_media_player_t *p_mi,
532                                  libvlc_exception_t *p_e )
533 {
534     input_thread_t * p_input_thread;
535
536     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
537     {
538         /* A thread already exists, send it a play message */
539         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
540         vlc_object_release( p_input_thread );
541         return;
542     }
543
544     /* Ignore previous exception */
545     libvlc_exception_clear( p_e );
546
547     vlc_mutex_lock( &p_mi->object_lock );
548
549     if( !p_mi->p_md )
550     {
551         libvlc_exception_raise( p_e, "no associated media descriptor" );
552         vlc_mutex_unlock( &p_mi->object_lock );
553         return;
554     }
555
556     p_mi->p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
557                       p_mi->p_md->p_input_item );
558
559
560     if( !p_mi->p_input_thread )
561     {
562         vlc_mutex_unlock( &p_mi->object_lock );
563         return;
564     }
565
566     p_input_thread = p_mi->p_input_thread;
567
568     if( p_mi->drawable )
569     {
570         vlc_value_t val;
571         val.i_int = p_mi->drawable;
572         var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
573         var_Set( p_input_thread, "drawable", val );
574     }
575
576     vlc_event_manager_t * p_em = input_get_event_manager( p_input_thread );
577     vlc_event_attach( p_em, vlc_InputStateChanged, input_state_changed, p_mi );
578
579     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
580     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
581     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
582
583     vlc_mutex_unlock( &p_mi->object_lock );
584 }
585
586 /**************************************************************************
587  * Pause
588  **************************************************************************/
589 void libvlc_media_player_pause( libvlc_media_player_t *p_mi,
590                                   libvlc_exception_t *p_e )
591 {
592     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
593
594     if( !p_input_thread )
595         return;
596
597     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
598
599     if( state == libvlc_Playing )
600     {
601         if( libvlc_media_player_can_pause( p_mi, p_e ) )
602             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
603         else
604             libvlc_media_player_stop( p_mi, p_e );
605     }
606     else
607         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
608
609     vlc_object_release( p_input_thread );
610 }
611
612 /**************************************************************************
613  * Stop
614  **************************************************************************/
615 void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
616                                  libvlc_exception_t *p_e )
617 {
618     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
619
620     if( state == libvlc_Playing || state == libvlc_Paused )
621     {
622         /* Send a stop notification event only of we are in playing or paused states */
623         libvlc_media_set_state( p_mi->p_md, libvlc_Ended, p_e );
624
625         /* Construct and send the event */
626         libvlc_event_t event;
627         event.type = libvlc_MediaPlayerEndReached;
628         libvlc_event_send( p_mi->p_event_manager, &event );
629     }
630
631     if( p_mi->b_own_its_input_thread )
632     {
633         vlc_mutex_lock( &p_mi->object_lock );
634         release_input_thread( p_mi ); /* This will stop the input thread */
635         vlc_mutex_unlock( &p_mi->object_lock );
636     }
637     else
638     {
639         input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
640
641         if( !p_input_thread )
642             return;
643
644         input_StopThread( p_input_thread );
645         vlc_object_release( p_input_thread );
646     }
647 }
648
649 /**************************************************************************
650  * Set Drawable
651  **************************************************************************/
652 void libvlc_media_player_set_drawable( libvlc_media_player_t *p_mi,
653                                        libvlc_drawable_t drawable,
654                                        libvlc_exception_t *p_e )
655 {
656     input_thread_t *p_input_thread;
657     vout_thread_t *p_vout = NULL;
658
659     p_mi->drawable = drawable;
660
661     /* Allow on the fly drawable changing. This is tricky has this may
662      * not be supported by every vout. We though can't disable it
663      * because of some creepy drawable type that are not flexible enough
664      * (Win32 HWND for instance) */
665     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
666     if( !p_input_thread ) {
667         /* No input, nothing more to do, we are fine */
668         libvlc_exception_clear( p_e );
669         return;
670     }
671
672     p_vout = vlc_object_find( p_input_thread, VLC_OBJECT_VOUT, FIND_CHILD );
673     if( p_vout )
674     {
675         vout_Control( p_vout , VOUT_REPARENT, drawable);
676         vlc_object_release( p_vout );
677     }
678     vlc_object_release( p_input_thread );
679 }
680
681 /**************************************************************************
682  * Get Drawable
683  **************************************************************************/
684 libvlc_drawable_t
685 libvlc_media_player_get_drawable ( libvlc_media_player_t *p_mi, libvlc_exception_t *p_e )
686 {
687     VLC_UNUSED(p_e);
688     return p_mi->drawable;
689 }
690
691 /**************************************************************************
692  * Getters for stream information
693  **************************************************************************/
694 libvlc_time_t libvlc_media_player_get_length(
695                              libvlc_media_player_t *p_mi,
696                              libvlc_exception_t *p_e )
697 {
698     input_thread_t *p_input_thread;
699     vlc_value_t val;
700
701     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
702     if( !p_input_thread )
703         return -1;
704
705     var_Get( p_input_thread, "length", &val );
706     vlc_object_release( p_input_thread );
707
708     return (val.i_time+500LL)/1000LL;
709 }
710
711 libvlc_time_t libvlc_media_player_get_time(
712                                    libvlc_media_player_t *p_mi,
713                                    libvlc_exception_t *p_e )
714 {
715     input_thread_t *p_input_thread;
716     vlc_value_t val;
717
718     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
719     if( !p_input_thread )
720         return -1;
721
722     var_Get( p_input_thread , "time", &val );
723     vlc_object_release( p_input_thread );
724     return (val.i_time+500LL)/1000LL;
725 }
726
727 void libvlc_media_player_set_time(
728                                  libvlc_media_player_t *p_mi,
729                                  libvlc_time_t time,
730                                  libvlc_exception_t *p_e )
731 {
732     input_thread_t *p_input_thread;
733     vlc_value_t value;
734
735     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
736     if( !p_input_thread )
737         return;
738
739     value.i_time = time*1000LL;
740     var_Set( p_input_thread, "time", value );
741     vlc_object_release( p_input_thread );
742 }
743
744 void libvlc_media_player_set_position(
745                                 libvlc_media_player_t *p_mi,
746                                 float position,
747                                 libvlc_exception_t *p_e )
748 {
749     input_thread_t *p_input_thread;
750     vlc_value_t val;
751     val.f_float = position;
752
753     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
754     if( !p_input_thread )
755         return;
756
757     var_Set( p_input_thread, "position", val );
758     vlc_object_release( p_input_thread );
759 }
760
761 float libvlc_media_player_get_position(
762                                  libvlc_media_player_t *p_mi,
763                                  libvlc_exception_t *p_e )
764 {
765     input_thread_t *p_input_thread;
766     vlc_value_t val;
767
768     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
769     if( !p_input_thread )
770         return -1.0;
771
772     var_Get( p_input_thread, "position", &val );
773     vlc_object_release( p_input_thread );
774
775     return val.f_float;
776 }
777
778 void libvlc_media_player_set_chapter(
779                                  libvlc_media_player_t *p_mi,
780                                  int chapter,
781                                  libvlc_exception_t *p_e )
782 {
783     input_thread_t *p_input_thread;
784     vlc_value_t val;
785     val.i_int = chapter;
786
787     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
788     if( !p_input_thread )
789         return;
790
791     var_Set( p_input_thread, "chapter", val );
792     vlc_object_release( p_input_thread );
793 }
794
795 int libvlc_media_player_get_chapter(
796                                  libvlc_media_player_t *p_mi,
797                                  libvlc_exception_t *p_e )
798 {
799     input_thread_t *p_input_thread;
800     vlc_value_t val;
801
802     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
803     if( !p_input_thread )
804         return -1;
805
806     var_Get( p_input_thread, "chapter", &val );
807     vlc_object_release( p_input_thread );
808
809     return val.i_int;
810 }
811
812 int libvlc_media_player_get_chapter_count(
813                                  libvlc_media_player_t *p_mi,
814                                  libvlc_exception_t *p_e )
815 {
816     input_thread_t *p_input_thread;
817     vlc_value_t val;
818
819     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
820     if( !p_input_thread )
821         return -1;
822
823     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
824     vlc_object_release( p_input_thread );
825
826     return val.i_int;
827 }
828
829 int libvlc_media_player_get_chapter_count_for_title(
830                                  libvlc_media_player_t *p_mi,
831                                  int i_title,
832                                  libvlc_exception_t *p_e )
833 {
834     input_thread_t *p_input_thread;
835     vlc_value_t val;
836
837     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
838     if( !p_input_thread )
839         return -1;
840
841     char *psz_name;
842     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
843     {
844         vlc_object_release( p_input_thread );
845         return -1;
846     }
847     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
848     vlc_object_release( p_input_thread );
849     free( psz_name );
850
851     return val.i_int;
852 }
853
854 void libvlc_media_player_set_title(
855                                  libvlc_media_player_t *p_mi,
856                                  int i_title,
857                                  libvlc_exception_t *p_e )
858 {
859     input_thread_t *p_input_thread;
860     vlc_value_t val;
861     val.i_int = i_title;
862
863     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
864     if( !p_input_thread )
865         return;
866
867     var_Set( p_input_thread, "title", val );
868     vlc_object_release( p_input_thread );
869
870     //send event
871     libvlc_event_t event;
872     event.type = libvlc_MediaPlayerTitleChanged;
873     event.u.media_player_title_changed.new_title = i_title;
874     libvlc_event_send( p_mi->p_event_manager, &event );
875 }
876
877 int libvlc_media_player_get_title(
878                                  libvlc_media_player_t *p_mi,
879                                  libvlc_exception_t *p_e )
880 {
881     input_thread_t *p_input_thread;
882     vlc_value_t val;
883
884     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
885     if( !p_input_thread )
886         return -1;
887
888     var_Get( p_input_thread, "title", &val );
889     vlc_object_release( p_input_thread );
890
891     return val.i_int;
892 }
893
894 int libvlc_media_player_get_title_count(
895                                  libvlc_media_player_t *p_mi,
896                                  libvlc_exception_t *p_e )
897 {
898     input_thread_t *p_input_thread;
899     vlc_value_t val;
900
901     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
902     if( !p_input_thread )
903         return -1;
904
905     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
906     vlc_object_release( p_input_thread );
907
908     return val.i_int;
909 }
910
911 void libvlc_media_player_next_chapter(
912                                  libvlc_media_player_t *p_mi,
913                                  libvlc_exception_t *p_e )
914 {
915     input_thread_t *p_input_thread;
916
917     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
918     if( !p_input_thread )
919         return;
920
921     int i_type = var_Type( p_input_thread, "next-chapter" );
922     vlc_value_t val;
923     val.b_bool = true;
924     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
925                             "next-chapter":"next-title", val );
926
927     vlc_object_release( p_input_thread );
928 }
929
930 void libvlc_media_player_previous_chapter(
931                                  libvlc_media_player_t *p_mi,
932                                  libvlc_exception_t *p_e )
933 {
934     input_thread_t *p_input_thread;
935
936     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
937     if( !p_input_thread )
938         return;
939
940     int i_type = var_Type( p_input_thread, "next-chapter" );
941     vlc_value_t val;
942     val.b_bool = true;
943     var_Set( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
944                             "prev-chapter":"prev-title", val );
945
946     vlc_object_release( p_input_thread );
947 }
948
949 float libvlc_media_player_get_fps(
950                                  libvlc_media_player_t *p_mi,
951                                  libvlc_exception_t *p_e)
952 {
953     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
954     double f_fps = 0.0;
955
956     if( p_input_thread )
957     {
958         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
959             f_fps = 0.0;
960         vlc_object_release( p_input_thread );
961     }
962     return f_fps;
963 }
964
965 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
966                                      libvlc_exception_t *p_e)
967 {
968     input_thread_t *p_input_thread =
969                             libvlc_get_input_thread ( p_mi, p_e);
970     if ( !p_input_thread )
971         return false;
972
973     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
974     {
975         vlc_object_release( p_input_thread );
976         return true;
977     }
978     vlc_object_release( p_input_thread );
979     return false;
980 }
981
982 void libvlc_media_player_set_rate(
983                                  libvlc_media_player_t *p_mi,
984                                  float rate,
985                                  libvlc_exception_t *p_e )
986 {
987     input_thread_t *p_input_thread;
988     vlc_value_t val;
989     bool b_can_rewind;
990
991     if( rate != 0 )
992         RAISEVOID( "Rate value is invalid" );
993
994     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
995     if( !p_input_thread )
996         return;
997
998     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
999     if( (rate < 0) && !b_can_rewind )
1000     {
1001         vlc_object_release( p_input_thread );
1002         libvlc_exception_raise( p_e, "Rate value is invalid" );
1003         return;
1004     }
1005
1006     val.i_int = 1000.0f/rate;
1007     var_Set( p_input_thread, "rate", val );
1008     vlc_object_release( p_input_thread );
1009 }
1010
1011 float libvlc_media_player_get_rate(
1012                                  libvlc_media_player_t *p_mi,
1013                                  libvlc_exception_t *p_e )
1014 {
1015     input_thread_t *p_input_thread;
1016     vlc_value_t val;
1017     bool b_can_rewind;
1018
1019     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1020     if( !p_input_thread )
1021         return 0.0;  /* rate < 0 indicates rewind */
1022
1023     var_Get( p_input_thread, "rate", &val );
1024     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1025     if( (val.i_int < 0) && !b_can_rewind )
1026     {
1027         libvlc_exception_raise( p_e, "invalid rate" );
1028         return 0.0;
1029     }
1030     vlc_object_release( p_input_thread );
1031
1032     return (float)1000.0f/val.i_int;
1033 }
1034
1035 libvlc_state_t libvlc_media_player_get_state(
1036                                  libvlc_media_player_t *p_mi,
1037                                  libvlc_exception_t *p_e )
1038 {
1039     input_thread_t *p_input_thread;
1040     vlc_value_t val;
1041
1042     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1043     if ( !p_input_thread )
1044     {
1045         /* We do return the right value, no need to throw an exception */
1046         if( libvlc_exception_raised( p_e ) )
1047             libvlc_exception_clear( p_e );
1048         return libvlc_Ended;
1049     }
1050
1051     var_Get( p_input_thread, "state", &val );
1052     vlc_object_release( p_input_thread );
1053
1054     return vlc_to_libvlc_state(val.i_int);
1055 }
1056
1057 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1058                                        libvlc_exception_t *p_e )
1059 {
1060     input_thread_t *p_input_thread;
1061     vlc_value_t val;
1062
1063     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1064     if ( !p_input_thread )
1065     {
1066         /* We do return the right value, no need to throw an exception */
1067         if( libvlc_exception_raised( p_e ) )
1068             libvlc_exception_clear( p_e );
1069         return false;
1070     }
1071     var_Get( p_input_thread, "can-seek", &val );
1072     vlc_object_release( p_input_thread );
1073
1074     return val.b_bool;
1075 }
1076
1077 /* internal function, used by audio, video */
1078 libvlc_track_description_t *
1079         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1080                                       const char *psz_variable,
1081                                       libvlc_exception_t *p_e )
1082 {
1083     input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
1084
1085     if( !p_input )
1086         return NULL;
1087
1088     vlc_value_t val_list, text_list;
1089     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1090
1091     if( val_list.p_list->i_count <= 0 ) /* no tracks */
1092         return NULL;
1093
1094     libvlc_track_description_t *p_track_description, *p_actual, *p_previous;
1095     p_track_description = ( libvlc_track_description_t * )
1096         malloc( sizeof( libvlc_track_description_t ) );
1097     if ( !p_track_description )
1098     {
1099         var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1100         vlc_object_release( p_input );
1101         libvlc_exception_raise( p_e, "no enough memory" );
1102         return NULL;
1103     }
1104     p_actual = p_track_description;
1105     p_previous = NULL;
1106     for( int i = 0; i < val_list.p_list->i_count; i++ )
1107     {
1108         if( !p_actual )
1109         {
1110             p_actual = ( libvlc_track_description_t * )
1111                 malloc( sizeof( libvlc_track_description_t ) );
1112             if ( !p_actual )
1113             {
1114                 libvlc_track_description_release( p_track_description );
1115                 var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1116                 vlc_object_release( p_input );
1117                 libvlc_exception_raise( p_e, "no enough memory" );
1118                 return NULL;
1119             }
1120         }
1121         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1122         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1123         p_actual->p_next = NULL;
1124         if( p_previous )
1125             p_previous->p_next = p_actual;
1126         p_previous = p_actual;
1127         p_actual =  NULL;
1128     }
1129     var_Change( p_input, psz_variable, VLC_VAR_FREELIST, &val_list, &text_list);
1130     vlc_object_release( p_input );
1131
1132     return p_track_description;
1133 }
1134
1135 void libvlc_track_description_release( libvlc_track_description_t *p_track_description )
1136 {
1137     libvlc_track_description_t *p_actual, *p_before;
1138     p_actual = p_track_description;
1139
1140     while ( p_actual )
1141     {
1142         free( p_actual->psz_name );
1143         p_before = p_actual;
1144         p_actual = p_before->p_next;
1145         free( p_before );
1146     }
1147 }
1148
1149 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1150                                      libvlc_exception_t *p_e )
1151 {
1152     input_thread_t *p_input_thread;
1153     vlc_value_t val;
1154
1155     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1156     if ( !p_input_thread )
1157     {
1158         /* We do return the right value, no need to throw an exception */
1159         if( libvlc_exception_raised( p_e ) )
1160             libvlc_exception_clear( p_e );
1161         return false;
1162     }
1163     var_Get( p_input_thread, "can-pause", &val );
1164     vlc_object_release( p_input_thread );
1165
1166     return val.b_bool;
1167 }