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