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