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