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