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