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