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