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