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