]> git.sesse.net Git - vlc/blob - lib/media_player.c
Register libvlc_MediaPlayerVout event.
[vlc] / lib / 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 #include <vlc_keys.h>
38
39 #include "libvlc_internal.h"
40 #include "media_internal.h" // libvlc_media_set_state()
41 #include "media_player_internal.h"
42
43 /*
44  * mapping of libvlc_navigate_mode_t to vlc_action_t
45  */
46 static const vlc_action_t libvlc_navigate_to_action[] =
47 {
48     ACTIONID_NAV_ACTIVATE,
49     ACTIONID_NAV_UP,
50     ACTIONID_NAV_DOWN,
51     ACTIONID_NAV_LEFT,
52     ACTIONID_NAV_RIGHT
53 };
54
55 static const uint32_t libvlc_navigate_to_action_size =                  \
56   sizeof( libvlc_navigate_to_action ) / sizeof( libvlc_navigate_to_action[0] );
57
58
59 static int
60 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
61                         vlc_value_t oldval, vlc_value_t newval,
62                         void * p_userdata );
63 static int
64 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
65                         vlc_value_t oldval, vlc_value_t newval,
66                         void * p_userdata );
67 static int
68 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
69                      vlc_value_t oldval, vlc_value_t newval,
70                      void * p_userdata );
71
72 static int
73 snapshot_was_taken( vlc_object_t *p_this, char const *psz_cmd,
74                     vlc_value_t oldval, vlc_value_t newval, void *p_data );
75
76 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi );
77
78 /*
79  * Shortcuts
80  */
81
82 #define register_event(a, b) __register_event(a, libvlc_MediaPlayer ## b)
83 static inline void __register_event(libvlc_media_player_t *mp, libvlc_event_type_t type)
84 {
85     libvlc_event_manager_register_event_type(mp->p_event_manager, type);
86 }
87
88 /*
89  * The input lock protects the input and input resource pointer.
90  * It MUST NOT be used from callbacks.
91  *
92  * The object lock protects the reset, namely the media and the player state.
93  * It can, and usually needs to be taken from callbacks.
94  * The object lock can be acquired under the input lock... and consequently
95  * the opposite order is STRICTLY PROHIBITED.
96  */
97 static inline void lock(libvlc_media_player_t *mp)
98 {
99     vlc_mutex_lock(&mp->object_lock);
100 }
101
102 static inline void unlock(libvlc_media_player_t *mp)
103 {
104     vlc_mutex_unlock(&mp->object_lock);
105 }
106
107 static inline void lock_input(libvlc_media_player_t *mp)
108 {
109     vlc_mutex_lock(&mp->input.lock);
110 }
111
112 static inline void unlock_input(libvlc_media_player_t *mp)
113 {
114     vlc_mutex_unlock(&mp->input.lock);
115 }
116
117 /*
118  * Release the associated input thread.
119  *
120  * Object lock is NOT held.
121  * Input lock is held or instance is being destroyed.
122  */
123 static void release_input_thread( libvlc_media_player_t *p_mi, bool b_input_abort )
124 {
125     assert( p_mi );
126
127     input_thread_t *p_input_thread = p_mi->input.p_thread;
128     if( !p_input_thread )
129         return;
130     p_mi->input.p_thread = NULL;
131
132     var_DelCallback( p_input_thread, "can-seek",
133                      input_seekable_changed, p_mi );
134     var_DelCallback( p_input_thread, "can-pause",
135                     input_pausable_changed, p_mi );
136     var_DelCallback( p_input_thread, "intf-event",
137                      input_event_changed, p_mi );
138
139     /* We owned this one */
140     input_Stop( p_input_thread, b_input_abort );
141     input_Close( p_input_thread );
142 }
143
144 /*
145  * Retrieve the input thread. Be sure to release the object
146  * once you are done with it. (libvlc Internal)
147  */
148 input_thread_t *libvlc_get_input_thread( libvlc_media_player_t *p_mi )
149 {
150     input_thread_t *p_input_thread;
151
152     assert( p_mi );
153
154     lock_input(p_mi);
155     p_input_thread = p_mi->input.p_thread;
156     if( p_input_thread )
157         vlc_object_hold( p_input_thread );
158     else
159         libvlc_printerr( "No active input" );
160     unlock_input(p_mi);
161
162     return p_input_thread;
163 }
164
165 /*
166  * Set the internal state of the media_player. (media player Internal)
167  *
168  * Function will lock the media_player.
169  */
170 static void set_state( libvlc_media_player_t *p_mi, libvlc_state_t state,
171     bool b_locked )
172 {
173     if(!b_locked)
174         lock(p_mi);
175     p_mi->state = state;
176
177     libvlc_media_t *media = p_mi->p_md;
178     if (media)
179         libvlc_media_retain(media);
180
181     if(!b_locked)
182         unlock(p_mi);
183
184     if (media)
185     {
186         // Also set the state of the corresponding media
187         // This is strictly for convenience.
188         libvlc_media_set_state(media, state);
189
190         libvlc_media_release(media);
191     }
192 }
193
194 static int
195 input_seekable_changed( vlc_object_t * p_this, char const * psz_cmd,
196                         vlc_value_t oldval, vlc_value_t newval,
197                         void * p_userdata )
198 {
199     VLC_UNUSED(oldval);
200     VLC_UNUSED(p_this);
201     VLC_UNUSED(psz_cmd);
202     libvlc_media_player_t * p_mi = p_userdata;
203     libvlc_event_t event;
204
205     event.type = libvlc_MediaPlayerSeekableChanged;
206     event.u.media_player_seekable_changed.new_seekable = newval.b_bool;
207
208     libvlc_event_send( p_mi->p_event_manager, &event );
209     return VLC_SUCCESS;
210 }
211
212 static int
213 input_pausable_changed( vlc_object_t * p_this, char const * psz_cmd,
214                         vlc_value_t oldval, vlc_value_t newval,
215                         void * p_userdata )
216 {
217     VLC_UNUSED(oldval);
218     VLC_UNUSED(p_this);
219     VLC_UNUSED(psz_cmd);
220     libvlc_media_player_t * p_mi = p_userdata;
221     libvlc_event_t event;
222
223     event.type = libvlc_MediaPlayerPausableChanged;
224     event.u.media_player_pausable_changed.new_pausable = newval.b_bool;
225
226     libvlc_event_send( p_mi->p_event_manager, &event );
227     return VLC_SUCCESS;
228 }
229
230 static int
231 input_event_changed( vlc_object_t * p_this, char const * psz_cmd,
232                      vlc_value_t oldval, vlc_value_t newval,
233                      void * p_userdata )
234 {
235     VLC_UNUSED(oldval);
236     input_thread_t * p_input = (input_thread_t *)p_this;
237     libvlc_media_player_t * p_mi = p_userdata;
238     libvlc_event_t event;
239
240     assert( !strcmp( psz_cmd, "intf-event" ) );
241
242     if( newval.i_int == INPUT_EVENT_STATE )
243     {
244         libvlc_state_t libvlc_state;
245
246         switch ( var_GetInteger( p_input, "state" ) )
247         {
248             case INIT_S:
249                 libvlc_state = libvlc_NothingSpecial;
250                 event.type = libvlc_MediaPlayerNothingSpecial;
251                 break;
252             case OPENING_S:
253                 libvlc_state = libvlc_Opening;
254                 event.type = libvlc_MediaPlayerOpening;
255                 break;
256             case PLAYING_S:
257                 libvlc_state = libvlc_Playing;
258                 event.type = libvlc_MediaPlayerPlaying;
259                 break;
260             case PAUSE_S:
261                 libvlc_state = libvlc_Paused;
262                 event.type = libvlc_MediaPlayerPaused;
263                 break;
264             case END_S:
265                 libvlc_state = libvlc_Ended;
266                 event.type = libvlc_MediaPlayerEndReached;
267                 break;
268             case ERROR_S:
269                 libvlc_state = libvlc_Error;
270                 event.type = libvlc_MediaPlayerEncounteredError;
271                 break;
272
273             default:
274                 return VLC_SUCCESS;
275         }
276
277         set_state( p_mi, libvlc_state, false );
278         libvlc_event_send( p_mi->p_event_manager, &event );
279     }
280     else if( newval.i_int == INPUT_EVENT_ABORT )
281     {
282         libvlc_state_t libvlc_state = libvlc_Stopped;
283         event.type = libvlc_MediaPlayerStopped;
284
285         set_state( p_mi, libvlc_state, false );
286         libvlc_event_send( p_mi->p_event_manager, &event );
287     }
288     else if( newval.i_int == INPUT_EVENT_POSITION )
289     {
290         if( var_GetInteger( p_input, "state" ) != PLAYING_S )
291             return VLC_SUCCESS; /* Don't send the position while stopped */
292
293         /* */
294         event.type = libvlc_MediaPlayerPositionChanged;
295         event.u.media_player_position_changed.new_position =
296                                           var_GetFloat( p_input, "position" );
297         libvlc_event_send( p_mi->p_event_manager, &event );
298
299         /* */
300         event.type = libvlc_MediaPlayerTimeChanged;
301         event.u.media_player_time_changed.new_time =
302            from_mtime(var_GetTime( p_input, "time" ));
303         libvlc_event_send( p_mi->p_event_manager, &event );
304     }
305     else if( newval.i_int == INPUT_EVENT_LENGTH )
306     {
307         event.type = libvlc_MediaPlayerLengthChanged;
308         event.u.media_player_length_changed.new_length =
309            from_mtime(var_GetTime( p_input, "length" ));
310         libvlc_event_send( p_mi->p_event_manager, &event );
311     }
312     else if( newval.i_int == INPUT_EVENT_CACHE )
313     {
314         event.type = libvlc_MediaPlayerBuffering;
315         event.u.media_player_buffering.new_cache = (int)(100 *
316             var_GetFloat( p_input, "cache" ));
317         libvlc_event_send( p_mi->p_event_manager, &event );
318     }
319     else if( newval.i_int == INPUT_EVENT_VOUT )
320     {
321         vout_thread_t **pp_vout;
322         size_t i_vout;
323         if( input_Control( p_input, INPUT_GET_VOUTS, &pp_vout, &i_vout ) )
324         {
325             i_vout  = 0;
326         }
327         else
328         {
329             for( size_t i = 0; i < i_vout; i++ )
330                 vlc_object_release( pp_vout[i] );
331             free( pp_vout );
332         }
333
334         event.type = libvlc_MediaPlayerVout;
335         event.u.media_player_vout.new_count = i_vout;
336         libvlc_event_send( p_mi->p_event_manager, &event );
337     }
338
339     return VLC_SUCCESS;
340 }
341
342 /**************************************************************************
343  * Snapshot Taken Event.
344  *
345  * FIXME: This snapshot API interface makes no sense in media_player.
346  *************************************************************************/
347 static int snapshot_was_taken(vlc_object_t *p_this, char const *psz_cmd,
348                               vlc_value_t oldval, vlc_value_t newval, void *p_data )
349 {
350     VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_this);
351
352     libvlc_media_player_t *mp = p_data;
353     libvlc_event_t event;
354     event.type = libvlc_MediaPlayerSnapshotTaken;
355     event.u.media_player_snapshot_taken.psz_filename = newval.psz_string;
356     libvlc_event_send(mp->p_event_manager, &event);
357
358     return VLC_SUCCESS;
359 }
360
361 static input_thread_t *find_input (vlc_object_t *obj)
362 {
363     libvlc_media_player_t *mp = (libvlc_media_player_t *)obj;
364
365     return libvlc_get_input_thread (mp);
366 }
367
368 /* */
369 static void libvlc_media_player_destroy( libvlc_media_player_t * );
370
371
372 /**************************************************************************
373  * Create a Media Instance object.
374  *
375  * Refcount strategy:
376  * - All items created by _new start with a refcount set to 1.
377  * - Accessor _release decrease the refcount by 1, if after that
378  *   operation the refcount is 0, the object is destroyed.
379  * - Accessor _retain increase the refcount by 1 (XXX: to implement)
380  *
381  * Object locking strategy:
382  * - No lock held while in constructor.
383  * - When accessing any member variable this lock is held. (XXX who locks?)
384  * - When attempting to destroy the object the lock is also held.
385  **************************************************************************/
386 libvlc_media_player_t *
387 libvlc_media_player_new( libvlc_instance_t *instance )
388 {
389     libvlc_media_player_t * mp;
390
391     assert(instance);
392
393     mp = vlc_object_create (instance->p_libvlc_int, sizeof(*mp));
394     if (unlikely(mp == NULL))
395     {
396         libvlc_printerr("Not enough memory");
397         return NULL;
398     }
399
400     /* Input */
401     var_Create (mp, "rate", VLC_VAR_FLOAT|VLC_VAR_DOINHERIT);
402
403     /* Video */
404     var_Create (mp, "vout", VLC_VAR_STRING|VLC_VAR_DOINHERIT);
405     var_Create (mp, "window", VLC_VAR_STRING);
406     var_Create (mp, "vmem-lock", VLC_VAR_ADDRESS);
407     var_Create (mp, "vmem-unlock", VLC_VAR_ADDRESS);
408     var_Create (mp, "vmem-display", VLC_VAR_ADDRESS);
409     var_Create (mp, "vmem-data", VLC_VAR_ADDRESS);
410     var_Create (mp, "vmem-setup", VLC_VAR_ADDRESS);
411     var_Create (mp, "vmem-cleanup", VLC_VAR_ADDRESS);
412     var_Create (mp, "vmem-chroma", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
413     var_Create (mp, "vmem-width", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
414     var_Create (mp, "vmem-height", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
415     var_Create (mp, "vmem-pitch", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
416     var_Create (mp, "drawable-xid", VLC_VAR_INTEGER);
417 #ifdef WIN32
418     var_Create (mp, "drawable-hwnd", VLC_VAR_INTEGER);
419 #endif
420 #ifdef __APPLE__
421     var_Create (mp, "drawable-agl", VLC_VAR_INTEGER);
422     var_Create (mp, "drawable-nsobject", VLC_VAR_ADDRESS);
423 #endif
424
425     var_Create (mp, "keyboard-events", VLC_VAR_BOOL);
426     var_SetBool (mp, "keyboard-events", true);
427     var_Create (mp, "mouse-events", VLC_VAR_BOOL);
428     var_SetBool (mp, "mouse-events", true);
429
430     var_Create (mp, "fullscreen", VLC_VAR_BOOL);
431     var_Create (mp, "autoscale", VLC_VAR_BOOL);
432     var_SetBool (mp, "autoscale", true);
433     var_Create (mp, "scale", VLC_VAR_FLOAT);
434     var_SetFloat (mp, "scale", 1.);
435     var_Create (mp, "aspect-ratio", VLC_VAR_STRING);
436     var_Create (mp, "crop", VLC_VAR_STRING);
437     var_Create (mp, "deinterlace", VLC_VAR_INTEGER);
438     var_Create (mp, "deinterlace-mode", VLC_VAR_STRING);
439
440     var_Create (mp, "vbi-page", VLC_VAR_INTEGER);
441
442     var_Create (mp, "marq-marquee", VLC_VAR_STRING);
443     var_Create (mp, "marq-color", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
444     var_Create (mp, "marq-opacity", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
445     var_Create (mp, "marq-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
446     var_Create (mp, "marq-refresh", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
447     var_Create (mp, "marq-size", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
448     var_Create (mp, "marq-timeout", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
449     var_Create (mp, "marq-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
450     var_Create (mp, "marq-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
451
452     var_Create (mp, "logo-file", VLC_VAR_STRING);
453     var_Create (mp, "logo-x", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
454     var_Create (mp, "logo-y", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
455     var_Create (mp, "logo-delay", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
456     var_Create (mp, "logo-repeat", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
457     var_Create (mp, "logo-opacity", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
458     var_Create (mp, "logo-position", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
459
460     var_Create (mp, "contrast", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
461     var_Create (mp, "brightness", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
462     var_Create (mp, "hue", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
463     var_Create (mp, "saturation", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
464     var_Create (mp, "gamma", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT);
465
466      /* Audio */
467     var_Create (mp, "aout", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
468     var_Create (mp, "mute", VLC_VAR_BOOL);
469     var_Create (mp, "volume", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
470     var_Create (mp, "find-input-callback", VLC_VAR_ADDRESS);
471     var_SetAddress (mp, "find-input-callback", find_input);
472     var_Create (mp, "amem-data", VLC_VAR_ADDRESS);
473     var_Create (mp, "amem-setup", VLC_VAR_ADDRESS);
474     var_Create (mp, "amem-close", VLC_VAR_ADDRESS);
475     var_Create (mp, "amem-play", VLC_VAR_ADDRESS);
476     var_Create (mp, "amem-pause", VLC_VAR_ADDRESS);
477     var_Create (mp, "amem-resume", VLC_VAR_ADDRESS);
478     var_Create (mp, "amem-flush", VLC_VAR_ADDRESS);
479     var_Create (mp, "amem-drain", VLC_VAR_ADDRESS);
480     var_Create (mp, "amem-set-volume", VLC_VAR_ADDRESS);
481     var_Create (mp, "amem-format", VLC_VAR_STRING | VLC_VAR_DOINHERIT);
482     var_Create (mp, "amem-rate", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
483     var_Create (mp, "amem-channels", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
484
485     mp->p_md = NULL;
486     mp->state = libvlc_NothingSpecial;
487     mp->p_libvlc_instance = instance;
488     mp->input.p_thread = NULL;
489     mp->input.p_resource = NULL;
490     vlc_mutex_init (&mp->input.lock);
491     mp->i_refcount = 1;
492     mp->p_event_manager = libvlc_event_manager_new(mp, instance);
493     if (unlikely(mp->p_event_manager == NULL))
494     {
495         vlc_object_release(mp);
496         return NULL;
497     }
498     vlc_mutex_init(&mp->object_lock);
499
500     register_event(mp, NothingSpecial);
501     register_event(mp, Opening);
502     register_event(mp, Buffering);
503     register_event(mp, Playing);
504     register_event(mp, Paused);
505     register_event(mp, Stopped);
506     register_event(mp, Forward);
507     register_event(mp, Backward);
508     register_event(mp, EndReached);
509     register_event(mp, EncounteredError);
510     register_event(mp, SeekableChanged);
511
512     register_event(mp, PositionChanged);
513     register_event(mp, TimeChanged);
514     register_event(mp, LengthChanged);
515     register_event(mp, TitleChanged);
516     register_event(mp, PausableChanged);
517
518     register_event(mp, Vout);
519
520     /* Snapshot initialization */
521     register_event(mp, SnapshotTaken);
522
523     register_event(mp, MediaChanged);
524
525     /* Attach a var callback to the global object to provide the glue between
526      * vout_thread that generates the event and media_player that re-emits it
527      * with its own event manager
528      *
529      * FIXME: It's unclear why we want to put this in public API, and why we
530      * want to expose it in such a limiting and ugly way.
531      */
532     var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
533
534     libvlc_retain(instance);
535     return mp;
536 }
537
538 /**************************************************************************
539  * Create a Media Instance object with a media descriptor.
540  **************************************************************************/
541 libvlc_media_player_t *
542 libvlc_media_player_new_from_media( libvlc_media_t * p_md )
543 {
544     libvlc_media_player_t * p_mi;
545
546     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
547     if( !p_mi )
548         return NULL;
549
550     libvlc_media_retain( p_md );
551     p_mi->p_md = p_md;
552
553     return p_mi;
554 }
555
556 /**************************************************************************
557  * Destroy a Media Instance object (libvlc internal)
558  *
559  * Warning: No lock held here, but hey, this is internal. Caller must lock.
560  **************************************************************************/
561 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
562 {
563     assert( p_mi );
564
565     /* Detach Callback from the main libvlc object */
566     var_DelCallback( p_mi->p_libvlc,
567                      "snapshot-file", snapshot_was_taken, p_mi );
568
569     /* No need for lock_input() because no other threads knows us anymore */
570     if( p_mi->input.p_thread )
571         release_input_thread(p_mi, true);
572     if( p_mi->input.p_resource )
573     {
574         input_resource_Terminate( p_mi->input.p_resource );
575         input_resource_Release( p_mi->input.p_resource );
576         p_mi->input.p_resource = NULL;
577     }
578     vlc_mutex_destroy( &p_mi->input.lock );
579
580     libvlc_event_manager_release( p_mi->p_event_manager );
581     libvlc_media_release( p_mi->p_md );
582     vlc_mutex_destroy( &p_mi->object_lock );
583
584     libvlc_instance_t *instance = p_mi->p_libvlc_instance;
585     vlc_object_release( p_mi );
586     libvlc_release(instance);
587 }
588
589 /**************************************************************************
590  * Release a Media Instance object.
591  *
592  * Function does the locking.
593  **************************************************************************/
594 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
595 {
596     bool destroy;
597
598     assert( p_mi );
599     lock(p_mi);
600     destroy = !--p_mi->i_refcount;
601     unlock(p_mi);
602
603     if( destroy )
604         libvlc_media_player_destroy( p_mi );
605 }
606
607 /**************************************************************************
608  * Retain a Media Instance object.
609  *
610  * Caller must hold the lock.
611  **************************************************************************/
612 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
613 {
614     assert( p_mi );
615
616     lock(p_mi);
617     p_mi->i_refcount++;
618     unlock(p_mi);
619 }
620
621 /**************************************************************************
622  * Set the Media descriptor associated with the instance.
623  *
624  * Enter without lock -- function will lock the object.
625  **************************************************************************/
626 void libvlc_media_player_set_media(
627                             libvlc_media_player_t *p_mi,
628                             libvlc_media_t *p_md )
629 {
630     lock_input(p_mi);
631
632     /* FIXME I am not sure if it is a user request or on die(eof/error)
633      * request here */
634     release_input_thread( p_mi,
635                           p_mi->input.p_thread &&
636                           !p_mi->input.p_thread->b_eof &&
637                           !p_mi->input.p_thread->b_error );
638
639     lock( p_mi );
640     set_state( p_mi, libvlc_NothingSpecial, true );
641     unlock_input( p_mi );
642
643     libvlc_media_release( p_mi->p_md );
644
645     if( !p_md )
646     {
647         p_mi->p_md = NULL;
648         unlock(p_mi);
649         return; /* It is ok to pass a NULL md */
650     }
651
652     libvlc_media_retain( p_md );
653     p_mi->p_md = p_md;
654
655     /* The policy here is to ignore that we were created using a different
656      * libvlc_instance, because we don't really care */
657     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
658
659     unlock(p_mi);
660
661     /* Send an event for the newly available media */
662     libvlc_event_t event;
663     event.type = libvlc_MediaPlayerMediaChanged;
664     event.u.media_player_media_changed.new_media = p_md;
665     libvlc_event_send( p_mi->p_event_manager, &event );
666
667 }
668
669 /**************************************************************************
670  * Get the Media descriptor associated with the instance.
671  **************************************************************************/
672 libvlc_media_t *
673 libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
674 {
675     libvlc_media_t *p_m;
676
677     lock(p_mi);
678     p_m = p_mi->p_md;
679     if( p_m )
680         libvlc_media_retain( p_mi->p_md );
681     unlock(p_mi);
682     return p_mi->p_md;
683 }
684
685 /**************************************************************************
686  * Get the event Manager.
687  **************************************************************************/
688 libvlc_event_manager_t *
689 libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
690 {
691     return p_mi->p_event_manager;
692 }
693
694 /**************************************************************************
695  * Tell media player to start playing.
696  **************************************************************************/
697 int libvlc_media_player_play( libvlc_media_player_t *p_mi )
698 {
699     lock_input( p_mi );
700
701     input_thread_t *p_input_thread = p_mi->input.p_thread;
702     if( p_input_thread )
703     {
704         /* A thread already exists, send it a play message */
705         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
706         unlock_input( p_mi );
707         return 0;
708     }
709
710     /* Ignore previous exception */
711     lock(p_mi);
712
713     if( !p_mi->p_md )
714     {
715         unlock(p_mi);
716         unlock_input( p_mi );
717         libvlc_printerr( "No associated media descriptor" );
718         return -1;
719     }
720
721     if( !p_mi->input.p_resource )
722         p_mi->input.p_resource = input_resource_New( VLC_OBJECT( p_mi ) );
723     p_input_thread = input_Create( p_mi, p_mi->p_md->p_input_item, NULL,
724                                    p_mi->input.p_resource );
725     unlock(p_mi);
726     if( !p_input_thread )
727     {
728         unlock_input(p_mi);
729         libvlc_printerr( "Not enough memory" );
730         return -1;
731     }
732
733     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
734     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
735     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
736
737     if( input_Start( p_input_thread ) )
738     {
739         unlock_input(p_mi);
740         var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
741         var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
742         var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
743         vlc_object_release( p_input_thread );
744         libvlc_printerr( "Input initialization failure" );
745         return -1;
746     }
747     p_mi->input.p_thread = p_input_thread;
748     unlock_input(p_mi);
749     return 0;
750 }
751
752 void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
753 {
754     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
755     if( !p_input_thread )
756         return;
757
758     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
759     if( state == libvlc_Playing || state == libvlc_Buffering )
760     {
761         if( paused )
762         {
763             if( libvlc_media_player_can_pause( p_mi ) )
764                 input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
765             else
766                 libvlc_media_player_stop( p_mi );
767         }
768     }
769     else
770     {
771         if( !paused )
772             input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
773     }
774
775     vlc_object_release( p_input_thread );
776 }
777
778 /**************************************************************************
779  * Toggle pause.
780  **************************************************************************/
781 void libvlc_media_player_pause( libvlc_media_player_t *p_mi )
782 {
783     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
784     bool playing = (state == libvlc_Playing || state == libvlc_Buffering);
785
786     libvlc_media_player_set_pause( p_mi, playing );
787 }
788
789 /**************************************************************************
790  * Tells whether the media player is currently playing.
791  *
792  * Enter with lock held.
793  **************************************************************************/
794 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
795 {
796     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
797     return (libvlc_Playing == state) || (libvlc_Buffering == state);
798 }
799
800 /**************************************************************************
801  * Stop playing.
802  **************************************************************************/
803 void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
804 {
805     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
806
807     lock_input(p_mi);
808     release_input_thread( p_mi, true ); /* This will stop the input thread */
809
810     /* Force to go to stopped state, in case we were in Ended, or Error
811      * state. */
812     if( state != libvlc_Stopped )
813     {
814         set_state( p_mi, libvlc_Stopped, false );
815
816         /* Construct and send the event */
817         libvlc_event_t event;
818         event.type = libvlc_MediaPlayerStopped;
819         libvlc_event_send( p_mi->p_event_manager, &event );
820     }
821
822     if( p_mi->input.p_resource != NULL )
823         input_resource_Terminate( p_mi->input.p_resource );
824     unlock_input(p_mi);
825 }
826
827
828 void libvlc_video_set_callbacks( libvlc_media_player_t *mp,
829     void *(*lock_cb) (void *, void **),
830     void (*unlock_cb) (void *, void *, void *const *),
831     void (*display_cb) (void *, void *),
832     void *opaque )
833 {
834     var_SetAddress( mp, "vmem-lock", lock_cb );
835     var_SetAddress( mp, "vmem-unlock", unlock_cb );
836     var_SetAddress( mp, "vmem-display", display_cb );
837     var_SetAddress( mp, "vmem-data", opaque );
838     var_SetString( mp, "vout", "vmem" );
839 }
840
841 void libvlc_video_set_format_callbacks( libvlc_media_player_t *mp,
842                                         libvlc_video_format_cb setup,
843                                         libvlc_video_cleanup_cb cleanup )
844 {
845     var_SetAddress( mp, "vmem-setup", setup );
846     var_SetAddress( mp, "vmem-cleanup", cleanup );
847 }
848
849 void libvlc_video_set_format( libvlc_media_player_t *mp, const char *chroma,
850                               unsigned width, unsigned height, unsigned pitch )
851 {
852     var_SetString( mp, "vmem-chroma", chroma );
853     var_SetInteger( mp, "vmem-width", width );
854     var_SetInteger( mp, "vmem-height", height );
855     var_SetInteger( mp, "vmem-pitch", pitch );
856 }
857
858 /**************************************************************************
859  * set_nsobject
860  **************************************************************************/
861 void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
862                                         void * drawable )
863 {
864     assert (p_mi != NULL);
865 #ifdef __APPLE__
866     var_SetAddress (p_mi, "drawable-nsobject", drawable);
867 #else
868     (void) p_mi; (void)drawable;
869 #endif
870 }
871
872 /**************************************************************************
873  * get_nsobject
874  **************************************************************************/
875 void * libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
876 {
877     assert (p_mi != NULL);
878 #ifdef __APPLE__
879     return var_GetAddress (p_mi, "drawable-nsobject");
880 #else
881     return NULL;
882 #endif
883 }
884
885 /**************************************************************************
886  * set_agl
887  **************************************************************************/
888 void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
889                                   uint32_t drawable )
890 {
891 #ifdef __APPLE__
892     var_SetInteger (p_mi, "drawable-agl", drawable);
893 #else
894     (void) p_mi; (void)drawable;
895 #endif
896 }
897
898 /**************************************************************************
899  * get_agl
900  **************************************************************************/
901 uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
902 {
903     assert (p_mi != NULL);
904 #ifdef __APPLE__
905     return var_GetInteger (p_mi, "drawable-agl");
906 #else
907     return 0;
908 #endif
909 }
910
911 /**************************************************************************
912  * set_xwindow
913  **************************************************************************/
914 void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
915                                       uint32_t drawable )
916 {
917     assert (p_mi != NULL);
918
919     var_SetString (p_mi, "vout", drawable ? "xid" : "any");
920     var_SetString (p_mi, "window", drawable ? "embed-xid,any" : "any");
921     var_SetInteger (p_mi, "drawable-xid", drawable);
922 }
923
924 /**************************************************************************
925  * get_xwindow
926  **************************************************************************/
927 uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
928 {
929     return var_GetInteger (p_mi, "drawable-xid");
930 }
931
932 /**************************************************************************
933  * set_hwnd
934  **************************************************************************/
935 void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
936                                    void *drawable )
937 {
938     assert (p_mi != NULL);
939 #ifdef WIN32
940     var_SetString (p_mi, "window",
941                    (drawable != NULL) ? "embed-hwnd,any" : "");
942     var_SetInteger (p_mi, "drawable-hwnd", (uintptr_t)drawable);
943 #else
944     (void) p_mi; (void) drawable;
945 #endif
946 }
947
948 /**************************************************************************
949  * get_hwnd
950  **************************************************************************/
951 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
952 {
953     assert (p_mi != NULL);
954 #ifdef WIN32
955     return (void *)(uintptr_t)var_GetInteger (p_mi, "drawable-hwnd");
956 #else
957     return NULL;
958 #endif
959 }
960
961 void libvlc_audio_set_callbacks( libvlc_media_player_t *mp,
962                                  libvlc_audio_play_cb play_cb,
963                                  libvlc_audio_pause_cb pause_cb,
964                                  libvlc_audio_resume_cb resume_cb,
965                                  libvlc_audio_flush_cb flush_cb,
966                                  libvlc_audio_drain_cb drain_cb,
967                                  void *opaque )
968 {
969     var_SetAddress( mp, "amem-play", play_cb );
970     var_SetAddress( mp, "amem-pause", pause_cb );
971     var_SetAddress( mp, "amem-resume", resume_cb );
972     var_SetAddress( mp, "amem-flush", flush_cb );
973     var_SetAddress( mp, "amem-drain", drain_cb );
974     var_SetAddress( mp, "amem-data", opaque );
975     var_SetString( mp, "aout", "amem,none" );
976 }
977
978 void libvlc_audio_set_volume_callback( libvlc_media_player_t *mp,
979                                        libvlc_audio_set_volume_cb cb )
980 {
981     var_SetAddress( mp, "amem-set-volume", cb );
982 }
983
984 void libvlc_audio_set_format_callbacks( libvlc_media_player_t *mp,
985                                         libvlc_audio_setup_cb setup,
986                                         libvlc_audio_cleanup_cb cleanup )
987 {
988     var_SetAddress( mp, "amem-setup", setup );
989     var_SetAddress( mp, "amem-cleanup", cleanup );
990 }
991
992 void libvlc_audio_set_format( libvlc_media_player_t *mp, const char *format,
993                               unsigned rate, unsigned channels )
994 {
995     var_SetString( mp, "amem-format", format );
996     var_SetInteger( mp, "amem-rate", rate );
997     var_SetInteger( mp, "amem-channels", channels );
998 }
999
1000
1001 /**************************************************************************
1002  * Getters for stream information
1003  **************************************************************************/
1004 libvlc_time_t libvlc_media_player_get_length(
1005                              libvlc_media_player_t *p_mi )
1006 {
1007     input_thread_t *p_input_thread;
1008     libvlc_time_t i_time;
1009
1010     p_input_thread = libvlc_get_input_thread ( p_mi );
1011     if( !p_input_thread )
1012         return -1;
1013
1014     i_time = from_mtime(var_GetTime( p_input_thread, "length" ));
1015     vlc_object_release( p_input_thread );
1016
1017     return i_time;
1018 }
1019
1020 libvlc_time_t libvlc_media_player_get_time( libvlc_media_player_t *p_mi )
1021 {
1022     input_thread_t *p_input_thread;
1023     libvlc_time_t i_time;
1024
1025     p_input_thread = libvlc_get_input_thread ( p_mi );
1026     if( !p_input_thread )
1027         return -1;
1028
1029     i_time = from_mtime(var_GetTime( p_input_thread , "time" ));
1030     vlc_object_release( p_input_thread );
1031     return i_time;
1032 }
1033
1034 void libvlc_media_player_set_time( libvlc_media_player_t *p_mi,
1035                                    libvlc_time_t i_time )
1036 {
1037     input_thread_t *p_input_thread;
1038
1039     p_input_thread = libvlc_get_input_thread ( p_mi );
1040     if( !p_input_thread )
1041         return;
1042
1043     var_SetTime( p_input_thread, "time", to_mtime(i_time) );
1044     vlc_object_release( p_input_thread );
1045 }
1046
1047 void libvlc_media_player_set_position( libvlc_media_player_t *p_mi,
1048                                        float position )
1049 {
1050     input_thread_t *p_input_thread;
1051
1052     p_input_thread = libvlc_get_input_thread ( p_mi );
1053     if( !p_input_thread )
1054         return;
1055
1056     var_SetFloat( p_input_thread, "position", position );
1057     vlc_object_release( p_input_thread );
1058 }
1059
1060 float libvlc_media_player_get_position( libvlc_media_player_t *p_mi )
1061 {
1062     input_thread_t *p_input_thread;
1063     float f_position;
1064
1065     p_input_thread = libvlc_get_input_thread ( p_mi );
1066     if( !p_input_thread )
1067         return -1.0;
1068
1069     f_position = var_GetFloat( p_input_thread, "position" );
1070     vlc_object_release( p_input_thread );
1071
1072     return f_position;
1073 }
1074
1075 void libvlc_media_player_set_chapter( libvlc_media_player_t *p_mi,
1076                                       int chapter )
1077 {
1078     input_thread_t *p_input_thread;
1079
1080     p_input_thread = libvlc_get_input_thread ( p_mi );
1081     if( !p_input_thread )
1082         return;
1083
1084     var_SetInteger( p_input_thread, "chapter", chapter );
1085     vlc_object_release( p_input_thread );
1086 }
1087
1088 int libvlc_media_player_get_chapter( libvlc_media_player_t *p_mi )
1089 {
1090     input_thread_t *p_input_thread;
1091     int i_chapter;
1092
1093     p_input_thread = libvlc_get_input_thread ( p_mi );
1094     if( !p_input_thread )
1095         return -1;
1096
1097     i_chapter = var_GetInteger( p_input_thread, "chapter" );
1098     vlc_object_release( p_input_thread );
1099
1100     return i_chapter;
1101 }
1102
1103 int libvlc_media_player_get_chapter_count( libvlc_media_player_t *p_mi )
1104 {
1105     input_thread_t *p_input_thread;
1106     vlc_value_t val;
1107
1108     p_input_thread = libvlc_get_input_thread ( p_mi );
1109     if( !p_input_thread )
1110         return -1;
1111
1112     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
1113     vlc_object_release( p_input_thread );
1114
1115     return val.i_int;
1116 }
1117
1118 int libvlc_media_player_get_chapter_count_for_title(
1119                                  libvlc_media_player_t *p_mi,
1120                                  int i_title )
1121 {
1122     input_thread_t *p_input_thread;
1123     vlc_value_t val;
1124
1125     p_input_thread = libvlc_get_input_thread ( p_mi );
1126     if( !p_input_thread )
1127         return -1;
1128
1129     char *psz_name;
1130     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
1131     {
1132         vlc_object_release( p_input_thread );
1133         return -1;
1134     }
1135     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
1136     vlc_object_release( p_input_thread );
1137     free( psz_name );
1138
1139     return val.i_int;
1140 }
1141
1142 void libvlc_media_player_set_title( libvlc_media_player_t *p_mi,
1143                                     int i_title )
1144 {
1145     input_thread_t *p_input_thread;
1146
1147     p_input_thread = libvlc_get_input_thread ( p_mi );
1148     if( !p_input_thread )
1149         return;
1150
1151     var_SetInteger( p_input_thread, "title", i_title );
1152     vlc_object_release( p_input_thread );
1153
1154     //send event
1155     libvlc_event_t event;
1156     event.type = libvlc_MediaPlayerTitleChanged;
1157     event.u.media_player_title_changed.new_title = i_title;
1158     libvlc_event_send( p_mi->p_event_manager, &event );
1159 }
1160
1161 int libvlc_media_player_get_title( libvlc_media_player_t *p_mi )
1162 {
1163     input_thread_t *p_input_thread;
1164     int i_title;
1165
1166     p_input_thread = libvlc_get_input_thread ( p_mi );
1167     if( !p_input_thread )
1168         return -1;
1169
1170     i_title = var_GetInteger( p_input_thread, "title" );
1171     vlc_object_release( p_input_thread );
1172
1173     return i_title;
1174 }
1175
1176 int libvlc_media_player_get_title_count( libvlc_media_player_t *p_mi )
1177 {
1178     input_thread_t *p_input_thread;
1179     vlc_value_t val;
1180
1181     p_input_thread = libvlc_get_input_thread ( p_mi );
1182     if( !p_input_thread )
1183         return -1;
1184
1185     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
1186     vlc_object_release( p_input_thread );
1187
1188     return val.i_int;
1189 }
1190
1191 void libvlc_media_player_next_chapter( libvlc_media_player_t *p_mi )
1192 {
1193     input_thread_t *p_input_thread;
1194
1195     p_input_thread = libvlc_get_input_thread ( p_mi );
1196     if( !p_input_thread )
1197         return;
1198
1199     int i_type = var_Type( p_input_thread, "next-chapter" );
1200     var_TriggerCallback( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1201                             "next-chapter":"next-title" );
1202
1203     vlc_object_release( p_input_thread );
1204 }
1205
1206 void libvlc_media_player_previous_chapter( libvlc_media_player_t *p_mi )
1207 {
1208     input_thread_t *p_input_thread;
1209
1210     p_input_thread = libvlc_get_input_thread ( p_mi );
1211     if( !p_input_thread )
1212         return;
1213
1214     int i_type = var_Type( p_input_thread, "next-chapter" );
1215     var_TriggerCallback( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1216                             "prev-chapter":"prev-title" );
1217
1218     vlc_object_release( p_input_thread );
1219 }
1220
1221 float libvlc_media_player_get_fps( libvlc_media_player_t *p_mi )
1222 {
1223     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1224     double f_fps = 0.0;
1225
1226     if( p_input_thread )
1227     {
1228         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1229             f_fps = 0.0;
1230         vlc_object_release( p_input_thread );
1231     }
1232     return f_fps;
1233 }
1234
1235 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi )
1236 {
1237     bool b_will_play;
1238     input_thread_t *p_input_thread =
1239                             libvlc_get_input_thread ( p_mi );
1240     if ( !p_input_thread )
1241         return false;
1242
1243     b_will_play = !p_input_thread->b_die && !p_input_thread->b_dead;
1244     vlc_object_release( p_input_thread );
1245
1246     return b_will_play;
1247 }
1248
1249 int libvlc_media_player_set_rate( libvlc_media_player_t *p_mi, float rate )
1250 {
1251     if (rate < 0.)
1252     {
1253         libvlc_printerr ("Playing backward not supported");
1254         return -1;
1255     }
1256
1257     var_SetFloat (p_mi, "rate", rate);
1258
1259     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1260     if( !p_input_thread )
1261         return 0;
1262     var_SetFloat( p_input_thread, "rate", rate );
1263     vlc_object_release( p_input_thread );
1264     return 0;
1265 }
1266
1267 float libvlc_media_player_get_rate( libvlc_media_player_t *p_mi )
1268 {
1269     return var_GetFloat (p_mi, "rate");
1270 }
1271
1272 libvlc_state_t libvlc_media_player_get_state( libvlc_media_player_t *p_mi )
1273 {
1274     lock(p_mi);
1275     libvlc_state_t state = p_mi->state;
1276     unlock(p_mi);
1277     return state;
1278 }
1279
1280 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi )
1281 {
1282     input_thread_t *p_input_thread;
1283     bool b_seekable;
1284
1285     p_input_thread = libvlc_get_input_thread ( p_mi );
1286     if ( !p_input_thread )
1287         return false;
1288     b_seekable = var_GetBool( p_input_thread, "can-seek" );
1289     vlc_object_release( p_input_thread );
1290
1291     return b_seekable;
1292 }
1293
1294 void libvlc_media_player_navigate( libvlc_media_player_t* p_mi,
1295                                    unsigned navigate )
1296 {
1297     input_thread_t *p_input_thread;
1298
1299     if ( navigate > libvlc_navigate_to_action_size)
1300       return;
1301
1302     p_input_thread = libvlc_get_input_thread ( p_mi );
1303     if ( !p_input_thread )
1304       return;
1305
1306     var_SetInteger( p_mi->p_libvlc_instance->p_libvlc_int,
1307                     "key-action", libvlc_navigate_to_action[navigate] );
1308
1309     vlc_object_release( p_input_thread );
1310 }
1311
1312 /* internal function, used by audio, video */
1313 libvlc_track_description_t *
1314         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1315                                       const char *psz_variable )
1316 {
1317     input_thread_t *p_input = libvlc_get_input_thread( p_mi );
1318     libvlc_track_description_t *p_track_description = NULL,
1319                                *p_actual, *p_previous;
1320
1321     if( !p_input )
1322         return NULL;
1323
1324     vlc_value_t val_list, text_list;
1325     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1326
1327     /* no tracks */
1328     if( val_list.p_list->i_count <= 0 )
1329         goto end;
1330
1331     p_track_description = ( libvlc_track_description_t * )
1332         malloc( sizeof( libvlc_track_description_t ) );
1333     if ( !p_track_description )
1334     {
1335         libvlc_printerr( "Not enough memory" );
1336         goto end;
1337     }
1338     p_actual = p_track_description;
1339     p_previous = NULL;
1340     for( int i = 0; i < val_list.p_list->i_count; i++ )
1341     {
1342         if( !p_actual )
1343         {
1344             p_actual = ( libvlc_track_description_t * )
1345                 malloc( sizeof( libvlc_track_description_t ) );
1346             if ( !p_actual )
1347             {
1348                 libvlc_track_description_release( p_track_description );
1349                 libvlc_printerr( "Not enough memory" );
1350                 goto end;
1351             }
1352         }
1353         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1354         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1355         p_actual->p_next = NULL;
1356         if( p_previous )
1357             p_previous->p_next = p_actual;
1358         p_previous = p_actual;
1359         p_actual =  NULL;
1360     }
1361
1362 end:
1363     var_FreeList( &val_list, &text_list );
1364     vlc_object_release( p_input );
1365
1366     return p_track_description;
1367 }
1368
1369 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1370 {
1371     libvlc_track_description_t *p_actual, *p_before;
1372     p_actual = p_td;
1373
1374     while ( p_actual )
1375     {
1376         free( p_actual->psz_name );
1377         p_before = p_actual;
1378         p_actual = p_before->p_next;
1379         free( p_before );
1380     }
1381 }
1382
1383 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi )
1384 {
1385     input_thread_t *p_input_thread;
1386     bool b_can_pause;
1387
1388     p_input_thread = libvlc_get_input_thread ( p_mi );
1389     if ( !p_input_thread )
1390         return false;
1391     b_can_pause = var_GetBool( p_input_thread, "can-pause" );
1392     vlc_object_release( p_input_thread );
1393
1394     return b_can_pause;
1395 }
1396
1397 void libvlc_media_player_next_frame( libvlc_media_player_t *p_mi )
1398 {
1399     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1400     if( p_input_thread != NULL )
1401     {
1402         var_TriggerCallback( p_input_thread, "frame-next" );
1403         vlc_object_release( p_input_thread );
1404     }
1405 }