]> git.sesse.net Git - vlc/blob - src/control/media_player.c
6a14377609a53ee9937ab07b5a71ba97ae56a8b4
[vlc] / src / control / media_player.c
1 /*****************************************************************************
2  * media_player.c: Libvlc API Media Instance management functions
3  *****************************************************************************
4  * Copyright (C) 2005-2009 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 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <assert.h>
29
30 #include <vlc/libvlc.h>
31 #include <vlc/libvlc_media.h>
32 #include <vlc/libvlc_events.h>
33
34 #include <vlc_demux.h>
35 #include <vlc_input.h>
36 #include <vlc_vout.h>
37
38 #include "libvlc.h"
39
40 #include "libvlc_internal.h"
41 #include "media_internal.h" // libvlc_media_set_state()
42 #include "media_player_internal.h"
43
44 static int
45 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
46                         vlc_value_t oldval, vlc_value_t newval,
47                         void * p_userdata );
48 static int
49 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
50                         vlc_value_t oldval, vlc_value_t newval,
51                         void * p_userdata );
52 static int
53 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
54                      vlc_value_t oldval, vlc_value_t newval,
55                      void * p_userdata );
56
57 static int
58 snapshot_was_taken( vlc_object_t *p_this, char const *psz_cmd,
59                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
60
61 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
62
63 /*
64  * Shortcuts
65  */
66
67 #define register_event(a, b) __register_event(a, libvlc_MediaPlayer ## b)
68 static inline void __register_event(libvlc_media_player_t *mp, libvlc_event_type_t type)
69 {
70     libvlc_event_manager_register_event_type(mp->p_event_manager, type, NULL);
71 }
72
73 static inline void lock(libvlc_media_player_t *mp)
74 {
75     vlc_mutex_lock(&mp->object_lock);
76 }
77
78 static inline void unlock(libvlc_media_player_t *mp)
79 {
80     vlc_mutex_unlock(&mp->object_lock);
81 }
82
83 static inline void clear_if_needed(libvlc_exception_t *e)
84 {
85     if (libvlc_exception_raised(e))
86         libvlc_exception_clear(e);
87 }
88
89 /*
90  * Release the associated input thread.
91  *
92  * Object lock is NOT held.
93  */
94 static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abort )
95 {
96     input_thread_t * p_input_thread;
97
98     if( !p_mi || !p_mi->p_input_thread )
99         return;
100
101     p_input_thread = p_mi->p_input_thread;
102
103     var_DelCallback( p_input_thread, "can-seek",
104                      input_seekable_changed, p_mi );
105     var_DelCallback( p_input_thread, "can-pause",
106                     input_pausable_changed, p_mi );
107     var_DelCallback( p_input_thread, "intf-event",
108                      input_event_changed, p_mi );
109
110     /* We owned this one */
111     input_Stop( p_input_thread, b_input_abort );
112
113     vlc_thread_join( p_input_thread );
114
115     assert( p_mi->p_input_resource == NULL );
116     assert( p_input_thread->b_dead );
117     /* Store the input resource for future use. */
118     p_mi->p_input_resource = input_DetachResource( p_input_thread );
119
120     var_Destroy( p_input_thread, "drawable-hwnd" );
121     var_Destroy( p_input_thread, "drawable-xid" );
122     var_Destroy( p_input_thread, "drawable-agl" );
123
124     vlc_object_release( p_input_thread );
125
126     p_mi->p_input_thread = NULL;
127 }
128
129 /*
130  * Retrieve the input thread. Be sure to release the object
131  * once you are done with it. (libvlc Internal)
132  *
133  * Function will lock the object.
134  */
135 input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi,
136                                          libvlc_exception_t *p_e )
137 {
138     input_thread_t *p_input_thread;
139
140     if( !p_mi ) RAISENULL( "Media Instance is NULL" );
141
142     lock(p_mi);
143
144     if( !p_mi->p_input_thread )
145     {
146         unlock(p_mi);
147         RAISENULL( "Input is NULL" );
148     }
149
150     p_input_thread = p_mi->p_input_thread;
151     vlc_object_hold( p_input_thread );
152
153     unlock(p_mi);
154
155     return p_input_thread;
156 }
157
158 static int
159 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
160                         vlc_value_t oldval, vlc_value_t newval,
161                         void * p_userdata )
162 {
163     VLC_UNUSED(oldval);
164     VLC_UNUSED(p_this);
165     VLC_UNUSED(psz_cmd);
166     libvlc_media_player_t * p_mi = p_userdata;
167     libvlc_event_t event;
168
169     libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial );
170     event.type = libvlc_MediaPlayerSeekableChanged;
171     event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
172
173     libvlc_event_send( p_mi->p_event_manager, &event );
174     return VLC_SUCCESS;
175 }
176
177 static int
178 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
179                         vlc_value_t oldval, vlc_value_t newval,
180                         void * p_userdata )
181 {
182     VLC_UNUSED(oldval);
183     VLC_UNUSED(p_this);
184     VLC_UNUSED(psz_cmd);
185     libvlc_media_player_t * p_mi = p_userdata;
186     libvlc_event_t event;
187
188     libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial );
189     event.type = libvlc_MediaPlayerPausableChanged;
190     event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
191
192     libvlc_event_send( p_mi->p_event_manager, &event );
193     return VLC_SUCCESS;
194 }
195
196 static int
197 input_event_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     VLC_UNUSED(oldval);
202     input_thread_t * p_input = (input_thread_t *)p_this;
203     libvlc_media_player_t * p_mi = p_userdata;
204     libvlc_event_t event;
205
206     assert( !strcmp( psz_cmd, "intf-event" ) );
207
208     if( newval.i_int == INPUT_EVENT_STATE )
209     {
210         libvlc_state_t libvlc_state;
211
212         switch ( var_GetInteger( p_input, "state" ) )
213         {
214             case INIT_S:
215                 libvlc_state = libvlc_NothingSpecial;
216                 event.type = libvlc_MediaPlayerNothingSpecial;
217                 break;
218             case OPENING_S:
219                 libvlc_state = libvlc_Opening;
220                 event.type = libvlc_MediaPlayerOpening;
221                 break;
222             case PLAYING_S:
223                 libvlc_state = libvlc_Playing;
224                 event.type = libvlc_MediaPlayerPlaying;
225                 break;
226             case PAUSE_S:
227                 libvlc_state = libvlc_Paused;
228                 event.type = libvlc_MediaPlayerPaused;
229                 break;
230             case END_S:
231                 libvlc_state = libvlc_Ended;
232                 event.type = libvlc_MediaPlayerEndReached;
233                 break;
234             case ERROR_S:
235                 libvlc_state = libvlc_Error;
236                 event.type = libvlc_MediaPlayerEncounteredError;
237                 break;
238
239             default:
240                 return VLC_SUCCESS;
241         }
242
243         libvlc_media_set_state( p_mi->p_md, libvlc_state );
244         libvlc_event_send( p_mi->p_event_manager, &event );
245     }
246     else if( newval.i_int == INPUT_EVENT_ABORT )
247     {
248         libvlc_state_t libvlc_state = libvlc_Stopped;
249         event.type = libvlc_MediaPlayerStopped;
250
251         libvlc_media_set_state( p_mi->p_md, libvlc_state );
252         libvlc_event_send( p_mi->p_event_manager, &event );
253     }
254     else if( newval.i_int == INPUT_EVENT_POSITION )
255     {
256         if( var_GetInteger( p_input, "state" ) != PLAYING_S )
257             return VLC_SUCCESS; /* Don't send the position while stopped */
258
259         /* */
260         event.type = libvlc_MediaPlayerPositionChanged;
261         event.u.media_player_position_changed.new_position =
262                                           var_GetFloat( p_input, "position" );
263         libvlc_event_send( p_mi->p_event_manager, &event );
264
265         /* */
266         event.type = libvlc_MediaPlayerTimeChanged;
267         event.u.media_player_time_changed.new_time =
268                                                var_GetTime( p_input, "time" );
269         libvlc_event_send( p_mi->p_event_manager, &event );
270     }
271     else if( newval.i_int == INPUT_EVENT_LENGTH )
272     {
273         event.type = libvlc_MediaPlayerLengthChanged;
274         event.u.media_player_length_changed.new_length =
275                                                var_GetTime( p_input, "length" );
276         libvlc_event_send( p_mi->p_event_manager, &event );
277     }
278
279     return VLC_SUCCESS;
280
281 }
282
283 /**************************************************************************
284  * Snapshot Taken Event.
285  *
286  * FIXME: This snapshot API interface makes no sense in media_player.
287  *************************************************************************/
288 static int snapshot_was_taken(vlc_object_t *p_this, char const *psz_cmd,
289                               vlc_value_t oldval, vlc_value_t newval, void *p_data )
290 {
291     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_this);
292
293     libvlc_media_player_t *mp = p_data;
294     libvlc_event_t event;
295     event.type = libvlc_MediaPlayerSnapshotTaken;
296     event.u.media_player_snapshot_taken.psz_filename = newval.psz_string;
297     libvlc_event_send(mp->p_event_manager, &event);
298
299     return VLC_SUCCESS;
300 }
301
302
303 static void libvlc_media_player_destroy( libvlc_media_player_t * );
304
305
306 /**************************************************************************
307  * Create a Media Instance object.
308  *
309  * Refcount strategy:
310  * - All items created by _new start with a refcount set to 1.
311  * - Accessor _release decrease the refcount by 1, if after that
312  *   operation the refcount is 0, the object is destroyed.
313  * - Accessor _retain increase the refcount by 1 (XXX: to implement)
314  *
315  * Object locking strategy:
316  * - No lock held while in constructor.
317  * - When accessing any member variable this lock is held. (XXX who locks?)
318  * - When attempting to destroy the object the lock is also held.
319  **************************************************************************/
320 libvlc_media_player_t *
321 libvlc_media_player_new( libvlc_instance_t *instance, libvlc_exception_t *e )
322 {
323     libvlc_media_player_t * mp;
324
325     assert(instance);
326
327     mp = malloc(sizeof(libvlc_media_player_t));
328     if (!mp)
329     {
330         libvlc_exception_raise(e);
331         libvlc_printerr("Not enough memory");
332         return NULL;
333     }
334     mp->p_md = NULL;
335     mp->drawable.agl = 0;
336     mp->drawable.xid = 0;
337     mp->drawable.hwnd = NULL;
338     mp->drawable.nsobject = NULL;
339     mp->p_libvlc_instance = instance;
340     mp->p_input_thread = NULL;
341     mp->p_input_resource = NULL;
342     mp->i_refcount = 1;
343     vlc_mutex_init(&mp->object_lock);
344     mp->p_event_manager = libvlc_event_manager_new(mp, instance, e);
345     if (libvlc_exception_raised(e))
346     {
347         vlc_mutex_destroy(&mp->object_lock);
348         free(mp);
349         return NULL;
350     }
351
352     register_event(mp, NothingSpecial);
353     register_event(mp, Opening);
354     register_event(mp, Buffering);
355     register_event(mp, Playing);
356     register_event(mp, Paused);
357     register_event(mp, Stopped);
358     register_event(mp, Forward);
359     register_event(mp, Backward);
360     register_event(mp, EndReached);
361     register_event(mp, EncounteredError);
362
363     register_event(mp, PositionChanged);
364     register_event(mp, TimeChanged);
365     register_event(mp, LengthChanged);
366     register_event(mp, TitleChanged);
367     register_event(mp, PausableChanged);
368
369     /* Snapshot initialization */
370     register_event(mp, SnapshotTaken);
371
372     /* Attach a var callback to the global object to provide the glue between
373      * vout_thread that generates the event and media_player that re-emits it
374      * with its own event manager
375      *
376      * FIXME: It's unclear why we want to put this in public API, and why we
377      * want to expose it in such a limiting and ugly way.
378      */
379     var_AddCallback(instance->p_libvlc_int, "snapshot-file", snapshot_was_taken, mp);
380
381     return mp;
382 }
383
384 /**************************************************************************
385  * Create a Media Instance object with a media descriptor.
386  **************************************************************************/
387 libvlc_media_player_t *
388 libvlc_media_player_new_from_media(
389                                     libvlc_media_t * p_md,
390                                     libvlc_exception_t *p_e )
391 {
392     libvlc_media_player_t * p_mi;
393
394     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance, p_e );
395     if( !p_mi )
396         return NULL;
397
398     libvlc_media_retain( p_md );
399     p_mi->p_md = p_md;
400
401     return p_mi;
402 }
403
404 /**************************************************************************
405  * Destroy a Media Instance object (libvlc internal)
406  *
407  * Warning: No lock held here, but hey, this is internal. Caller must lock.
408  **************************************************************************/
409 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
410 {
411     assert( p_mi );
412
413     /* Detach Callback from the main libvlc object */
414     var_DelCallback( p_mi->p_libvlc_instance->p_libvlc_int,
415                      "snapshot-file", snapshot_was_taken, p_mi );
416
417     /* Release the input thread */
418     release_input_thread( p_mi, true );
419
420     if( p_mi->p_input_resource )
421     {
422         input_resource_Delete( p_mi->p_input_resource );
423         p_mi->p_input_resource = NULL;    
424     }
425
426     libvlc_event_manager_release( p_mi->p_event_manager );
427     libvlc_media_release( p_mi->p_md );
428     vlc_mutex_destroy( &p_mi->object_lock );
429     free( p_mi );
430 }
431
432 /**************************************************************************
433  * Release a Media Instance object.
434  *
435  * Function does the locking.
436  **************************************************************************/
437 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
438 {
439     bool destroy;
440
441     assert( p_mi );
442     lock(p_mi);
443     destroy = !--p_mi->i_refcount;
444     unlock(p_mi);
445
446     if( destroy )
447         libvlc_media_player_destroy( p_mi );
448 }
449
450 /**************************************************************************
451  * Retain a Media Instance object.
452  *
453  * Caller must hold the lock.
454  **************************************************************************/
455 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
456 {
457     assert( p_mi );
458
459     lock(p_mi);
460     p_mi->i_refcount++;
461     unlock(p_mi);
462 }
463
464 /**************************************************************************
465  * Set the Media descriptor associated with the instance.
466  *
467  * Enter without lock -- function will lock the object.
468  **************************************************************************/
469 void libvlc_media_player_set_media(
470                             libvlc_media_player_t *p_mi,
471                             libvlc_media_t *p_md,
472                             libvlc_exception_t *p_e )
473 {
474     VLC_UNUSED(p_e);
475
476     if( !p_mi )
477         return;
478
479     lock(p_mi);
480
481     /* FIXME I am not sure if it is a user request or on die(eof/error)
482      * request here */
483     release_input_thread( p_mi,
484                           p_mi->p_input_thread &&
485                           !p_mi->p_input_thread->b_eof &&
486                           !p_mi->p_input_thread->b_error );
487
488     if( p_mi->p_md )
489         libvlc_media_set_state( p_mi->p_md, libvlc_NothingSpecial );
490
491     libvlc_media_release( p_mi->p_md );
492
493     if( !p_md )
494     {
495         p_mi->p_md = NULL;
496         unlock(p_mi);
497         return; /* It is ok to pass a NULL md */
498     }
499
500     libvlc_media_retain( p_md );
501     p_mi->p_md = p_md;
502
503     /* The policy here is to ignore that we were created using a different
504      * libvlc_instance, because we don't really care */
505     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
506
507     unlock(p_mi);
508 }
509
510 /**************************************************************************
511  * Get the Media descriptor associated with the instance.
512  **************************************************************************/
513 libvlc_media_t *
514 libvlc_media_player_get_media(
515                             libvlc_media_player_t *p_mi,
516                             libvlc_exception_t *p_e )
517 {
518     libvlc_media_t *p_m;
519     VLC_UNUSED(p_e);
520
521     lock(p_mi);
522     p_m = p_mi->p_md;
523     if( p_m )
524         libvlc_media_retain( p_mi->p_md );
525     unlock(p_mi);
526     return p_mi->p_md;
527 }
528
529 /**************************************************************************
530  * Get the event Manager.
531  **************************************************************************/
532 libvlc_event_manager_t *
533 libvlc_media_player_event_manager(
534                             libvlc_media_player_t *p_mi,
535                             libvlc_exception_t *p_e )
536 {
537     VLC_UNUSED(p_e);
538
539     return p_mi->p_event_manager;
540 }
541
542 /**************************************************************************
543  * Tell media player to start playing.
544  **************************************************************************/
545 void libvlc_media_player_play( libvlc_media_player_t *p_mi,
546                                  libvlc_exception_t *p_e )
547 {
548     input_thread_t * p_input_thread;
549
550     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
551     {
552         /* A thread already exists, send it a play message */
553         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
554         vlc_object_release( p_input_thread );
555         return;
556     }
557
558     /* Ignore previous exception */
559     libvlc_exception_clear( p_e );
560
561     lock(p_mi);
562
563     if( !p_mi->p_md )
564     {
565         unlock(p_mi);
566         libvlc_exception_raise( p_e );
567         libvlc_printerr( "No associated media descriptor" );
568         return;
569     }
570
571     p_mi->p_input_thread = input_Create( p_mi->p_libvlc_instance->p_libvlc_int,
572                                          p_mi->p_md->p_input_item, NULL, p_mi->p_input_resource );
573
574     if( !p_mi->p_input_thread )
575     {
576         unlock(p_mi);
577         return;
578     }
579
580     p_mi->p_input_resource = NULL;
581     p_input_thread = p_mi->p_input_thread;
582
583     var_Create( p_input_thread, "drawable-agl", VLC_VAR_INTEGER );
584     if( p_mi->drawable.agl )
585         var_SetInteger( p_input_thread, "drawable-agl", p_mi->drawable.agl );
586
587     var_Create( p_input_thread, "drawable-xid", VLC_VAR_INTEGER );
588     if( p_mi->drawable.xid )
589         var_SetInteger( p_input_thread, "drawable-xid", p_mi->drawable.xid );
590
591     var_Create( p_input_thread, "drawable-hwnd", VLC_VAR_ADDRESS );
592     if( p_mi->drawable.hwnd != NULL )
593         var_SetAddress( p_input_thread, "drawable-hwnd", p_mi->drawable.hwnd );
594
595     var_Create( p_input_thread, "drawable-nsobject", VLC_VAR_ADDRESS );
596     if( p_mi->drawable.nsobject != NULL )
597         var_SetAddress( p_input_thread, "drawable-nsobject", p_mi->drawable.nsobject );
598
599     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
600     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
601     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
602
603     if( input_Start( p_input_thread ) )
604     {
605         vlc_object_release( p_input_thread );
606         p_mi->p_input_thread = NULL;
607     }
608
609     unlock(p_mi);
610 }
611
612 /**************************************************************************
613  * Pause.
614  **************************************************************************/
615 void libvlc_media_player_pause( libvlc_media_player_t *p_mi,
616                                   libvlc_exception_t *p_e )
617 {
618     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
619     if( !p_input_thread )
620         return;
621
622     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
623     if( state == libvlc_Playing || state == libvlc_Buffering )
624     {
625         if( libvlc_media_player_can_pause( p_mi, p_e ) )
626             input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
627         else
628             libvlc_media_player_stop( p_mi, p_e );
629     }
630     else
631         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
632
633     vlc_object_release( p_input_thread );
634 }
635
636 /**************************************************************************
637  * Tells whether the media player is currently playing.
638  *
639  * Enter with lock held.
640  **************************************************************************/
641 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi,
642                                      libvlc_exception_t *p_e )
643 {
644     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
645     return (libvlc_Playing == state) || (libvlc_Buffering == state);
646 }
647
648 /**************************************************************************
649  * Stop playing.
650  **************************************************************************/
651 void libvlc_media_player_stop( libvlc_media_player_t *p_mi,
652                                  libvlc_exception_t *p_e )
653 {
654     libvlc_state_t state = libvlc_media_player_get_state( p_mi, p_e );
655
656     if( state == libvlc_Playing ||
657         state == libvlc_Paused ||
658         state == libvlc_Buffering )
659     {
660         /* Send a stop notification event only if we are in playing,
661          * buffering or paused states */
662         libvlc_media_set_state( p_mi->p_md, libvlc_Stopped );
663
664         /* Construct and send the event */
665         libvlc_event_t event;
666         event.type = libvlc_MediaPlayerStopped;
667         libvlc_event_send( p_mi->p_event_manager, &event );
668     }
669
670     lock(p_mi);
671     release_input_thread( p_mi, true ); /* This will stop the input thread */
672     unlock(p_mi);
673 }
674
675 /**************************************************************************
676  * set_nsobject
677  **************************************************************************/
678 void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
679                                         void * drawable,
680                                         libvlc_exception_t *p_e )
681 {
682     (void) p_e;
683     p_mi->drawable.nsobject = drawable;
684 }
685
686 /**************************************************************************
687  * get_nsobject
688  **************************************************************************/
689 void * libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
690 {
691     return p_mi->drawable.nsobject;
692 }
693
694 /**************************************************************************
695  * set_agl
696  **************************************************************************/
697 void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
698                                       uint32_t drawable,
699                                       libvlc_exception_t *p_e )
700 {
701     (void) p_e;
702     p_mi->drawable.agl = drawable;
703 }
704
705 /**************************************************************************
706  * get_agl
707  **************************************************************************/
708 uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
709 {
710     return p_mi->drawable.agl;
711 }
712
713 /**************************************************************************
714  * set_xwindow
715  **************************************************************************/
716 void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
717                                       uint32_t drawable,
718                                       libvlc_exception_t *p_e )
719 {
720     (void) p_e;
721     p_mi->drawable.xid = drawable;
722 }
723
724 /**************************************************************************
725  * get_xwindow
726  **************************************************************************/
727 uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
728 {
729     return p_mi->drawable.xid;
730 }
731
732 /**************************************************************************
733  * set_hwnd
734  **************************************************************************/
735 void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
736                                    void *drawable,
737                                    libvlc_exception_t *p_e )
738 {
739     (void) p_e;
740     p_mi->drawable.hwnd = drawable;
741 }
742
743 /**************************************************************************
744  * get_hwnd
745  **************************************************************************/
746 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
747 {
748     return p_mi->drawable.hwnd;
749 }
750
751 /**************************************************************************
752  * Getters for stream information
753  **************************************************************************/
754 libvlc_time_t libvlc_media_player_get_length(
755                              libvlc_media_player_t *p_mi,
756                              libvlc_exception_t *p_e )
757 {
758     input_thread_t *p_input_thread;
759     libvlc_time_t i_time;
760
761     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
762     if( !p_input_thread )
763         return -1;
764
765     i_time = var_GetTime( p_input_thread, "length" );
766     vlc_object_release( p_input_thread );
767
768     return (i_time+500LL)/1000LL;
769 }
770
771 libvlc_time_t libvlc_media_player_get_time(
772                                    libvlc_media_player_t *p_mi,
773                                    libvlc_exception_t *p_e )
774 {
775     input_thread_t *p_input_thread;
776     libvlc_time_t i_time;
777
778     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
779     if( !p_input_thread )
780         return -1;
781
782     i_time = var_GetTime( p_input_thread , "time" );
783     vlc_object_release( p_input_thread );
784     return (i_time+500LL)/1000LL;
785 }
786
787 void libvlc_media_player_set_time(
788                                  libvlc_media_player_t *p_mi,
789                                  libvlc_time_t i_time,
790                                  libvlc_exception_t *p_e )
791 {
792     input_thread_t *p_input_thread;
793
794     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
795     if( !p_input_thread )
796         return;
797
798     var_SetTime( p_input_thread, "time", i_time*1000LL );
799     vlc_object_release( p_input_thread );
800 }
801
802 void libvlc_media_player_set_position(
803                                 libvlc_media_player_t *p_mi,
804                                 float position,
805                                 libvlc_exception_t *p_e )
806 {
807     input_thread_t *p_input_thread;
808
809     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
810     if( !p_input_thread )
811         return;
812
813     var_SetFloat( p_input_thread, "position", position );
814     vlc_object_release( p_input_thread );
815 }
816
817 float libvlc_media_player_get_position(
818                                  libvlc_media_player_t *p_mi,
819                                  libvlc_exception_t *p_e )
820 {
821     input_thread_t *p_input_thread;
822     float f_position;
823
824     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
825     if( !p_input_thread )
826         return -1.0;
827
828     f_position = var_GetFloat( p_input_thread, "position" );
829     vlc_object_release( p_input_thread );
830
831     return f_position;
832 }
833
834 void libvlc_media_player_set_chapter(
835                                  libvlc_media_player_t *p_mi,
836                                  int chapter,
837                                  libvlc_exception_t *p_e )
838 {
839     input_thread_t *p_input_thread;
840
841     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
842     if( !p_input_thread )
843         return;
844
845     var_SetInteger( p_input_thread, "chapter", chapter );
846     vlc_object_release( p_input_thread );
847 }
848
849 int libvlc_media_player_get_chapter(
850                                  libvlc_media_player_t *p_mi,
851                                  libvlc_exception_t *p_e )
852 {
853     input_thread_t *p_input_thread;
854     int i_chapter;
855
856     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
857     if( !p_input_thread )
858         return -1;
859
860     i_chapter = var_GetInteger( p_input_thread, "chapter" );
861     vlc_object_release( p_input_thread );
862
863     return i_chapter;
864 }
865
866 int libvlc_media_player_get_chapter_count(
867                                  libvlc_media_player_t *p_mi,
868                                  libvlc_exception_t *p_e )
869 {
870     input_thread_t *p_input_thread;
871     vlc_value_t val;
872
873     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
874     if( !p_input_thread )
875         return -1;
876
877     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
878     vlc_object_release( p_input_thread );
879
880     return val.i_int;
881 }
882
883 int libvlc_media_player_get_chapter_count_for_title(
884                                  libvlc_media_player_t *p_mi,
885                                  int i_title,
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         return -1;
894
895     char *psz_name;
896     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
897     {
898         vlc_object_release( p_input_thread );
899         return -1;
900     }
901     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
902     vlc_object_release( p_input_thread );
903     free( psz_name );
904
905     return val.i_int;
906 }
907
908 void libvlc_media_player_set_title(
909                                  libvlc_media_player_t *p_mi,
910                                  int i_title,
911                                  libvlc_exception_t *p_e )
912 {
913     input_thread_t *p_input_thread;
914
915     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
916     if( !p_input_thread )
917         return;
918
919     var_SetInteger( p_input_thread, "title", i_title );
920     vlc_object_release( p_input_thread );
921
922     //send event
923     libvlc_event_t event;
924     event.type = libvlc_MediaPlayerTitleChanged;
925     event.u.media_player_title_changed.new_title = i_title;
926     libvlc_event_send( p_mi->p_event_manager, &event );
927 }
928
929 int libvlc_media_player_get_title(
930                                  libvlc_media_player_t *p_mi,
931                                  libvlc_exception_t *p_e )
932 {
933     input_thread_t *p_input_thread;
934     int i_title;
935
936     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
937     if( !p_input_thread )
938         return -1;
939
940     i_title = var_GetInteger( p_input_thread, "title" );
941     vlc_object_release( p_input_thread );
942
943     return i_title;
944 }
945
946 int libvlc_media_player_get_title_count(
947                                  libvlc_media_player_t *p_mi,
948                                  libvlc_exception_t *p_e )
949 {
950     input_thread_t *p_input_thread;
951     vlc_value_t val;
952
953     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
954     if( !p_input_thread )
955         return -1;
956
957     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
958     vlc_object_release( p_input_thread );
959
960     return val.i_int;
961 }
962
963 void libvlc_media_player_next_chapter(
964                                  libvlc_media_player_t *p_mi,
965                                  libvlc_exception_t *p_e )
966 {
967     input_thread_t *p_input_thread;
968
969     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
970     if( !p_input_thread )
971         return;
972
973     int i_type = var_Type( p_input_thread, "next-chapter" );
974     var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
975                             "next-chapter":"next-title", true );
976
977     vlc_object_release( p_input_thread );
978 }
979
980 void libvlc_media_player_previous_chapter(
981                                  libvlc_media_player_t *p_mi,
982                                  libvlc_exception_t *p_e )
983 {
984     input_thread_t *p_input_thread;
985
986     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
987     if( !p_input_thread )
988         return;
989
990     int i_type = var_Type( p_input_thread, "next-chapter" );
991     var_SetBool( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
992                             "prev-chapter":"prev-title", true );
993
994     vlc_object_release( p_input_thread );
995 }
996
997 float libvlc_media_player_get_fps(
998                                  libvlc_media_player_t *p_mi,
999                                  libvlc_exception_t *p_e)
1000 {
1001     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1002     double f_fps = 0.0;
1003
1004     if( p_input_thread )
1005     {
1006         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1007             f_fps = 0.0;
1008         vlc_object_release( p_input_thread );
1009     }
1010     return f_fps;
1011 }
1012
1013 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi,
1014                                      libvlc_exception_t *p_e)
1015 {
1016     input_thread_t *p_input_thread =
1017                             libvlc_get_input_thread ( p_mi, p_e);
1018     if ( !p_input_thread )
1019         return false;
1020
1021     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
1022     {
1023         vlc_object_release( p_input_thread );
1024         return true;
1025     }
1026     vlc_object_release( p_input_thread );
1027     return false;
1028 }
1029
1030 void libvlc_media_player_set_rate(
1031                                  libvlc_media_player_t *p_mi,
1032                                  float rate,
1033                                  libvlc_exception_t *p_e )
1034 {
1035     input_thread_t *p_input_thread;
1036     bool b_can_rewind;
1037
1038     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1039     if( !p_input_thread )
1040         return;
1041
1042     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1043     if( (rate < 0.0) && !b_can_rewind )
1044     {
1045         vlc_object_release( p_input_thread );
1046         libvlc_exception_raise( p_e );
1047         libvlc_printerr( "Invalid playback rate" );
1048         return;
1049     }
1050
1051     var_SetInteger( p_input_thread, "rate", 1000.0f/rate );
1052     vlc_object_release( p_input_thread );
1053 }
1054
1055 float libvlc_media_player_get_rate(
1056                                  libvlc_media_player_t *p_mi,
1057                                  libvlc_exception_t *p_e )
1058 {
1059     input_thread_t *p_input_thread;
1060     int i_rate;
1061     bool b_can_rewind;
1062
1063     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1064     if( !p_input_thread )
1065         return 0.0;  /* rate < 0 indicates rewind */
1066
1067     i_rate = var_GetInteger( p_input_thread, "rate" );
1068     b_can_rewind = var_GetBool( p_input_thread, "can-rewind" );
1069     if( i_rate < 0 && !b_can_rewind )
1070     {
1071         vlc_object_release( p_input_thread );
1072         return 0.0;
1073     }
1074     vlc_object_release( p_input_thread );
1075
1076     return (float)1000.0f/i_rate;
1077 }
1078
1079 libvlc_state_t libvlc_media_player_get_state(
1080                                  libvlc_media_player_t *p_mi,
1081                                  libvlc_exception_t *p_e )
1082 {
1083     input_thread_t *p_input_thread;
1084     libvlc_state_t state = libvlc_Ended;
1085
1086     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1087     if( !p_input_thread )
1088     {
1089         /* We do return the right value, no need to throw an exception */
1090         clear_if_needed(p_e);
1091         return state;
1092     }
1093
1094     state = libvlc_media_get_state( p_mi->p_md );
1095     if( state == libvlc_Playing )
1096     {
1097         float caching;
1098         caching = var_GetFloat( p_input_thread, "cache" );
1099         if( caching > 0.0 && caching < 1.0 )
1100             state = libvlc_Buffering;
1101     }
1102     vlc_object_release( p_input_thread );
1103     return state;
1104 }
1105
1106 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi,
1107                                        libvlc_exception_t *p_e )
1108 {
1109     input_thread_t *p_input_thread;
1110     bool b_seekable;
1111
1112     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1113     if ( !p_input_thread )
1114     {
1115         /* We do return the right value, no need to throw an exception */
1116         clear_if_needed(p_e);
1117         return false;
1118     }
1119     b_seekable = var_GetBool( p_input_thread, "can-seek" );
1120     vlc_object_release( p_input_thread );
1121
1122     return b_seekable;
1123 }
1124
1125 /* internal function, used by audio, video */
1126 libvlc_track_description_t *
1127         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1128                                       const char *psz_variable,
1129                                       libvlc_exception_t *p_e )
1130 {
1131     input_thread_t *p_input = libvlc_get_input_thread( p_mi, p_e );
1132     libvlc_track_description_t *p_track_description = NULL,
1133                                *p_actual, *p_previous;
1134
1135     if( !p_input )
1136         return NULL;
1137
1138     vlc_value_t val_list, text_list;
1139     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1140
1141     /* no tracks */
1142     if( val_list.p_list->i_count <= 0 )
1143         goto end;
1144
1145     p_track_description = ( libvlc_track_description_t * )
1146         malloc( sizeof( libvlc_track_description_t ) );
1147     if ( !p_track_description )
1148     {
1149         libvlc_exception_raise( p_e );
1150         libvlc_printerr( "Not enough memory" );
1151         goto end;
1152     }
1153     p_actual = p_track_description;
1154     p_previous = NULL;
1155     for( int i = 0; i < val_list.p_list->i_count; i++ )
1156     {
1157         if( !p_actual )
1158         {
1159             p_actual = ( libvlc_track_description_t * )
1160                 malloc( sizeof( libvlc_track_description_t ) );
1161             if ( !p_actual )
1162             {
1163                 libvlc_track_description_release( p_track_description );
1164                 libvlc_exception_raise( p_e );
1165                 libvlc_printerr( "Not enough memory" );
1166                 goto end;
1167             }
1168         }
1169         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1170         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1171         p_actual->p_next = NULL;
1172         if( p_previous )
1173             p_previous->p_next = p_actual;
1174         p_previous = p_actual;
1175         p_actual =  NULL;
1176     }
1177
1178 end:
1179     var_FreeList( &val_list, &text_list );
1180     vlc_object_release( p_input );
1181
1182     return p_track_description;
1183 }
1184
1185 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1186 {
1187     libvlc_track_description_t *p_actual, *p_before;
1188     p_actual = p_td;
1189
1190     while ( p_actual )
1191     {
1192         free( p_actual->psz_name );
1193         p_before = p_actual;
1194         p_actual = p_before->p_next;
1195         free( p_before );
1196     }
1197 }
1198
1199 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi,
1200                                      libvlc_exception_t *p_e )
1201 {
1202     input_thread_t *p_input_thread;
1203     bool b_can_pause;
1204
1205     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1206     if ( !p_input_thread )
1207     {
1208         /* We do return the right value, no need to throw an exception */
1209         clear_if_needed(p_e);
1210         return false;
1211     }
1212     b_can_pause = var_GetBool( p_input_thread, "can-pause" );
1213     vlc_object_release( p_input_thread );
1214
1215     return b_can_pause;
1216 }
1217
1218 void libvlc_media_player_next_frame( libvlc_media_player_t *p_mi, libvlc_exception_t *p_e )
1219 {
1220     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
1221     if( p_input_thread != NULL )
1222     {
1223         var_TriggerCallback( p_input_thread, "frame-next" );
1224         vlc_object_release( p_input_thread );
1225     }
1226     else
1227     {
1228         libvlc_exception_raise( p_e );
1229         libvlc_printerr( "No active input" );
1230     }
1231 }