]> git.sesse.net Git - vlc/blob - lib/media_player.c
libvlc: add API to control display of the video title
[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     /* Video Title */
461     var_Create (mp, "video-title-show", VLC_VAR_BOOL);
462     var_Create (mp, "video-title-position", VLC_VAR_INTEGER);
463     var_Create (mp, "video-title-timeout", VLC_VAR_INTEGER);
464
465     mp->p_md = NULL;
466     mp->state = libvlc_NothingSpecial;
467     mp->p_libvlc_instance = instance;
468     mp->input.p_thread = NULL;
469     mp->input.p_resource = input_resource_New(VLC_OBJECT(mp));
470     if (unlikely(mp->input.p_resource == NULL))
471     {
472         vlc_object_release(mp);
473         return NULL;
474     }
475     vlc_mutex_init (&mp->input.lock);
476     mp->i_refcount = 1;
477     mp->p_event_manager = libvlc_event_manager_new(mp, instance);
478     if (unlikely(mp->p_event_manager == NULL))
479     {
480         input_resource_Release(mp->input.p_resource);
481         vlc_object_release(mp);
482         return NULL;
483     }
484     vlc_mutex_init(&mp->object_lock);
485
486     register_event(mp, NothingSpecial);
487     register_event(mp, Opening);
488     register_event(mp, Buffering);
489     register_event(mp, Playing);
490     register_event(mp, Paused);
491     register_event(mp, Stopped);
492     register_event(mp, Forward);
493     register_event(mp, Backward);
494     register_event(mp, EndReached);
495     register_event(mp, EncounteredError);
496     register_event(mp, SeekableChanged);
497
498     register_event(mp, PositionChanged);
499     register_event(mp, TimeChanged);
500     register_event(mp, LengthChanged);
501     register_event(mp, TitleChanged);
502     register_event(mp, PausableChanged);
503
504     register_event(mp, Vout);
505
506     /* Snapshot initialization */
507     register_event(mp, SnapshotTaken);
508
509     register_event(mp, MediaChanged);
510
511     /* Attach a var callback to the global object to provide the glue between
512      * vout_thread that generates the event and media_player that re-emits it
513      * with its own event manager
514      *
515      * FIXME: It's unclear why we want to put this in public API, and why we
516      * want to expose it in such a limiting and ugly way.
517      */
518     var_AddCallback(mp->p_libvlc, "snapshot-file", snapshot_was_taken, mp);
519
520     libvlc_retain(instance);
521     return mp;
522 }
523
524 /**************************************************************************
525  * Create a Media Instance object with a media descriptor.
526  **************************************************************************/
527 libvlc_media_player_t *
528 libvlc_media_player_new_from_media( libvlc_media_t * p_md )
529 {
530     libvlc_media_player_t * p_mi;
531
532     p_mi = libvlc_media_player_new( p_md->p_libvlc_instance );
533     if( !p_mi )
534         return NULL;
535
536     libvlc_media_retain( p_md );
537     p_mi->p_md = p_md;
538
539     return p_mi;
540 }
541
542 /**************************************************************************
543  * Destroy a Media Instance object (libvlc internal)
544  *
545  * Warning: No lock held here, but hey, this is internal. Caller must lock.
546  **************************************************************************/
547 static void libvlc_media_player_destroy( libvlc_media_player_t *p_mi )
548 {
549     assert( p_mi );
550
551     /* Detach Callback from the main libvlc object */
552     var_DelCallback( p_mi->p_libvlc,
553                      "snapshot-file", snapshot_was_taken, p_mi );
554
555     /* No need for lock_input() because no other threads knows us anymore */
556     if( p_mi->input.p_thread )
557         release_input_thread(p_mi, true);
558     input_resource_Terminate( p_mi->input.p_resource );
559     input_resource_Release( p_mi->input.p_resource );
560     vlc_mutex_destroy( &p_mi->input.lock );
561
562     libvlc_event_manager_release( p_mi->p_event_manager );
563     libvlc_media_release( p_mi->p_md );
564     vlc_mutex_destroy( &p_mi->object_lock );
565
566     libvlc_instance_t *instance = p_mi->p_libvlc_instance;
567     vlc_object_release( p_mi );
568     libvlc_release(instance);
569 }
570
571 /**************************************************************************
572  * Release a Media Instance object.
573  *
574  * Function does the locking.
575  **************************************************************************/
576 void libvlc_media_player_release( libvlc_media_player_t *p_mi )
577 {
578     bool destroy;
579
580     assert( p_mi );
581     lock(p_mi);
582     destroy = !--p_mi->i_refcount;
583     unlock(p_mi);
584
585     if( destroy )
586         libvlc_media_player_destroy( p_mi );
587 }
588
589 /**************************************************************************
590  * Retain a Media Instance object.
591  *
592  * Caller must hold the lock.
593  **************************************************************************/
594 void libvlc_media_player_retain( libvlc_media_player_t *p_mi )
595 {
596     assert( p_mi );
597
598     lock(p_mi);
599     p_mi->i_refcount++;
600     unlock(p_mi);
601 }
602
603 /**************************************************************************
604  * Set the Media descriptor associated with the instance.
605  *
606  * Enter without lock -- function will lock the object.
607  **************************************************************************/
608 void libvlc_media_player_set_media(
609                             libvlc_media_player_t *p_mi,
610                             libvlc_media_t *p_md )
611 {
612     lock_input(p_mi);
613
614     /* FIXME I am not sure if it is a user request or on die(eof/error)
615      * request here */
616     release_input_thread( p_mi,
617                           p_mi->input.p_thread &&
618                           !p_mi->input.p_thread->b_eof &&
619                           !p_mi->input.p_thread->b_error );
620
621     lock( p_mi );
622     set_state( p_mi, libvlc_NothingSpecial, true );
623     unlock_input( p_mi );
624
625     libvlc_media_release( p_mi->p_md );
626
627     if( !p_md )
628     {
629         p_mi->p_md = NULL;
630         unlock(p_mi);
631         return; /* It is ok to pass a NULL md */
632     }
633
634     libvlc_media_retain( p_md );
635     p_mi->p_md = p_md;
636
637     /* The policy here is to ignore that we were created using a different
638      * libvlc_instance, because we don't really care */
639     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
640
641     unlock(p_mi);
642
643     /* Send an event for the newly available media */
644     libvlc_event_t event;
645     event.type = libvlc_MediaPlayerMediaChanged;
646     event.u.media_player_media_changed.new_media = p_md;
647     libvlc_event_send( p_mi->p_event_manager, &event );
648
649 }
650
651 /**************************************************************************
652  * Get the Media descriptor associated with the instance.
653  **************************************************************************/
654 libvlc_media_t *
655 libvlc_media_player_get_media( libvlc_media_player_t *p_mi )
656 {
657     libvlc_media_t *p_m;
658
659     lock(p_mi);
660     p_m = p_mi->p_md;
661     if( p_m )
662         libvlc_media_retain( p_mi->p_md );
663     unlock(p_mi);
664     return p_mi->p_md;
665 }
666
667 /**************************************************************************
668  * Get the event Manager.
669  **************************************************************************/
670 libvlc_event_manager_t *
671 libvlc_media_player_event_manager( libvlc_media_player_t *p_mi )
672 {
673     return p_mi->p_event_manager;
674 }
675
676 /**************************************************************************
677  * Tell media player to start playing.
678  **************************************************************************/
679 int libvlc_media_player_play( libvlc_media_player_t *p_mi )
680 {
681     lock_input( p_mi );
682
683     input_thread_t *p_input_thread = p_mi->input.p_thread;
684     if( p_input_thread )
685     {
686         /* A thread already exists, send it a play message */
687         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
688         unlock_input( p_mi );
689         return 0;
690     }
691
692     /* Ignore previous exception */
693     lock(p_mi);
694
695     if( !p_mi->p_md )
696     {
697         unlock(p_mi);
698         unlock_input( p_mi );
699         libvlc_printerr( "No associated media descriptor" );
700         return -1;
701     }
702
703     p_input_thread = input_Create( p_mi, p_mi->p_md->p_input_item, NULL,
704                                    p_mi->input.p_resource );
705     unlock(p_mi);
706     if( !p_input_thread )
707     {
708         unlock_input(p_mi);
709         libvlc_printerr( "Not enough memory" );
710         return -1;
711     }
712
713     var_AddCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
714     var_AddCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
715     var_AddCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
716
717     if( input_Start( p_input_thread ) )
718     {
719         unlock_input(p_mi);
720         var_DelCallback( p_input_thread, "intf-event", input_event_changed, p_mi );
721         var_DelCallback( p_input_thread, "can-pause", input_pausable_changed, p_mi );
722         var_DelCallback( p_input_thread, "can-seek", input_seekable_changed, p_mi );
723         vlc_object_release( p_input_thread );
724         libvlc_printerr( "Input initialization failure" );
725         return -1;
726     }
727     p_mi->input.p_thread = p_input_thread;
728     unlock_input(p_mi);
729     return 0;
730 }
731
732 void libvlc_media_player_set_pause( libvlc_media_player_t *p_mi, int paused )
733 {
734     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi );
735     if( !p_input_thread )
736         return;
737
738     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
739     if( state == libvlc_Playing || state == libvlc_Buffering )
740     {
741         if( paused )
742         {
743             if( libvlc_media_player_can_pause( p_mi ) )
744                 input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
745             else
746                 libvlc_media_player_stop( p_mi );
747         }
748     }
749     else
750     {
751         if( !paused )
752             input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
753     }
754
755     vlc_object_release( p_input_thread );
756 }
757
758 /**************************************************************************
759  * Toggle pause.
760  **************************************************************************/
761 void libvlc_media_player_pause( libvlc_media_player_t *p_mi )
762 {
763     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
764     bool playing = (state == libvlc_Playing || state == libvlc_Buffering);
765
766     libvlc_media_player_set_pause( p_mi, playing );
767 }
768
769 /**************************************************************************
770  * Tells whether the media player is currently playing.
771  *
772  * Enter with lock held.
773  **************************************************************************/
774 int libvlc_media_player_is_playing( libvlc_media_player_t *p_mi )
775 {
776     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
777     return (libvlc_Playing == state) || (libvlc_Buffering == state);
778 }
779
780 /**************************************************************************
781  * Stop playing.
782  **************************************************************************/
783 void libvlc_media_player_stop( libvlc_media_player_t *p_mi )
784 {
785     libvlc_state_t state = libvlc_media_player_get_state( p_mi );
786
787     lock_input(p_mi);
788     release_input_thread( p_mi, true ); /* This will stop the input thread */
789
790     /* Force to go to stopped state, in case we were in Ended, or Error
791      * state. */
792     if( state != libvlc_Stopped )
793     {
794         set_state( p_mi, libvlc_Stopped, false );
795
796         /* Construct and send the event */
797         libvlc_event_t event;
798         event.type = libvlc_MediaPlayerStopped;
799         libvlc_event_send( p_mi->p_event_manager, &event );
800     }
801
802     input_resource_Terminate( p_mi->input.p_resource );
803     unlock_input(p_mi);
804 }
805
806
807 void libvlc_video_set_callbacks( libvlc_media_player_t *mp,
808     void *(*lock_cb) (void *, void **),
809     void (*unlock_cb) (void *, void *, void *const *),
810     void (*display_cb) (void *, void *),
811     void *opaque )
812 {
813     var_SetAddress( mp, "vmem-lock", lock_cb );
814     var_SetAddress( mp, "vmem-unlock", unlock_cb );
815     var_SetAddress( mp, "vmem-display", display_cb );
816     var_SetAddress( mp, "vmem-data", opaque );
817     var_SetString( mp, "vout", "vmem" );
818 }
819
820 void libvlc_video_set_format_callbacks( libvlc_media_player_t *mp,
821                                         libvlc_video_format_cb setup,
822                                         libvlc_video_cleanup_cb cleanup )
823 {
824     var_SetAddress( mp, "vmem-setup", setup );
825     var_SetAddress( mp, "vmem-cleanup", cleanup );
826 }
827
828 void libvlc_video_set_format( libvlc_media_player_t *mp, const char *chroma,
829                               unsigned width, unsigned height, unsigned pitch )
830 {
831     var_SetString( mp, "vmem-chroma", chroma );
832     var_SetInteger( mp, "vmem-width", width );
833     var_SetInteger( mp, "vmem-height", height );
834     var_SetInteger( mp, "vmem-pitch", pitch );
835 }
836
837 /**************************************************************************
838  * set_nsobject
839  **************************************************************************/
840 void libvlc_media_player_set_nsobject( libvlc_media_player_t *p_mi,
841                                         void * drawable )
842 {
843     assert (p_mi != NULL);
844 #ifdef __APPLE__
845     var_SetAddress (p_mi, "drawable-nsobject", drawable);
846 #else
847     (void) p_mi; (void)drawable;
848 #endif
849 }
850
851 /**************************************************************************
852  * get_nsobject
853  **************************************************************************/
854 void * libvlc_media_player_get_nsobject( libvlc_media_player_t *p_mi )
855 {
856     assert (p_mi != NULL);
857 #ifdef __APPLE__
858     return var_GetAddress (p_mi, "drawable-nsobject");
859 #else
860     return NULL;
861 #endif
862 }
863
864 /**************************************************************************
865  * set_agl
866  **************************************************************************/
867 void libvlc_media_player_set_agl( libvlc_media_player_t *p_mi,
868                                   uint32_t drawable )
869 {
870 #ifdef __APPLE__
871     var_SetInteger (p_mi, "drawable-agl", drawable);
872 #else
873     (void) p_mi; (void)drawable;
874 #endif
875 }
876
877 /**************************************************************************
878  * get_agl
879  **************************************************************************/
880 uint32_t libvlc_media_player_get_agl( libvlc_media_player_t *p_mi )
881 {
882     assert (p_mi != NULL);
883 #ifdef __APPLE__
884     return var_GetInteger (p_mi, "drawable-agl");
885 #else
886     return 0;
887 #endif
888 }
889
890 /**************************************************************************
891  * set_xwindow
892  **************************************************************************/
893 void libvlc_media_player_set_xwindow( libvlc_media_player_t *p_mi,
894                                       uint32_t drawable )
895 {
896     assert (p_mi != NULL);
897
898     var_SetString (p_mi, "vout", drawable ? "xid" : "any");
899     var_SetString (p_mi, "window", drawable ? "embed-xid,any" : "any");
900     var_SetInteger (p_mi, "drawable-xid", drawable);
901 }
902
903 /**************************************************************************
904  * get_xwindow
905  **************************************************************************/
906 uint32_t libvlc_media_player_get_xwindow( libvlc_media_player_t *p_mi )
907 {
908     return var_GetInteger (p_mi, "drawable-xid");
909 }
910
911 /**************************************************************************
912  * set_hwnd
913  **************************************************************************/
914 void libvlc_media_player_set_hwnd( libvlc_media_player_t *p_mi,
915                                    void *drawable )
916 {
917     assert (p_mi != NULL);
918 #if defined (_WIN32) || defined (__OS2__)
919     var_SetString (p_mi, "window",
920                    (drawable != NULL) ? "embed-hwnd,any" : "");
921     var_SetInteger (p_mi, "drawable-hwnd", (uintptr_t)drawable);
922 #else
923     (void) p_mi; (void) drawable;
924 #endif
925 }
926
927 /**************************************************************************
928  * get_hwnd
929  **************************************************************************/
930 void *libvlc_media_player_get_hwnd( libvlc_media_player_t *p_mi )
931 {
932     assert (p_mi != NULL);
933 #if defined (_WIN32) || defined (__OS2__)
934     return (void *)(uintptr_t)var_GetInteger (p_mi, "drawable-hwnd");
935 #else
936     return NULL;
937 #endif
938 }
939
940 void libvlc_audio_set_callbacks( libvlc_media_player_t *mp,
941                                  libvlc_audio_play_cb play_cb,
942                                  libvlc_audio_pause_cb pause_cb,
943                                  libvlc_audio_resume_cb resume_cb,
944                                  libvlc_audio_flush_cb flush_cb,
945                                  libvlc_audio_drain_cb drain_cb,
946                                  void *opaque )
947 {
948     var_SetAddress( mp, "amem-play", play_cb );
949     var_SetAddress( mp, "amem-pause", pause_cb );
950     var_SetAddress( mp, "amem-resume", resume_cb );
951     var_SetAddress( mp, "amem-flush", flush_cb );
952     var_SetAddress( mp, "amem-drain", drain_cb );
953     var_SetAddress( mp, "amem-data", opaque );
954     var_SetString( mp, "aout", "amem,none" );
955 }
956
957 void libvlc_audio_set_volume_callback( libvlc_media_player_t *mp,
958                                        libvlc_audio_set_volume_cb cb )
959 {
960     var_SetAddress( mp, "amem-set-volume", cb );
961 }
962
963 void libvlc_audio_set_format_callbacks( libvlc_media_player_t *mp,
964                                         libvlc_audio_setup_cb setup,
965                                         libvlc_audio_cleanup_cb cleanup )
966 {
967     var_SetAddress( mp, "amem-setup", setup );
968     var_SetAddress( mp, "amem-cleanup", cleanup );
969 }
970
971 void libvlc_audio_set_format( libvlc_media_player_t *mp, const char *format,
972                               unsigned rate, unsigned channels )
973 {
974     var_SetString( mp, "amem-format", format );
975     var_SetInteger( mp, "amem-rate", rate );
976     var_SetInteger( mp, "amem-channels", channels );
977 }
978
979
980 /**************************************************************************
981  * Getters for stream information
982  **************************************************************************/
983 libvlc_time_t libvlc_media_player_get_length(
984                              libvlc_media_player_t *p_mi )
985 {
986     input_thread_t *p_input_thread;
987     libvlc_time_t i_time;
988
989     p_input_thread = libvlc_get_input_thread ( p_mi );
990     if( !p_input_thread )
991         return -1;
992
993     i_time = from_mtime(var_GetTime( p_input_thread, "length" ));
994     vlc_object_release( p_input_thread );
995
996     return i_time;
997 }
998
999 libvlc_time_t libvlc_media_player_get_time( libvlc_media_player_t *p_mi )
1000 {
1001     input_thread_t *p_input_thread;
1002     libvlc_time_t i_time;
1003
1004     p_input_thread = libvlc_get_input_thread ( p_mi );
1005     if( !p_input_thread )
1006         return -1;
1007
1008     i_time = from_mtime(var_GetTime( p_input_thread , "time" ));
1009     vlc_object_release( p_input_thread );
1010     return i_time;
1011 }
1012
1013 void libvlc_media_player_set_time( libvlc_media_player_t *p_mi,
1014                                    libvlc_time_t i_time )
1015 {
1016     input_thread_t *p_input_thread;
1017
1018     p_input_thread = libvlc_get_input_thread ( p_mi );
1019     if( !p_input_thread )
1020         return;
1021
1022     var_SetTime( p_input_thread, "time", to_mtime(i_time) );
1023     vlc_object_release( p_input_thread );
1024 }
1025
1026 void libvlc_media_player_set_position( libvlc_media_player_t *p_mi,
1027                                        float position )
1028 {
1029     input_thread_t *p_input_thread;
1030
1031     p_input_thread = libvlc_get_input_thread ( p_mi );
1032     if( !p_input_thread )
1033         return;
1034
1035     var_SetFloat( p_input_thread, "position", position );
1036     vlc_object_release( p_input_thread );
1037 }
1038
1039 float libvlc_media_player_get_position( libvlc_media_player_t *p_mi )
1040 {
1041     input_thread_t *p_input_thread;
1042     float f_position;
1043
1044     p_input_thread = libvlc_get_input_thread ( p_mi );
1045     if( !p_input_thread )
1046         return -1.0;
1047
1048     f_position = var_GetFloat( p_input_thread, "position" );
1049     vlc_object_release( p_input_thread );
1050
1051     return f_position;
1052 }
1053
1054 void libvlc_media_player_set_chapter( libvlc_media_player_t *p_mi,
1055                                       int chapter )
1056 {
1057     input_thread_t *p_input_thread;
1058
1059     p_input_thread = libvlc_get_input_thread ( p_mi );
1060     if( !p_input_thread )
1061         return;
1062
1063     var_SetInteger( p_input_thread, "chapter", chapter );
1064     vlc_object_release( p_input_thread );
1065 }
1066
1067 int libvlc_media_player_get_chapter( libvlc_media_player_t *p_mi )
1068 {
1069     input_thread_t *p_input_thread;
1070     int i_chapter;
1071
1072     p_input_thread = libvlc_get_input_thread ( p_mi );
1073     if( !p_input_thread )
1074         return -1;
1075
1076     i_chapter = var_GetInteger( p_input_thread, "chapter" );
1077     vlc_object_release( p_input_thread );
1078
1079     return i_chapter;
1080 }
1081
1082 int libvlc_media_player_get_chapter_count( libvlc_media_player_t *p_mi )
1083 {
1084     input_thread_t *p_input_thread;
1085     vlc_value_t val;
1086
1087     p_input_thread = libvlc_get_input_thread ( p_mi );
1088     if( !p_input_thread )
1089         return -1;
1090
1091     var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
1092     vlc_object_release( p_input_thread );
1093
1094     return val.i_int;
1095 }
1096
1097 int libvlc_media_player_get_chapter_count_for_title(
1098                                  libvlc_media_player_t *p_mi,
1099                                  int i_title )
1100 {
1101     input_thread_t *p_input_thread;
1102     vlc_value_t val;
1103
1104     p_input_thread = libvlc_get_input_thread ( p_mi );
1105     if( !p_input_thread )
1106         return -1;
1107
1108     char *psz_name;
1109     if( asprintf( &psz_name,  "title %2i", i_title ) == -1 )
1110     {
1111         vlc_object_release( p_input_thread );
1112         return -1;
1113     }
1114     var_Change( p_input_thread, psz_name, VLC_VAR_CHOICESCOUNT, &val, NULL );
1115     vlc_object_release( p_input_thread );
1116     free( psz_name );
1117
1118     return val.i_int;
1119 }
1120
1121 void libvlc_media_player_set_title( libvlc_media_player_t *p_mi,
1122                                     int i_title )
1123 {
1124     input_thread_t *p_input_thread;
1125
1126     p_input_thread = libvlc_get_input_thread ( p_mi );
1127     if( !p_input_thread )
1128         return;
1129
1130     var_SetInteger( p_input_thread, "title", i_title );
1131     vlc_object_release( p_input_thread );
1132
1133     //send event
1134     libvlc_event_t event;
1135     event.type = libvlc_MediaPlayerTitleChanged;
1136     event.u.media_player_title_changed.new_title = i_title;
1137     libvlc_event_send( p_mi->p_event_manager, &event );
1138 }
1139
1140 int libvlc_media_player_get_title( libvlc_media_player_t *p_mi )
1141 {
1142     input_thread_t *p_input_thread;
1143     int i_title;
1144
1145     p_input_thread = libvlc_get_input_thread ( p_mi );
1146     if( !p_input_thread )
1147         return -1;
1148
1149     i_title = var_GetInteger( p_input_thread, "title" );
1150     vlc_object_release( p_input_thread );
1151
1152     return i_title;
1153 }
1154
1155 int libvlc_media_player_get_title_count( libvlc_media_player_t *p_mi )
1156 {
1157     input_thread_t *p_input_thread;
1158     vlc_value_t val;
1159
1160     p_input_thread = libvlc_get_input_thread ( p_mi );
1161     if( !p_input_thread )
1162         return -1;
1163
1164     var_Change( p_input_thread, "title", VLC_VAR_CHOICESCOUNT, &val, NULL );
1165     vlc_object_release( p_input_thread );
1166
1167     return val.i_int;
1168 }
1169
1170 void libvlc_media_player_next_chapter( libvlc_media_player_t *p_mi )
1171 {
1172     input_thread_t *p_input_thread;
1173
1174     p_input_thread = libvlc_get_input_thread ( p_mi );
1175     if( !p_input_thread )
1176         return;
1177
1178     int i_type = var_Type( p_input_thread, "next-chapter" );
1179     var_TriggerCallback( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1180                             "next-chapter":"next-title" );
1181
1182     vlc_object_release( p_input_thread );
1183 }
1184
1185 void libvlc_media_player_previous_chapter( libvlc_media_player_t *p_mi )
1186 {
1187     input_thread_t *p_input_thread;
1188
1189     p_input_thread = libvlc_get_input_thread ( p_mi );
1190     if( !p_input_thread )
1191         return;
1192
1193     int i_type = var_Type( p_input_thread, "next-chapter" );
1194     var_TriggerCallback( p_input_thread, (i_type & VLC_VAR_TYPE) != 0 ?
1195                             "prev-chapter":"prev-title" );
1196
1197     vlc_object_release( p_input_thread );
1198 }
1199
1200 float libvlc_media_player_get_fps( libvlc_media_player_t *p_mi )
1201 {
1202     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1203     double f_fps = 0.0;
1204
1205     if( p_input_thread )
1206     {
1207         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
1208             f_fps = 0.0;
1209         vlc_object_release( p_input_thread );
1210     }
1211     return f_fps;
1212 }
1213
1214 int libvlc_media_player_will_play( libvlc_media_player_t *p_mi )
1215 {
1216     bool b_will_play;
1217     input_thread_t *p_input_thread =
1218                             libvlc_get_input_thread ( p_mi );
1219     if ( !p_input_thread )
1220         return false;
1221
1222     b_will_play = !p_input_thread->b_dead;
1223     vlc_object_release( p_input_thread );
1224
1225     return b_will_play;
1226 }
1227
1228 int libvlc_media_player_set_rate( libvlc_media_player_t *p_mi, float rate )
1229 {
1230     if (rate < 0.)
1231     {
1232         libvlc_printerr ("Playing backward not supported");
1233         return -1;
1234     }
1235
1236     var_SetFloat (p_mi, "rate", rate);
1237
1238     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1239     if( !p_input_thread )
1240         return 0;
1241     var_SetFloat( p_input_thread, "rate", rate );
1242     vlc_object_release( p_input_thread );
1243     return 0;
1244 }
1245
1246 float libvlc_media_player_get_rate( libvlc_media_player_t *p_mi )
1247 {
1248     return var_GetFloat (p_mi, "rate");
1249 }
1250
1251 libvlc_state_t libvlc_media_player_get_state( libvlc_media_player_t *p_mi )
1252 {
1253     lock(p_mi);
1254     libvlc_state_t state = p_mi->state;
1255     unlock(p_mi);
1256     return state;
1257 }
1258
1259 int libvlc_media_player_is_seekable( libvlc_media_player_t *p_mi )
1260 {
1261     input_thread_t *p_input_thread;
1262     bool b_seekable;
1263
1264     p_input_thread = libvlc_get_input_thread ( p_mi );
1265     if ( !p_input_thread )
1266         return false;
1267     b_seekable = var_GetBool( p_input_thread, "can-seek" );
1268     vlc_object_release( p_input_thread );
1269
1270     return b_seekable;
1271 }
1272
1273 void libvlc_media_player_navigate( libvlc_media_player_t* p_mi,
1274                                    unsigned navigate )
1275 {
1276     static const vlc_action_t map[] =
1277     {
1278         INPUT_NAV_ACTIVATE, INPUT_NAV_UP, INPUT_NAV_DOWN,
1279         INPUT_NAV_LEFT, INPUT_NAV_RIGHT,
1280     };
1281
1282     if( navigate >= sizeof(map) / sizeof(map[0]) )
1283       return;
1284
1285     input_thread_t *p_input = libvlc_get_input_thread ( p_mi );
1286     if ( p_input == NULL )
1287       return;
1288
1289     input_Control( p_input, map[navigate], NULL );
1290     vlc_object_release( p_input );
1291 }
1292
1293 /* internal function, used by audio, video */
1294 libvlc_track_description_t *
1295         libvlc_get_track_description( libvlc_media_player_t *p_mi,
1296                                       const char *psz_variable )
1297 {
1298     input_thread_t *p_input = libvlc_get_input_thread( p_mi );
1299     libvlc_track_description_t *p_track_description = NULL,
1300                                *p_actual, *p_previous;
1301
1302     if( !p_input )
1303         return NULL;
1304
1305     vlc_value_t val_list, text_list;
1306     var_Change( p_input, psz_variable, VLC_VAR_GETLIST, &val_list, &text_list);
1307
1308     /* no tracks */
1309     if( val_list.p_list->i_count <= 0 )
1310         goto end;
1311
1312     p_track_description = ( libvlc_track_description_t * )
1313         malloc( sizeof( libvlc_track_description_t ) );
1314     if ( !p_track_description )
1315     {
1316         libvlc_printerr( "Not enough memory" );
1317         goto end;
1318     }
1319     p_actual = p_track_description;
1320     p_previous = NULL;
1321     for( int i = 0; i < val_list.p_list->i_count; i++ )
1322     {
1323         if( !p_actual )
1324         {
1325             p_actual = ( libvlc_track_description_t * )
1326                 malloc( sizeof( libvlc_track_description_t ) );
1327             if ( !p_actual )
1328             {
1329                 libvlc_track_description_list_release( p_track_description );
1330                 libvlc_printerr( "Not enough memory" );
1331                 goto end;
1332             }
1333         }
1334         p_actual->i_id = val_list.p_list->p_values[i].i_int;
1335         p_actual->psz_name = strdup( text_list.p_list->p_values[i].psz_string );
1336         p_actual->p_next = NULL;
1337         if( p_previous )
1338             p_previous->p_next = p_actual;
1339         p_previous = p_actual;
1340         p_actual =  NULL;
1341     }
1342
1343 end:
1344     var_FreeList( &val_list, &text_list );
1345     vlc_object_release( p_input );
1346
1347     return p_track_description;
1348 }
1349
1350 // Deprecated alias for libvlc_track_description_list_release
1351 void libvlc_track_description_release( libvlc_track_description_t *p_td )
1352 {
1353     libvlc_track_description_list_release( p_td );
1354 }
1355
1356 void libvlc_track_description_list_release( libvlc_track_description_t *p_td )
1357 {
1358     libvlc_track_description_t *p_actual, *p_before;
1359     p_actual = p_td;
1360
1361     while ( p_actual )
1362     {
1363         free( p_actual->psz_name );
1364         p_before = p_actual;
1365         p_actual = p_before->p_next;
1366         free( p_before );
1367     }
1368 }
1369
1370 int libvlc_media_player_can_pause( libvlc_media_player_t *p_mi )
1371 {
1372     input_thread_t *p_input_thread;
1373     bool b_can_pause;
1374
1375     p_input_thread = libvlc_get_input_thread ( p_mi );
1376     if ( !p_input_thread )
1377         return false;
1378     b_can_pause = var_GetBool( p_input_thread, "can-pause" );
1379     vlc_object_release( p_input_thread );
1380
1381     return b_can_pause;
1382 }
1383
1384 void libvlc_media_player_next_frame( libvlc_media_player_t *p_mi )
1385 {
1386     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi );
1387     if( p_input_thread != NULL )
1388     {
1389         var_TriggerCallback( p_input_thread, "frame-next" );
1390         vlc_object_release( p_input_thread );
1391     }
1392 }
1393
1394 void libvlc_media_player_set_video_title_display( libvlc_media_player_t *p_mi, libvlc_position_t position, unsigned timeout )
1395 {
1396     if ( position != libvlc_position_disable )
1397     {
1398         var_SetBool( p_mi, "video-title-show", true );
1399         var_SetInteger( p_mi, "video-title-position", position );
1400         var_SetInteger( p_mi, "video-title-timeout", timeout );
1401     }
1402     else
1403     {
1404         var_SetBool( p_mi, "video-title-show", false );
1405     }
1406 }