366a4fed4fe5664f97bca595c3ac707f2141014e
[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
225     libvlc_event_send( p_mi->p_event_manager, &event );
226     return VLC_SUCCESS;
227 }
228
229 /**************************************************************************
230  * Create a Media Instance object
231  **************************************************************************/
232 libvlc_media_instance_t *
233 libvlc_media_instance_new( libvlc_instance_t * p_libvlc_instance,
234                            libvlc_exception_t * p_e )
235 {
236     libvlc_media_instance_t * p_mi;
237
238     if( !p_libvlc_instance )
239     {
240         libvlc_exception_raise( p_e, "invalid libvlc instance" );
241         return NULL;
242     }
243
244     p_mi = malloc( sizeof(libvlc_media_instance_t) );
245     p_mi->p_md = NULL;
246     p_mi->drawable = 0;
247     p_mi->p_libvlc_instance = p_libvlc_instance;
248     p_mi->i_input_id = -1;
249     /* refcount strategy:
250      * - All items created by _new start with a refcount set to 1
251      * - Accessor _release decrease the refcount by 1, if after that
252      *   operation the refcount is 0, the object is destroyed.
253      * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
254     p_mi->i_refcount = 1;
255     p_mi->b_own_its_input_thread = VLC_TRUE;
256     /* object_lock strategy:
257      * - No lock held in constructor
258      * - Lock when accessing all variable this lock is held
259      * - Lock when attempting to destroy the object the lock is also held */
260     vlc_mutex_init( p_mi->p_libvlc_instance->p_libvlc_int,
261                     &p_mi->object_lock );
262     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
263             p_libvlc_instance, p_e );
264     if( libvlc_exception_raised( p_e ) )
265     {
266         free( p_mi );
267         return NULL;
268     }
269  
270     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
271             libvlc_MediaInstanceReachedEnd, p_e );
272     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
273             libvlc_MediaInstancePaused, p_e );
274     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
275             libvlc_MediaInstancePlayed, p_e );
276     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
277             libvlc_MediaInstancePositionChanged, p_e );
278     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
279             libvlc_MediaInstanceTimeChanged, p_e );
280
281     return p_mi;
282 }
283
284 /**************************************************************************
285  * Create a Media Instance object with a media descriptor
286  **************************************************************************/
287 libvlc_media_instance_t *
288 libvlc_media_instance_new_from_media_descriptor(
289                                     libvlc_media_descriptor_t * p_md,
290                                     libvlc_exception_t *p_e )
291 {
292     libvlc_media_instance_t * p_mi;
293     p_mi = libvlc_media_instance_new( p_md->p_libvlc_instance, p_e );
294
295     if( !p_mi )
296         return NULL;
297
298     libvlc_media_descriptor_retain( p_md );
299     p_mi->p_md = p_md;
300
301     return p_mi;
302 }
303
304 /**************************************************************************
305  * Create a new media instance object from an input_thread (Libvlc Internal)
306  **************************************************************************/
307 libvlc_media_instance_t * libvlc_media_instance_new_from_input_thread(
308                                    struct libvlc_instance_t *p_libvlc_instance,
309                                    input_thread_t *p_input,
310                                    libvlc_exception_t *p_e )
311 {
312     libvlc_media_instance_t * p_mi;
313
314     if( !p_input )
315     {
316         libvlc_exception_raise( p_e, "invalid input thread" );
317         return NULL;
318     }
319
320     p_mi = libvlc_media_instance_new( p_libvlc_instance, p_e );
321
322     if( !p_mi )
323         return NULL;
324
325     p_mi->p_md = libvlc_media_descriptor_new_from_input_item(
326                     p_libvlc_instance,
327                     input_GetItem( p_input ), p_e );
328
329     if( !p_mi->p_md )
330     {
331         libvlc_media_instance_destroy( p_mi );
332         return NULL;
333     }
334
335     p_mi->i_input_id = p_input->i_object_id;
336     p_mi->b_own_its_input_thread = VLC_FALSE;
337
338     /* will be released in media_instance_release() */
339     vlc_object_yield( p_input );
340
341     /* XXX: Hack as the playlist doesn't yield the input thread we retain
342      * the input for the playlist. (see corresponding hack in _release) */
343     vlc_object_yield( p_input );
344
345     return p_mi;
346 }
347
348 /**************************************************************************
349  * Destroy a Media Instance object (libvlc internal)
350  *
351  * Warning: No lock held here, but hey, this is internal.
352  **************************************************************************/
353 void libvlc_media_instance_destroy( libvlc_media_instance_t *p_mi )
354 {
355     input_thread_t *p_input_thread;
356     libvlc_exception_t p_e;
357
358     libvlc_exception_init( &p_e );
359
360     if( !p_mi )
361         return;
362
363     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
364
365     if( libvlc_exception_raised( &p_e ) )
366     {
367         libvlc_event_manager_release( p_mi->p_event_manager );
368         libvlc_exception_clear( &p_e );
369         free( p_mi );
370         return; /* no need to worry about no input thread */
371     }
372     vlc_mutex_destroy( &p_mi->object_lock );
373
374     input_DestroyThread( p_input_thread );
375
376     libvlc_media_descriptor_release( p_mi->p_md );
377
378     free( p_mi );
379 }
380
381 /**************************************************************************
382  * Release a Media Instance object
383  **************************************************************************/
384 void libvlc_media_instance_release( libvlc_media_instance_t *p_mi )
385 {
386     if( !p_mi )
387         return;
388
389     vlc_mutex_lock( &p_mi->object_lock );
390  
391     p_mi->i_refcount--;
392
393     if( p_mi->i_refcount > 0 )
394     {
395         vlc_mutex_unlock( &p_mi->object_lock );
396         return;
397     }
398     vlc_mutex_unlock( &p_mi->object_lock );
399     vlc_mutex_destroy( &p_mi->object_lock );
400
401     release_input_thread( p_mi );
402
403     libvlc_event_manager_release( p_mi->p_event_manager );
404  
405     libvlc_media_descriptor_release( p_mi->p_md );
406
407     free( p_mi );
408 }
409
410 /**************************************************************************
411  * Retain a Media Instance object
412  **************************************************************************/
413 void libvlc_media_instance_retain( libvlc_media_instance_t *p_mi )
414 {
415     if( !p_mi )
416         return;
417
418     p_mi->i_refcount++;
419 }
420
421 /**************************************************************************
422  * Set the Media descriptor associated with the instance
423  **************************************************************************/
424 void libvlc_media_instance_set_media_descriptor(
425                             libvlc_media_instance_t *p_mi,
426                             libvlc_media_descriptor_t *p_md,
427                             libvlc_exception_t *p_e )
428 {
429     (void)p_e;
430
431     if( !p_mi )
432         return;
433
434     vlc_mutex_lock( &p_mi->object_lock );
435
436     release_input_thread( p_mi );
437
438     libvlc_media_descriptor_release( p_mi->p_md );
439
440     if( !p_md )
441     {
442         p_mi->p_md = NULL;
443         vlc_mutex_unlock( &p_mi->object_lock );
444         return; /* It is ok to pass a NULL md */
445     }
446
447     libvlc_media_descriptor_retain( p_md );
448     p_mi->p_md = p_md;
449  
450     /* The policy here is to ignore that we were created using a different
451      * libvlc_instance, because we don't really care */
452     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
453
454     vlc_mutex_unlock( &p_mi->object_lock );
455 }
456
457 /**************************************************************************
458  * Get the Media descriptor associated with the instance
459  **************************************************************************/
460 libvlc_media_descriptor_t *
461 libvlc_media_instance_get_media_descriptor(
462                             libvlc_media_instance_t *p_mi,
463                             libvlc_exception_t *p_e )
464 {
465     (void)p_e;
466
467     if( !p_mi->p_md )
468         return NULL;
469
470     libvlc_media_descriptor_retain( p_mi->p_md );
471     return p_mi->p_md;
472 }
473
474 /**************************************************************************
475  * Get the event Manager
476  **************************************************************************/
477 libvlc_event_manager_t *
478 libvlc_media_instance_event_manager(
479                             libvlc_media_instance_t *p_mi,
480                             libvlc_exception_t *p_e )
481 {
482     (void)p_e;
483
484     return p_mi->p_event_manager;
485 }
486
487 /**************************************************************************
488  * Play
489  **************************************************************************/
490 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
491                                  libvlc_exception_t *p_e )
492 {
493     input_thread_t * p_input_thread;
494
495     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
496     {
497         /* A thread alread exists, send it a play message */
498         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
499         vlc_object_release( p_input_thread );
500         return;
501     }
502
503     /* Ignore previous exception */
504     libvlc_exception_clear( p_e );
505
506     vlc_mutex_lock( &p_mi->object_lock );
507  
508     if( !p_mi->p_md )
509     {
510         libvlc_exception_raise( p_e, "no associated media descriptor" );
511         vlc_mutex_unlock( &p_mi->object_lock );
512         return;
513     }
514
515     p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
516                                          p_mi->p_md->p_input_item );
517     p_mi->i_input_id = p_input_thread->i_object_id;
518
519     if( p_mi->drawable )
520     {
521         vlc_value_t val;
522         val.i_int = p_mi->drawable;
523         var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
524         var_Set( p_input_thread, "drawable", val );
525     }
526     var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
527     var_AddCallback( p_input_thread, "seekable", input_state_changed, p_mi );
528     var_AddCallback( p_input_thread, "pausable", input_state_changed, p_mi );
529     var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
530     var_AddCallback( p_input_thread, "intf-change", input_time_changed, p_mi );
531
532     /* will be released in media_instance_release() */
533     vlc_object_yield( p_input_thread );
534
535     vlc_mutex_unlock( &p_mi->object_lock );
536 }
537
538 /**************************************************************************
539  * Pause
540  **************************************************************************/
541 void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
542                                   libvlc_exception_t *p_e )
543 {
544     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
545
546     if( !p_input_thread )
547         return;
548
549     int state = var_GetInteger( p_input_thread, "state" );
550
551     if( state == PLAYING_S )
552     {
553         if( libvlc_media_instance_can_pause( p_mi, p_e ) )
554             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
555         else
556             libvlc_media_instance_stop( p_mi, p_e );
557     }
558     else
559         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
560
561     vlc_object_release( p_input_thread );
562 }
563
564 /**************************************************************************
565  * Stop
566  **************************************************************************/
567 void libvlc_media_instance_stop( libvlc_media_instance_t *p_mi,
568                                  libvlc_exception_t *p_e )
569 {
570     if( p_mi->b_own_its_input_thread )
571         release_input_thread( p_mi ); /* This will stop the input thread */
572     else
573     {
574         input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
575
576         if( !p_input_thread )
577             return;
578
579         input_StopThread( p_input_thread );
580         vlc_object_release( p_input_thread );
581     }
582 }
583
584 /**************************************************************************
585  * Set Drawable
586  **************************************************************************/
587 void libvlc_media_instance_set_drawable( libvlc_media_instance_t *p_mi,
588                                          libvlc_drawable_t drawable,
589                                          libvlc_exception_t *p_e )
590 {
591     p_mi->drawable = drawable;
592 }
593
594 /**************************************************************************
595  * Getters for stream information
596  **************************************************************************/
597 vlc_int64_t libvlc_media_instance_get_length(
598                              libvlc_media_instance_t *p_mi,
599                              libvlc_exception_t *p_e )
600 {
601     input_thread_t *p_input_thread;
602     vlc_value_t val;
603
604     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
605     if( !p_input_thread )
606         return -1;
607
608     var_Get( p_input_thread, "length", &val );
609     vlc_object_release( p_input_thread );
610
611     return (val.i_time+500LL)/1000LL;
612 }
613
614 vlc_int64_t libvlc_media_instance_get_time(
615                                    libvlc_media_instance_t *p_mi,
616                                    libvlc_exception_t *p_e )
617 {
618     input_thread_t *p_input_thread;
619     vlc_value_t val;
620
621     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
622     if( !p_input_thread )
623         return -1;
624
625     var_Get( p_input_thread , "time", &val );
626     vlc_object_release( p_input_thread );
627     return (val.i_time+500LL)/1000LL;
628 }
629
630 void libvlc_media_instance_set_time(
631                                  libvlc_media_instance_t *p_mi,
632                                  vlc_int64_t time,
633                                  libvlc_exception_t *p_e )
634 {
635     input_thread_t *p_input_thread;
636     vlc_value_t value;
637
638     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
639     if( !p_input_thread )
640         return;
641
642     value.i_time = time*1000LL;
643     var_Set( p_input_thread, "time", value );
644     vlc_object_release( p_input_thread );
645 }
646
647 void libvlc_media_instance_set_position(
648                                 libvlc_media_instance_t *p_mi,
649                                 float position,
650                                 libvlc_exception_t *p_e )
651 {
652     input_thread_t *p_input_thread;
653     vlc_value_t val;
654     val.f_float = position;
655
656     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
657     if( !p_input_thread )
658         return;
659
660     var_Set( p_input_thread, "position", val );
661     vlc_object_release( p_input_thread );
662 }
663
664 float libvlc_media_instance_get_position(
665                                  libvlc_media_instance_t *p_mi,
666                                  libvlc_exception_t *p_e )
667 {
668     input_thread_t *p_input_thread;
669     vlc_value_t val;
670
671     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
672     if( !p_input_thread )
673         return -1.0;
674
675     var_Get( p_input_thread, "position", &val );
676     vlc_object_release( p_input_thread );
677
678     return val.f_float;
679 }
680
681 void libvlc_media_instance_set_chapter(
682                                  libvlc_media_instance_t *p_mi,
683                                  int chapter,
684                                  libvlc_exception_t *p_e )
685 {
686     input_thread_t *p_input_thread;
687     vlc_value_t val;
688     val.i_int = chapter;
689
690     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
691     if( !p_input_thread )
692         return;
693
694     var_Set( p_input_thread, "chapter", val );
695     vlc_object_release( p_input_thread );
696 }
697
698 int libvlc_media_instance_get_chapter(
699                                  libvlc_media_instance_t *p_mi,
700                                  libvlc_exception_t *p_e )
701 {
702     input_thread_t *p_input_thread;
703     vlc_value_t val;
704
705     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
706     if( !p_input_thread )
707         return -1.0;
708
709     var_Get( p_input_thread, "chapter", &val );
710     vlc_object_release( p_input_thread );
711
712     return val.i_int;
713 }
714
715 int libvlc_media_instance_get_chapter_count(
716                                  libvlc_media_instance_t *p_mi,
717                                  libvlc_exception_t *p_e )
718 {
719     input_thread_t *p_input_thread;
720     vlc_value_t val;
721
722     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
723     if( !p_input_thread )
724         return -1.0;
725
726     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
727     vlc_object_release( p_input_thread );
728
729     return val.i_int;
730 }
731
732 float libvlc_media_instance_get_fps(
733                                  libvlc_media_instance_t *p_mi,
734                                  libvlc_exception_t *p_e)
735 {
736     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
737     double f_fps = 0.0;
738
739     if( p_input_thread )
740     {
741         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
742             f_fps = 0.0;
743         vlc_object_release( p_input_thread );
744     }
745     return f_fps;
746 }
747
748 vlc_bool_t libvlc_media_instance_will_play(
749                                  libvlc_media_instance_t *p_mi,
750                                  libvlc_exception_t *p_e)
751 {
752     input_thread_t *p_input_thread =
753                             libvlc_get_input_thread ( p_mi, p_e);
754     if ( !p_input_thread )
755         return VLC_FALSE;
756
757     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
758     {
759         vlc_object_release( p_input_thread );
760         return VLC_TRUE;
761     }
762     vlc_object_release( p_input_thread );
763     return VLC_FALSE;
764 }
765
766 void libvlc_media_instance_set_rate(
767                                  libvlc_media_instance_t *p_mi,
768                                  float rate,
769                                  libvlc_exception_t *p_e )
770 {
771     input_thread_t *p_input_thread;
772     vlc_value_t val;
773
774     if( rate <= 0 )
775         RAISEVOID( "Rate value is invalid" );
776
777     val.i_int = 1000.0f/rate;
778
779     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
780     if ( !p_input_thread )
781         return;
782
783     var_Set( p_input_thread, "rate", val );
784     vlc_object_release( p_input_thread );
785 }
786
787 float libvlc_media_instance_get_rate(
788                                  libvlc_media_instance_t *p_mi,
789                                  libvlc_exception_t *p_e )
790 {
791     input_thread_t *p_input_thread;
792     vlc_value_t val;
793
794     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
795     if ( !p_input_thread )
796         return -1.0;
797
798     var_Get( p_input_thread, "rate", &val );
799     vlc_object_release( p_input_thread );
800
801     return (float)1000.0f/val.i_int;
802 }
803
804 libvlc_state_t libvlc_media_instance_get_state(
805                                  libvlc_media_instance_t *p_mi,
806                                  libvlc_exception_t *p_e )
807 {
808     input_thread_t *p_input_thread;
809     vlc_value_t val;
810
811     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
812     if ( !p_input_thread )
813     {
814         /* We do return the right value, no need to throw an exception */
815         if( libvlc_exception_raised( p_e ) )
816             libvlc_exception_clear( p_e );
817         return libvlc_Stopped;
818     }
819
820     var_Get( p_input_thread, "state", &val );
821     vlc_object_release( p_input_thread );
822
823     return vlc_to_libvlc_state(val.i_int);
824 }
825
826 vlc_bool_t libvlc_media_instance_is_seekable(
827                                  libvlc_media_instance_t *p_mi,
828                                  libvlc_exception_t *p_e )
829 {
830     input_thread_t *p_input_thread;
831     vlc_value_t val;
832
833     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
834     if ( !p_input_thread )
835     {
836         /* We do return the right value, no need to throw an exception */
837         if( libvlc_exception_raised( p_e ) )
838             libvlc_exception_clear( p_e );
839         return VLC_FALSE;
840     }
841     var_Get( p_input_thread, "seekable", &val );
842     vlc_object_release( p_input_thread );
843
844     return val.b_bool;
845 }
846
847 vlc_bool_t libvlc_media_instance_can_pause(
848                                  libvlc_media_instance_t *p_mi,
849                                  libvlc_exception_t *p_e )
850 {
851     input_thread_t *p_input_thread;
852     vlc_value_t val;
853
854     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
855     if ( !p_input_thread )
856     {
857         /* We do return the right value, no need to throw an exception */
858         if( libvlc_exception_raised( p_e ) )
859             libvlc_exception_clear( p_e );
860         return VLC_FALSE;
861     }
862     var_Get( p_input_thread, "can-pause", &val );
863     vlc_object_release( p_input_thread );
864
865     return val.b_bool;
866 }