26f04c226bae85b29d11898599a2d8020119c13c
[vlc.git] / src / control / media_instance.c
1 /*****************************************************************************
2  * media_instance.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 <vlc/libvlc.h>
25 #include <vlc_demux.h>
26 #include <vlc_input.h>
27 #include "libvlc_internal.h"
28 #include "libvlc.h"
29
30 /*
31  * Release the associated input thread
32  *
33  * Object lock is NOT held.
34  */
35 static void release_input_thread( libvlc_media_instance_t *p_mi )
36 {
37     input_thread_t *p_input_thread;
38     vlc_bool_t should_destroy;
39
40     if( !p_mi || p_mi->i_input_id == -1 )
41         return;
42
43     p_input_thread = (input_thread_t*)vlc_object_get(
44                                              p_mi->p_libvlc_instance->p_libvlc_int,
45                                              p_mi->i_input_id );
46
47     p_mi->i_input_id = -1;
48
49     if( !p_input_thread )
50         return;
51  
52     /* release for previous vlc_object_get */
53     vlc_object_release( p_input_thread );
54
55     /* release for initial p_input_thread yield (see _new()) */
56     vlc_object_release( p_input_thread );
57
58     /* No one is tracking this input_thread appart us. Destroy it */
59     if( p_mi->b_own_its_input_thread )
60     {
61         /* We owned this one */
62         input_StopThread( p_input_thread );
63         var_Destroy( p_input_thread, "drawable" );
64         input_DestroyThread( p_input_thread );
65     }
66     else
67     {
68         /* XXX: hack the playlist doesn't retain the input thread,
69          * so we did it for the playlist (see _new_from_input_thread),
70          * revert that here. This will be deleted with the playlist API */
71         vlc_object_release( p_input_thread );
72     }
73 }
74
75 /*
76  * Retrieve the input thread. Be sure to release the object
77  * once you are done with it. (libvlc Internal)
78  *
79  * Object lock is held.
80  */
81 input_thread_t *libvlc_get_input_thread( libvlc_media_instance_t *p_mi,
82                                          libvlc_exception_t *p_e )
83 {
84     input_thread_t *p_input_thread;
85
86     vlc_mutex_lock( &p_mi->object_lock );
87
88     if( !p_mi || p_mi->i_input_id == -1 )
89     {
90         vlc_mutex_unlock( &p_mi->object_lock );
91         RAISENULL( "Input is NULL" );
92     }
93
94     p_input_thread = (input_thread_t*)vlc_object_get(
95                                              p_mi->p_libvlc_instance->p_libvlc_int,
96                                              p_mi->i_input_id );
97     if( !p_input_thread )
98     {
99         vlc_mutex_unlock( &p_mi->object_lock );
100         RAISENULL( "Input does not exist" );
101     }
102
103     vlc_mutex_unlock( &p_mi->object_lock );
104     return p_input_thread;
105 }
106
107 /*
108  * input_state_changed (Private) (input var "state" Callback)
109  */
110 static int
111 input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
112                      vlc_value_t oldval, vlc_value_t newval,
113                      void * p_userdata )
114 {
115     libvlc_media_instance_t * p_mi = p_userdata;
116     libvlc_event_t event;
117
118     if( newval.i_int == oldval.i_int )
119         return VLC_SUCCESS; /* No change since last time, don't propagate */
120
121     switch ( newval.i_int )
122     {
123         case END_S:
124             event.type = libvlc_MediaInstanceReachedEnd;
125             break;
126         case PAUSE_S:
127             event.type = libvlc_MediaInstancePaused;
128             break;
129         case PLAYING_S:
130             event.type = libvlc_MediaInstancePlayed;
131             break;
132         default:
133             return VLC_SUCCESS;
134     }
135
136     libvlc_event_send( p_mi->p_event_manager, &event );
137     return VLC_SUCCESS;
138 }
139
140 /*
141  * input_position_changed (Private) (input var "intf-change" Callback)
142  */
143 static int
144 input_position_changed( vlc_object_t * p_this, char const * psz_cmd,
145                      vlc_value_t oldval, vlc_value_t newval,
146                      void * p_userdata )
147 {
148     libvlc_media_instance_t * p_mi = p_userdata;
149     vlc_value_t val;
150  
151     if (!strcmp(psz_cmd, "intf" /* "-change" no need to go further */))
152     {
153         input_thread_t * p_input = (input_thread_t *)p_this;
154         var_Get( p_input, "position", &val );
155
156         if ((val.i_time % I64C(500000)) != 0)
157             return VLC_SUCCESS; /* No need to have a better precision */
158         var_Get( p_input, "state", &val );
159
160         if( val.i_int != PLAYING_S )
161             return VLC_SUCCESS; /* Don't send the position while stopped */
162     }
163     else
164         val.i_time = newval.i_time;
165
166     libvlc_event_t event;
167     event.type = libvlc_MediaInstancePositionChanged;
168     event.u.media_instance_position_changed.new_position = val.i_time;
169
170     libvlc_event_send( p_mi->p_event_manager, &event );
171     return VLC_SUCCESS;
172 }
173
174 /**************************************************************************
175  * Create a Media Instance object
176  **************************************************************************/
177 libvlc_media_instance_t *
178 libvlc_media_instance_new( libvlc_instance_t * p_libvlc_instance,
179                            libvlc_exception_t * p_e )
180 {
181     libvlc_media_instance_t * p_mi;
182
183     if( !p_libvlc_instance )
184     {
185         libvlc_exception_raise( p_e, "invalid libvlc instance" );
186         return NULL;
187     }
188
189     p_mi = malloc( sizeof(libvlc_media_instance_t) );
190     p_mi->p_md = NULL;
191     p_mi->drawable = 0;
192     p_mi->p_libvlc_instance = p_libvlc_instance;
193     p_mi->i_input_id = -1;
194     /* refcount strategy:
195      * - All items created by _new start with a refcount set to 1
196      * - Accessor _release decrease the refcount by 1, if after that
197      *   operation the refcount is 0, the object is destroyed.
198      * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
199     p_mi->i_refcount = 1;
200     p_mi->b_own_its_input_thread = VLC_TRUE;
201     /* object_lock strategy:
202      * - No lock held in constructor
203      * - Lock when accessing all variable this lock is held
204      * - Lock when attempting to destroy the object the lock is also held */
205     vlc_mutex_init( p_mi->p_libvlc_instance->p_libvlc_int,
206                     &p_mi->object_lock );
207     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
208             p_libvlc_instance, p_e );
209     if( libvlc_exception_raised( p_e ) )
210     {
211         free( p_mi );
212         return NULL;
213     }
214  
215     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
216             libvlc_MediaInstanceReachedEnd, p_e );
217     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
218             libvlc_MediaInstancePaused, p_e );
219     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
220             libvlc_MediaInstancePlayed, p_e );
221     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
222             libvlc_MediaInstancePositionChanged, p_e );
223
224     return p_mi;
225 }
226
227 /**************************************************************************
228  * Create a Media Instance object with a media descriptor
229  **************************************************************************/
230 libvlc_media_instance_t *
231 libvlc_media_instance_new_from_media_descriptor(
232                                     libvlc_media_descriptor_t * p_md,
233                                     libvlc_exception_t *p_e )
234 {
235     libvlc_media_instance_t * p_mi;
236     p_mi = libvlc_media_instance_new( p_md->p_libvlc_instance, p_e );
237
238     if( !p_mi )
239         return NULL;
240
241     libvlc_media_descriptor_retain( p_md );
242     p_mi->p_md = p_md;
243
244     return p_mi;
245 }
246
247 /**************************************************************************
248  * Create a new media instance object from an input_thread (Libvlc Internal)
249  **************************************************************************/
250 libvlc_media_instance_t * libvlc_media_instance_new_from_input_thread(
251                                    struct libvlc_instance_t *p_libvlc_instance,
252                                    input_thread_t *p_input,
253                                    libvlc_exception_t *p_e )
254 {
255     libvlc_media_instance_t * p_mi;
256
257     if( !p_input )
258     {
259         libvlc_exception_raise( p_e, "invalid input thread" );
260         return NULL;
261     }
262
263     p_mi = libvlc_media_instance_new( p_libvlc_instance, p_e );
264
265     if( !p_mi )
266         return NULL;
267
268     p_mi->p_md = libvlc_media_descriptor_new_from_input_item(
269                     p_libvlc_instance,
270                     input_GetItem( p_input ), p_e );
271
272     if( !p_mi->p_md )
273     {
274         libvlc_media_instance_destroy( p_mi );
275         return NULL;
276     }
277
278     p_mi->i_input_id = p_input->i_object_id;
279     p_mi->b_own_its_input_thread = VLC_FALSE;
280
281     /* will be released in media_instance_release() */
282     vlc_object_yield( p_input );
283
284     /* XXX: Hack as the playlist doesn't yield the input thread we retain
285      * the input for the playlist. (see corresponding hack in _release) */
286     vlc_object_yield( p_input );
287
288     return p_mi;
289 }
290
291 /**************************************************************************
292  * Destroy a Media Instance object (libvlc internal)
293  *
294  * Warning: No lock held here, but hey, this is internal.
295  **************************************************************************/
296 void libvlc_media_instance_destroy( libvlc_media_instance_t *p_mi )
297 {
298     input_thread_t *p_input_thread;
299     libvlc_exception_t p_e;
300
301     libvlc_exception_init( &p_e );
302
303     if( !p_mi )
304         return;
305
306     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
307
308     if( libvlc_exception_raised( &p_e ) )
309     {
310         libvlc_event_manager_release( p_mi->p_event_manager );
311         free( p_mi );
312         return; /* no need to worry about no input thread */
313     }
314     vlc_mutex_destroy( &p_mi->object_lock );
315
316     input_DestroyThread( p_input_thread );
317
318     libvlc_media_descriptor_release( p_mi->p_md );
319
320     free( p_mi );
321 }
322
323 /**************************************************************************
324  * Release a Media Instance object
325  **************************************************************************/
326 void libvlc_media_instance_release( libvlc_media_instance_t *p_mi )
327 {
328     if( !p_mi )
329         return;
330
331     vlc_mutex_lock( &p_mi->object_lock );
332  
333     p_mi->i_refcount--;
334
335     if( p_mi->i_refcount > 0 )
336     {
337         vlc_mutex_unlock( &p_mi->object_lock );
338         return;
339     }
340     vlc_mutex_unlock( &p_mi->object_lock );
341     vlc_mutex_destroy( &p_mi->object_lock );
342
343     release_input_thread( p_mi );
344
345     libvlc_event_manager_release( p_mi->p_event_manager );
346  
347     libvlc_media_descriptor_release( p_mi->p_md );
348
349     free( p_mi );
350 }
351
352 /**************************************************************************
353  * Retain a Media Instance object
354  **************************************************************************/
355 void libvlc_media_instance_retain( libvlc_media_instance_t *p_mi )
356 {
357     if( !p_mi )
358         return;
359
360     p_mi->i_refcount++;
361 }
362
363 /**************************************************************************
364  * Set the Media descriptor associated with the instance
365  **************************************************************************/
366 void libvlc_media_instance_set_media_descriptor(
367                             libvlc_media_instance_t *p_mi,
368                             libvlc_media_descriptor_t *p_md,
369                             libvlc_exception_t *p_e )
370 {
371     (void)p_e;
372
373     if( !p_mi )
374         return;
375
376     vlc_mutex_lock( &p_mi->object_lock );
377
378     release_input_thread( p_mi );
379
380     libvlc_media_descriptor_release( p_mi->p_md );
381
382     if( !p_md )
383     {
384         p_mi->p_md = NULL;
385         vlc_mutex_unlock( &p_mi->object_lock );
386         return; /* It is ok to pass a NULL md */
387     }
388
389     libvlc_media_descriptor_retain( p_md );
390     p_mi->p_md = p_md;
391  
392     /* The policy here is to ignore that we were created using a different
393      * libvlc_instance, because we don't really care */
394     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
395
396     vlc_mutex_unlock( &p_mi->object_lock );
397 }
398
399 /**************************************************************************
400  * Get the Media descriptor associated with the instance
401  **************************************************************************/
402 libvlc_media_descriptor_t *
403 libvlc_media_instance_get_media_descriptor(
404                             libvlc_media_instance_t *p_mi,
405                             libvlc_exception_t *p_e )
406 {
407     (void)p_e;
408
409     if( !p_mi->p_md )
410         return NULL;
411
412     libvlc_media_descriptor_retain( p_mi->p_md );
413     return p_mi->p_md;
414 }
415
416 /**************************************************************************
417  * Get the event Manager
418  **************************************************************************/
419 libvlc_event_manager_t *
420 libvlc_media_instance_event_manager(
421                             libvlc_media_instance_t *p_mi,
422                             libvlc_exception_t *p_e )
423 {
424     (void)p_e;
425
426     return p_mi->p_event_manager;
427 }
428
429 /**************************************************************************
430  * Play
431  **************************************************************************/
432 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
433                                  libvlc_exception_t *p_e )
434 {
435     input_thread_t * p_input_thread;
436
437     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
438     {
439         /* A thread alread exists, send it a play message */
440         input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
441         vlc_object_release( p_input_thread );
442         return;
443     }
444
445     /* Ignore previous exception */
446     libvlc_exception_clear( p_e );
447
448     vlc_mutex_lock( &p_mi->object_lock );
449  
450     if( !p_mi->p_md )
451     {
452         libvlc_exception_raise( p_e, "no associated media descriptor" );
453         vlc_mutex_unlock( &p_mi->object_lock );
454         return;
455     }
456
457     p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
458                                          p_mi->p_md->p_input_item );
459     p_mi->i_input_id = p_input_thread->i_object_id;
460
461     if( p_mi->drawable )
462     {
463         vlc_value_t val;
464         val.i_int = p_mi->drawable;
465         var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
466         var_Set( p_input_thread, "drawable", val );
467     }
468     var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
469     var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
470
471     /* will be released in media_instance_release() */
472     vlc_object_yield( p_input_thread );
473
474     vlc_mutex_unlock( &p_mi->object_lock );
475 }
476
477 /**************************************************************************
478  * Pause
479  **************************************************************************/
480 void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
481                                   libvlc_exception_t *p_e )
482 {
483     input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
484
485     if( !p_input_thread )
486         return;
487
488     input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
489     vlc_object_release( p_input_thread );
490 }
491
492 /**************************************************************************
493  * Stop
494  **************************************************************************/
495 void libvlc_media_instance_stop( libvlc_media_instance_t *p_mi,
496                                  libvlc_exception_t *p_e )
497 {
498     //libvlc_exception_raise( p_e, "Not implemented" );
499 }
500
501 /**************************************************************************
502  * Set Drawable
503  **************************************************************************/
504 void libvlc_media_instance_set_drawable( libvlc_media_instance_t *p_mi,
505                                          libvlc_drawable_t drawable,
506                                          libvlc_exception_t *p_e )
507 {
508     p_mi->drawable = drawable;
509 }
510
511 /**************************************************************************
512  * Getters for stream information
513  **************************************************************************/
514 vlc_int64_t libvlc_media_instance_get_length(
515                              libvlc_media_instance_t *p_mi,
516                              libvlc_exception_t *p_e )
517 {
518     input_thread_t *p_input_thread;
519     vlc_value_t val;
520
521     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
522     if( !p_input_thread )
523         return -1;
524
525     var_Get( p_input_thread, "length", &val );
526     vlc_object_release( p_input_thread );
527
528     return (val.i_time+500LL)/1000LL;
529 }
530
531 vlc_int64_t libvlc_media_instance_get_time(
532                                    libvlc_media_instance_t *p_mi,
533                                    libvlc_exception_t *p_e )
534 {
535     input_thread_t *p_input_thread;
536     vlc_value_t val;
537
538     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
539     if( !p_input_thread )
540         return -1;
541
542     var_Get( p_input_thread , "time", &val );
543     vlc_object_release( p_input_thread );
544     return (val.i_time+500LL)/1000LL;
545 }
546
547 void libvlc_media_instance_set_time(
548                                  libvlc_media_instance_t *p_mi,
549                                  vlc_int64_t time,
550                                  libvlc_exception_t *p_e )
551 {
552     input_thread_t *p_input_thread;
553     vlc_value_t value;
554
555     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
556     if( !p_input_thread )
557         return;
558
559     value.i_time = time*1000LL;
560     var_Set( p_input_thread, "time", value );
561     vlc_object_release( p_input_thread );
562 }
563
564 void libvlc_media_instance_set_position(
565                                 libvlc_media_instance_t *p_mi,
566                                 float position,
567                                 libvlc_exception_t *p_e )
568 {
569     input_thread_t *p_input_thread;
570     vlc_value_t val;
571     val.f_float = position;
572
573     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
574     if( !p_input_thread )
575         return;
576
577     var_Set( p_input_thread, "position", val );
578     vlc_object_release( p_input_thread );
579 }
580
581 float libvlc_media_instance_get_position(
582                                  libvlc_media_instance_t *p_mi,
583                                  libvlc_exception_t *p_e )
584 {
585     input_thread_t *p_input_thread;
586     vlc_value_t val;
587
588     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
589     if( !p_input_thread )
590         return -1.0;
591
592     var_Get( p_input_thread, "position", &val );
593     vlc_object_release( p_input_thread );
594
595     return val.f_float;
596 }
597
598 float libvlc_media_instance_get_fps(
599                                  libvlc_media_instance_t *p_mi,
600                                  libvlc_exception_t *p_e)
601 {
602     input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
603     double f_fps = 0.0;
604
605     if( p_input_thread )
606     {
607         if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
608             f_fps = 0.0;
609         vlc_object_release( p_input_thread );
610     }
611     return f_fps;
612 }
613
614 vlc_bool_t libvlc_media_instance_will_play(
615                                  libvlc_media_instance_t *p_mi,
616                                  libvlc_exception_t *p_e)
617 {
618     input_thread_t *p_input_thread =
619                             libvlc_get_input_thread ( p_mi, p_e);
620     if ( !p_input_thread )
621         return VLC_FALSE;
622
623     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
624     {
625         vlc_object_release( p_input_thread );
626         return VLC_TRUE;
627     }
628     vlc_object_release( p_input_thread );
629     return VLC_FALSE;
630 }
631
632 void libvlc_media_instance_set_rate(
633                                  libvlc_media_instance_t *p_mi,
634                                  float rate,
635                                  libvlc_exception_t *p_e )
636 {
637     input_thread_t *p_input_thread;
638     vlc_value_t val;
639
640     if( rate <= 0 )
641         RAISEVOID( "Rate value is invalid" );
642
643     val.i_int = 1000.0f/rate;
644
645     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
646     if ( !p_input_thread )
647         return;
648
649     var_Set( p_input_thread, "rate", val );
650     vlc_object_release( p_input_thread );
651 }
652
653 float libvlc_media_instance_get_rate(
654                                  libvlc_media_instance_t *p_mi,
655                                  libvlc_exception_t *p_e )
656 {
657     input_thread_t *p_input_thread;
658     vlc_value_t val;
659
660     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
661     if ( !p_input_thread )
662         return -1.0;
663
664     var_Get( p_input_thread, "rate", &val );
665     vlc_object_release( p_input_thread );
666
667     return (float)1000.0f/val.i_int;
668 }
669
670 static libvlc_state_t vlc_to_libvlc_state[] =
671 {
672     [INIT_S]        = libvlc_Opening,
673     [OPENING_S]     = libvlc_Opening,
674     [BUFFERING_S]   = libvlc_Buffering,    
675     [PLAYING_S]     = libvlc_Playing,    
676     [PAUSE_S]       = libvlc_Paused,    
677     [END_S]         = libvlc_Ended,    
678     [ERROR_S]       = libvlc_Error,    
679 };
680
681 libvlc_state_t libvlc_media_instance_get_state(
682                                  libvlc_media_instance_t *p_mi,
683                                  libvlc_exception_t *p_e )
684 {
685     input_thread_t *p_input_thread;
686     vlc_value_t val;
687
688     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
689     if ( !p_input_thread )
690         return libvlc_Stopped;
691
692     var_Get( p_input_thread, "state", &val );
693     vlc_object_release( p_input_thread );
694
695     if( val.i_int < 0 || val.i_int > 6 )
696         return libvlc_Stopped;
697
698     return vlc_to_libvlc_state[val.i_int];
699 }