vlc/libvlc.h: Use libvlc_time_t.
[vlc.git] / src / control / media_instance.c
1 /*****************************************************************************
2  * media_instance.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 <vlc/libvlc.h>
25 #include <vlc_demux.h>
26 #include <vlc_input.h>
27 #include "libvlc_internal.h"
28 #include "libvlc.h"
29
30 static const libvlc_state_t vlc_to_libvlc_state_array[] =
31 {
32     [INIT_S]        = libvlc_Opening,
33     [OPENING_S]     = libvlc_Opening,
34     [BUFFERING_S]   = libvlc_Buffering,    
35     [PLAYING_S]     = libvlc_Playing,    
36     [PAUSE_S]       = libvlc_Paused,    
37     [END_S]         = libvlc_Ended,    
38     [ERROR_S]       = libvlc_Error,    
39 };
40 static inline libvlc_state_t vlc_to_libvlc_state( int vlc_state )
41 {
42     if( vlc_state < 0 || vlc_state > 6 )
43         return libvlc_Stopped;
44
45     return vlc_to_libvlc_state_array[vlc_state];
46 }
47
48 /*
49  * Release the associated input thread
50  *
51  * Object lock is NOT held.
52  */
53 static void release_input_thread( libvlc_media_instance_t *p_mi )
54 {
55     input_thread_t *p_input_thread;
56
57     if( !p_mi || p_mi->i_input_id == -1 )
58         return;
59
60     p_input_thread = (input_thread_t*)vlc_object_get(
61                                              p_mi->p_libvlc_instance->p_libvlc_int,
62                                              p_mi->i_input_id );
63
64     p_mi->i_input_id = -1;
65
66     if( !p_input_thread )
67         return;
68  
69     /* release for previous vlc_object_get */
70     vlc_object_release( p_input_thread );
71
72     /* release for initial p_input_thread yield (see _new()) */
73     vlc_object_release( p_input_thread );
74
75     /* No one is tracking this input_thread appart us. Destroy it */
76     if( p_mi->b_own_its_input_thread )
77     {
78         /* We owned this one */
79         input_StopThread( p_input_thread );
80         var_Destroy( p_input_thread, "drawable" );
81         input_DestroyThread( p_input_thread );
82     }
83     else
84     {
85         /* XXX: hack the playlist doesn't retain the input thread,
86          * so we did it for the playlist (see _new_from_input_thread),
87          * revert that here. This will be deleted with the playlist API */
88         vlc_object_release( p_input_thread );
89     }
90 }
91
92 /*
93  * Retrieve the input thread. Be sure to release the object
94  * once you are done with it. (libvlc Internal)
95  *
96  * Object lock is held.
97  */
98 input_thread_t *libvlc_get_input_thread( libvlc_media_instance_t *p_mi,
99                                          libvlc_exception_t *p_e )
100 {
101     input_thread_t *p_input_thread;
102
103     vlc_mutex_lock( &p_mi->object_lock );
104
105     if( !p_mi || p_mi->i_input_id == -1 )
106     {
107         vlc_mutex_unlock( &p_mi->object_lock );
108         RAISENULL( "Input is NULL" );
109     }
110
111     p_input_thread = (input_thread_t*)vlc_object_get(
112                                              p_mi->p_libvlc_instance->p_libvlc_int,
113                                              p_mi->i_input_id );
114     if( !p_input_thread )
115     {
116         vlc_mutex_unlock( &p_mi->object_lock );
117         RAISENULL( "Input does not exist" );
118     }
119
120     vlc_mutex_unlock( &p_mi->object_lock );
121     return p_input_thread;
122 }
123
124 /*
125  * input_state_changed (Private) (input var "state" Callback)
126  */
127 static int
128 input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
129                      vlc_value_t oldval, vlc_value_t newval,
130                      void * p_userdata )
131 {
132     libvlc_media_instance_t * p_mi = p_userdata;
133     libvlc_event_t event;
134     libvlc_event_type_t type = newval.i_int;
135
136     if( strcmp( psz_cmd, "state" ) )
137         type = var_GetBool( p_this, "state" );
138
139     switch ( type )
140     {
141         case END_S:
142             libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
143             event.type = libvlc_MediaInstanceReachedEnd;
144             break;
145         case PAUSE_S:
146             libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Playing, NULL);
147             event.type = libvlc_MediaInstancePaused;
148             break;
149         case PLAYING_S:
150             libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Playing, NULL);
151             event.type = libvlc_MediaInstancePlayed;
152             break;
153         case ERROR_S:
154             libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Error, NULL);
155             event.type = libvlc_MediaInstanceReachedEnd; /* Because ERROR_S is buggy */
156             break;
157         default:
158             return VLC_SUCCESS;
159     }
160
161     libvlc_event_send( p_mi->p_event_manager, &event );
162     return VLC_SUCCESS;
163 }
164
165 /*
166  * input_position_changed (Private) (input var "intf-change" Callback)
167  */
168 static int
169 input_position_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     libvlc_media_instance_t * p_mi = p_userdata;
174     vlc_value_t val;
175
176     if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */))
177     {
178         input_thread_t * p_input = (input_thread_t *)p_this;
179
180         var_Get( p_input, "state", &val );
181         if( val.i_int != PLAYING_S )
182             return VLC_SUCCESS; /* Don't send the position while stopped */
183
184         var_Get( p_input, "position", &val );
185     }
186     else
187         val.i_time = newval.i_time;
188
189     libvlc_event_t event;
190     event.type = libvlc_MediaInstancePositionChanged;
191     event.u.media_instance_position_changed.new_position = val.f_float;
192
193     libvlc_event_send( p_mi->p_event_manager, &event );
194     return VLC_SUCCESS;
195 }
196
197 /*
198  * input_time_changed (Private) (input var "intf-change" Callback)
199  */
200 static int
201 input_time_changed( vlc_object_t * p_this, char const * psz_cmd,
202                      vlc_value_t oldval, vlc_value_t newval,
203                      void * p_userdata )
204 {
205     libvlc_media_instance_t * p_mi = p_userdata;
206     vlc_value_t val;
207
208     if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */))
209     {
210         input_thread_t * p_input = (input_thread_t *)p_this;
211     
212         var_Get( p_input, "state", &val );
213         if( val.i_int != PLAYING_S )
214             return VLC_SUCCESS; /* Don't send the position while stopped */
215
216         var_Get( p_input, "time", &val );
217     }
218     else
219         val.i_time = newval.i_time;
220
221     libvlc_event_t event;
222     event.type = libvlc_MediaInstanceTimeChanged;
223     event.u.media_instance_time_changed.new_time = val.i_time;
224     libvlc_event_send( p_mi->p_event_manager, &event );
225     return VLC_SUCCESS;
226 }
227
228 /**************************************************************************
229  * Create a Media Instance object
230  **************************************************************************/
231 libvlc_media_instance_t *
232 libvlc_media_instance_new( libvlc_instance_t * p_libvlc_instance,
233                            libvlc_exception_t * p_e )
234 {
235     libvlc_media_instance_t * p_mi;
236
237     if( !p_libvlc_instance )
238     {
239         libvlc_exception_raise( p_e, "invalid libvlc instance" );
240         return NULL;
241     }
242
243     p_mi = malloc( sizeof(libvlc_media_instance_t) );
244     p_mi->p_md = NULL;
245     p_mi->drawable = 0;
246     p_mi->p_libvlc_instance = p_libvlc_instance;
247     p_mi->i_input_id = -1;
248     /* refcount strategy:
249      * - All items created by _new start with a refcount set to 1
250      * - Accessor _release decrease the refcount by 1, if after that
251      *   operation the refcount is 0, the object is destroyed.
252      * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
253     p_mi->i_refcount = 1;
254     p_mi->b_own_its_input_thread = VLC_TRUE;
255     /* object_lock strategy:
256      * - No lock held in constructor
257      * - Lock when accessing all variable this lock is held
258      * - Lock when attempting to destroy the object the lock is also held */
259     vlc_mutex_init( p_mi->p_libvlc_instance->p_libvlc_int,
260                     &p_mi->object_lock );
261     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
262             p_libvlc_instance, p_e );
263     if( libvlc_exception_raised( p_e ) )
264     {
265         free( p_mi );
266         return NULL;
267     }
268  
269     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
270             libvlc_MediaInstanceReachedEnd, p_e );
271     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
272             libvlc_MediaInstancePaused, p_e );
273     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
274             libvlc_MediaInstancePlayed, p_e );
275     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
276             libvlc_MediaInstancePositionChanged, p_e );
277     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
278             libvlc_MediaInstanceTimeChanged, p_e );
279
280     return p_mi;
281 }
282
283 /**************************************************************************
284  * Create a Media Instance object with a media descriptor
285  **************************************************************************/
286 libvlc_media_instance_t *
287 libvlc_media_instance_new_from_media_descriptor(
288                                     libvlc_media_descriptor_t * p_md,
289                                     libvlc_exception_t *p_e )
290 {
291     libvlc_media_instance_t * p_mi;
292     p_mi = libvlc_media_instance_new( p_md->p_libvlc_instance, p_e );
293
294     if( !p_mi )
295         return NULL;
296
297     libvlc_media_descriptor_retain( p_md );
298     p_mi->p_md = p_md;
299
300     return p_mi;
301 }
302
303 /**************************************************************************
304  * Create a new media instance object from an input_thread (Libvlc Internal)
305  **************************************************************************/
306 libvlc_media_instance_t * libvlc_media_instance_new_from_input_thread(
307                                    struct libvlc_instance_t *p_libvlc_instance,
308                                    input_thread_t *p_input,
309                                    libvlc_exception_t *p_e )
310 {
311     libvlc_media_instance_t * p_mi;
312
313     if( !p_input )
314     {
315         libvlc_exception_raise( p_e, "invalid input thread" );
316         return NULL;
317     }
318
319     p_mi = libvlc_media_instance_new( p_libvlc_instance, p_e );
320
321     if( !p_mi )
322         return NULL;
323
324     p_mi->p_md = libvlc_media_descriptor_new_from_input_item(
325                     p_libvlc_instance,
326                     input_GetItem( p_input ), p_e );
327
328     if( !p_mi->p_md )
329     {
330         libvlc_media_instance_destroy( p_mi );
331         return NULL;
332     }
333
334     p_mi->i_input_id = p_input->i_object_id;
335     p_mi->b_own_its_input_thread = VLC_FALSE;
336
337     /* will be released in media_instance_release() */
338     vlc_object_yield( p_input );
339
340     /* XXX: Hack as the playlist doesn't yield the input thread we retain
341      * the input for the playlist. (see corresponding hack in _release) */
342     vlc_object_yield( p_input );
343
344     return p_mi;
345 }
346
347 /**************************************************************************
348  * Destroy a Media Instance object (libvlc internal)
349  *
350  * Warning: No lock held here, but hey, this is internal.
351  **************************************************************************/
352 void libvlc_media_instance_destroy( libvlc_media_instance_t *p_mi )
353 {
354     input_thread_t *p_input_thread;
355     libvlc_exception_t p_e;
356
357     libvlc_exception_init( &p_e );
358
359     if( !p_mi )
360         return;
361
362     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
363
364     if( libvlc_exception_raised( &p_e ) )
365     {
366         libvlc_event_manager_release( p_mi->p_event_manager );
367         libvlc_exception_clear( &p_e );
368         free( p_mi );
369         return; /* no need to worry about no input thread */
370     }
371     vlc_mutex_destroy( &p_mi->object_lock );
372
373     input_DestroyThread( p_input_thread );
374
375     libvlc_media_descriptor_release( p_mi->p_md );
376
377     free( p_mi );
378 }
379
380 /**************************************************************************
381  * Release a Media Instance object
382  **************************************************************************/
383 void libvlc_media_instance_release( libvlc_media_instance_t *p_mi )
384 {
385     if( !p_mi )
386         return;
387
388     vlc_mutex_lock( &p_mi->object_lock );
389  
390     p_mi->i_refcount--;
391
392     if( p_mi->i_refcount > 0 )
393     {
394         vlc_mutex_unlock( &p_mi->object_lock );
395         return;
396     }
397     vlc_mutex_unlock( &p_mi->object_lock );
398     vlc_mutex_destroy( &p_mi->object_lock );
399
400     release_input_thread( p_mi );
401
402     libvlc_event_manager_release( p_mi->p_event_manager );
403  
404     libvlc_media_descriptor_release( p_mi->p_md );
405
406     free( p_mi );
407 }
408
409 /**************************************************************************
410  * Retain a Media Instance object
411  **************************************************************************/
412 void libvlc_media_instance_retain( libvlc_media_instance_t *p_mi )
413 {
414     if( !p_mi )
415         return;
416
417     p_mi->i_refcount++;
418 }
419
420 /**************************************************************************
421  * Set the Media descriptor associated with the instance
422  **************************************************************************/
423 void libvlc_media_instance_set_media_descriptor(
424                             libvlc_media_instance_t *p_mi,
425                             libvlc_media_descriptor_t *p_md,
426                             libvlc_exception_t *p_e )
427 {
428     (void)p_e;
429
430     if( !p_mi )
431         return;
432
433     vlc_mutex_lock( &p_mi->object_lock );
434
435     release_input_thread( p_mi );
436
437     libvlc_media_descriptor_release( p_mi->p_md );
438
439     if( !p_md )
440     {
441         p_mi->p_md = NULL;
442         vlc_mutex_unlock( &p_mi->object_lock );
443         return; /* It is ok to pass a NULL md */
444     }
445
446     libvlc_media_descriptor_retain( p_md );
447     p_mi->p_md = p_md;
448  
449     /* The policy here is to ignore that we were created using a different
450      * libvlc_instance, because we don't really care */
451     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
452
453     vlc_mutex_unlock( &p_mi->object_lock );
454 }
455
456 /**************************************************************************
457  * Get the Media descriptor associated with the instance
458  **************************************************************************/
459 libvlc_media_descriptor_t *
460 libvlc_media_instance_get_media_descriptor(
461                             libvlc_media_instance_t *p_mi,
462                             libvlc_exception_t *p_e )
463 {
464     (void)p_e;
465
466     if( !p_mi->p_md )
467         return NULL;
468
469     libvlc_media_descriptor_retain( p_mi->p_md );
470     return p_mi->p_md;
471 }
472
473 /**************************************************************************
474  * Get the event Manager
475  **************************************************************************/
476 libvlc_event_manager_t *
477 libvlc_media_instance_event_manager(
478                             libvlc_media_instance_t *p_mi,
479                             libvlc_exception_t *p_e )
480 {
481     (void)p_e;
482
483     return p_mi->p_event_manager;
484 }
485
486 /**************************************************************************
487  * Play
488  **************************************************************************/
489 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
490                                  libvlc_exception_t *p_e )
491 {
492     input_thread_t * p_input_thread;
493
494     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
495     {
496         /* A thread alread exists, send it a play message */
497         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
498         vlc_object_release( p_input_thread );
499         return;
500     }
501
502     /* Ignore previous exception */
503     libvlc_exception_clear( p_e );
504
505     vlc_mutex_lock( &p_mi->object_lock );
506  
507     if( !p_mi->p_md )
508     {
509         libvlc_exception_raise( p_e, "no associated media descriptor" );
510         vlc_mutex_unlock( &p_mi->object_lock );
511         return;
512     }
513
514     p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
515                                          p_mi->p_md->p_input_item );
516     p_mi->i_input_id = p_input_thread->i_object_id;
517
518     if( p_mi->drawable )
519     {
520         vlc_value_t val;
521         val.i_int = p_mi->drawable;
522         var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
523         var_Set( p_input_thread, "drawable", val );
524     }
525     var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
526     var_AddCallback( p_input_thread, "seekable", input_state_changed, p_mi );
527     var_AddCallback( p_input_thread, "pausable", input_state_changed, p_mi );
528     var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
529     var_AddCallback( p_input_thread, "intf-change", input_time_changed, p_mi );
530
531     /* will be released in media_instance_release() */
532     vlc_object_yield( p_input_thread );
533
534     vlc_mutex_unlock( &p_mi->object_lock );
535 }
536
537 /**************************************************************************
538  * Pause
539  **************************************************************************/
540 void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
541                                   libvlc_exception_t *p_e )
542 {
543     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
544
545     if( !p_input_thread )
546         return;
547
548     int state = var_GetInteger( p_input_thread, "state" );
549
550     if( state == PLAYING_S )
551     {
552         if( libvlc_media_instance_can_pause( p_mi, p_e ) )
553             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
554         else
555             libvlc_media_instance_stop( p_mi, p_e );
556     }
557     else
558         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
559
560     vlc_object_release( p_input_thread );
561 }
562
563 /**************************************************************************
564  * Stop
565  **************************************************************************/
566 void libvlc_media_instance_stop( libvlc_media_instance_t *p_mi,
567                                  libvlc_exception_t *p_e )
568 {
569     if( p_mi->b_own_its_input_thread )
570         release_input_thread( p_mi ); /* This will stop the input thread */
571     else
572     {
573         input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
574
575         if( !p_input_thread )
576             return;
577
578         input_StopThread( p_input_thread );
579         vlc_object_release( p_input_thread );
580     }
581 }
582
583 /**************************************************************************
584  * Set Drawable
585  **************************************************************************/
586 void libvlc_media_instance_set_drawable( libvlc_media_instance_t *p_mi,
587                                          libvlc_drawable_t drawable,
588                                          libvlc_exception_t *p_e )
589 {
590     p_mi->drawable = drawable;
591 }
592
593 /**************************************************************************
594  * Getters for stream information
595  **************************************************************************/
596 libvlc_time_t libvlc_media_instance_get_length(
597                              libvlc_media_instance_t *p_mi,
598                              libvlc_exception_t *p_e )
599 {
600     input_thread_t *p_input_thread;
601     vlc_value_t val;
602
603     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
604     if( !p_input_thread )
605         return -1;
606
607     var_Get( p_input_thread, "length", &val );
608     vlc_object_release( p_input_thread );
609
610     return (val.i_time+500LL)/1000LL;
611 }
612
613 libvlc_time_t libvlc_media_instance_get_time(
614                                    libvlc_media_instance_t *p_mi,
615                                    libvlc_exception_t *p_e )
616 {
617     input_thread_t *p_input_thread;
618     vlc_value_t val;
619
620     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
621     if( !p_input_thread )
622         return -1;
623
624     var_Get( p_input_thread , "time", &val );
625     vlc_object_release( p_input_thread );
626     return (val.i_time+500LL)/1000LL;
627 }
628
629 void libvlc_media_instance_set_time(
630                                  libvlc_media_instance_t *p_mi,
631                                  libvlc_time_t time,
632                                  libvlc_exception_t *p_e )
633 {
634     input_thread_t *p_input_thread;
635     vlc_value_t value;
636
637     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
638     if( !p_input_thread )
639         return;
640
641     value.i_time = time*1000LL;
642     var_Set( p_input_thread, "time", value );
643     vlc_object_release( p_input_thread );
644 }
645
646 void libvlc_media_instance_set_position(
647                                 libvlc_media_instance_t *p_mi,
648                                 float position,
649                                 libvlc_exception_t *p_e )
650 {
651     input_thread_t *p_input_thread;
652     vlc_value_t val;
653     val.f_float = position;
654
655     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
656     if( !p_input_thread )
657         return;
658
659     var_Set( p_input_thread, "position", val );
660     vlc_object_release( p_input_thread );
661 }
662
663 float libvlc_media_instance_get_position(
664                                  libvlc_media_instance_t *p_mi,
665                                  libvlc_exception_t *p_e )
666 {
667     input_thread_t *p_input_thread;
668     vlc_value_t val;
669
670     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
671     if( !p_input_thread )
672         return -1.0;
673
674     var_Get( p_input_thread, "position", &val );
675     vlc_object_release( p_input_thread );
676
677     return val.f_float;
678 }
679
680 void libvlc_media_instance_set_chapter(
681                                  libvlc_media_instance_t *p_mi,
682                                  int chapter,
683                                  libvlc_exception_t *p_e )
684 {
685     input_thread_t *p_input_thread;
686     vlc_value_t val;
687     val.i_int = chapter;
688
689     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
690     if( !p_input_thread )
691         return;
692
693     var_Set( p_input_thread, "chapter", val );
694     vlc_object_release( p_input_thread );
695 }
696
697 int libvlc_media_instance_get_chapter(
698                                  libvlc_media_instance_t *p_mi,
699                                  libvlc_exception_t *p_e )
700 {
701     input_thread_t *p_input_thread;
702     vlc_value_t val;
703
704     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
705     if( !p_input_thread )
706         return -1.0;
707
708     var_Get( p_input_thread, "chapter", &val );
709     vlc_object_release( p_input_thread );
710
711     return val.i_int;
712 }
713
714 int libvlc_media_instance_get_chapter_count(
715                                  libvlc_media_instance_t *p_mi,
716                                  libvlc_exception_t *p_e )
717 {
718     input_thread_t *p_input_thread;
719     vlc_value_t val;
720
721     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
722     if( !p_input_thread )
723         return -1.0;
724
725     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
726     vlc_object_release( p_input_thread );
727
728     return val.i_int;
729 }
730
731 float libvlc_media_instance_get_fps(
732                                  libvlc_media_instance_t *p_mi,
733                                  libvlc_exception_t *p_e)
734 {
735     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
736     double f_fps = 0.0;
737
738     if( p_input_thread )
739     {
740         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
741             f_fps = 0.0;
742         vlc_object_release( p_input_thread );
743     }
744     return f_fps;
745 }
746
747 vlc_bool_t libvlc_media_instance_will_play(
748                                  libvlc_media_instance_t *p_mi,
749                                  libvlc_exception_t *p_e)
750 {
751     input_thread_t *p_input_thread =
752                             libvlc_get_input_thread ( p_mi, p_e);
753     if ( !p_input_thread )
754         return VLC_FALSE;
755
756     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
757     {
758         vlc_object_release( p_input_thread );
759         return VLC_TRUE;
760     }
761     vlc_object_release( p_input_thread );
762     return VLC_FALSE;
763 }
764
765 void libvlc_media_instance_set_rate(
766                                  libvlc_media_instance_t *p_mi,
767                                  float rate,
768                                  libvlc_exception_t *p_e )
769 {
770     input_thread_t *p_input_thread;
771     vlc_value_t val;
772
773     if( rate <= 0 )
774         RAISEVOID( "Rate value is invalid" );
775
776     val.i_int = 1000.0f/rate;
777
778     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
779     if ( !p_input_thread )
780         return;
781
782     var_Set( p_input_thread, "rate", val );
783     vlc_object_release( p_input_thread );
784 }
785
786 float libvlc_media_instance_get_rate(
787                                  libvlc_media_instance_t *p_mi,
788                                  libvlc_exception_t *p_e )
789 {
790     input_thread_t *p_input_thread;
791     vlc_value_t val;
792
793     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
794     if ( !p_input_thread )
795         return -1.0;
796
797     var_Get( p_input_thread, "rate", &val );
798     vlc_object_release( p_input_thread );
799
800     return (float)1000.0f/val.i_int;
801 }
802
803 libvlc_state_t libvlc_media_instance_get_state(
804                                  libvlc_media_instance_t *p_mi,
805                                  libvlc_exception_t *p_e )
806 {
807     input_thread_t *p_input_thread;
808     vlc_value_t val;
809
810     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
811     if ( !p_input_thread )
812     {
813         /* We do return the right value, no need to throw an exception */
814         if( libvlc_exception_raised( p_e ) )
815             libvlc_exception_clear( p_e );
816         return libvlc_Stopped;
817     }
818
819     var_Get( p_input_thread, "state", &val );
820     vlc_object_release( p_input_thread );
821
822     return vlc_to_libvlc_state(val.i_int);
823 }
824
825 vlc_bool_t libvlc_media_instance_is_seekable(
826                                  libvlc_media_instance_t *p_mi,
827                                  libvlc_exception_t *p_e )
828 {
829     input_thread_t *p_input_thread;
830     vlc_value_t val;
831
832     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
833     if ( !p_input_thread )
834     {
835         /* We do return the right value, no need to throw an exception */
836         if( libvlc_exception_raised( p_e ) )
837             libvlc_exception_clear( p_e );
838         return VLC_FALSE;
839     }
840     var_Get( p_input_thread, "seekable", &val );
841     vlc_object_release( p_input_thread );
842
843     return val.b_bool;
844 }
845
846 vlc_bool_t libvlc_media_instance_can_pause(
847                                  libvlc_media_instance_t *p_mi,
848                                  libvlc_exception_t *p_e )
849 {
850     input_thread_t *p_input_thread;
851     vlc_value_t val;
852
853     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
854     if ( !p_input_thread )
855     {
856         /* We do return the right value, no need to throw an exception */
857         if( libvlc_exception_raised( p_e ) )
858             libvlc_exception_clear( p_e );
859         return VLC_FALSE;
860     }
861     var_Get( p_input_thread, "can-pause", &val );
862     vlc_object_release( p_input_thread );
863
864     return val.b_bool;
865 }