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