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