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