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