]> git.sesse.net Git - vlc/blob - src/control/media_instance.c
control/media_instance.c: Fix the dead lock reported by Olivier Aubert.
[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     libvlc_exception_t p_e;
40
41     libvlc_exception_init( &p_e );
42
43     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
44
45     p_mi->i_input_id = -1;
46
47     if( libvlc_exception_raised( &p_e ) )
48         return;
49
50     /* release for previous libvlc_get_input_thread */
51     vlc_object_release( p_input_thread );
52
53     should_destroy = p_input_thread->i_refcount == 1;
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( should_destroy )
60         input_DestroyThread( p_input_thread );
61 }
62
63 /*
64  * Retrieve the input thread. Be sure to release the object
65  * once you are done with it. (libvlc Internal)
66  *
67  * Object lock is held.
68  */
69 input_thread_t *libvlc_get_input_thread( libvlc_media_instance_t *p_mi,
70                                          libvlc_exception_t *p_e ) 
71 {
72     input_thread_t *p_input_thread;
73
74     vlc_mutex_lock( &p_mi->object_lock );
75
76     if( !p_mi || p_mi->i_input_id == -1 )
77     {
78         vlc_mutex_unlock( &p_mi->object_lock );
79         RAISENULL( "Input is NULL" );
80     }
81
82     p_input_thread = (input_thread_t*)vlc_object_get(
83                                              p_mi->p_libvlc_instance->p_libvlc_int,
84                                              p_mi->i_input_id );
85     if( !p_input_thread )
86     {
87         vlc_mutex_unlock( &p_mi->object_lock );
88         RAISENULL( "Input does not exist" );
89     }
90
91     vlc_mutex_unlock( &p_mi->object_lock );
92     return p_input_thread;
93 }
94
95
96 /**************************************************************************
97  * Create a Media Instance object
98  **************************************************************************/
99 libvlc_media_instance_t *
100 libvlc_media_instance_new( libvlc_instance_t * p_libvlc_instance,
101                            libvlc_exception_t *p_e )
102 {
103     libvlc_media_instance_t * p_mi;
104
105     if( !p_libvlc_instance )
106     {
107         libvlc_exception_raise( p_e, "invalid libvlc instance" );
108         return NULL;
109     }
110
111     p_mi = malloc( sizeof(libvlc_media_instance_t) );
112     p_mi->p_md = NULL;
113     p_mi->p_libvlc_instance = p_libvlc_instance;
114     p_mi->i_input_id = -1;
115     /* refcount strategy:
116      * - All items created by _new start with a refcount set to 1
117      * - Accessor _release decrease the refcount by 1, if after that
118      *   operation the refcount is 0, the object is destroyed.
119      * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
120     p_mi->i_refcount = 1;
121     /* object_lock strategy:
122      * - No lock held in constructor
123      * - Lock when accessing all variable this lock is held
124      * - Lock when attempting to destroy the object the lock is also held */
125     vlc_mutex_init( p_mi->p_libvlc_instance->p_libvlc_int,
126                     &p_mi->object_lock );
127
128     return p_mi;
129 }
130
131 /**************************************************************************
132  * Create a Media Instance object with a media descriptor
133  **************************************************************************/
134 libvlc_media_instance_t *
135 libvlc_media_instance_new_from_media_descriptor(
136                                     libvlc_media_descriptor_t * p_md,
137                                     libvlc_exception_t *p_e )
138 {
139     libvlc_media_instance_t * p_mi;
140
141     if( !p_md )
142     {
143         libvlc_exception_raise( p_e, "invalid media descriptor" );
144         return NULL;
145     }
146
147     p_mi = malloc( sizeof(libvlc_media_instance_t) );
148     p_mi->p_md = libvlc_media_descriptor_duplicate( p_md );
149     p_mi->p_libvlc_instance = p_mi->p_md->p_libvlc_instance;
150     p_mi->i_input_id = -1;
151     /* same strategy as before */
152     p_mi->i_refcount = 1;
153     /* same strategy as before */
154     vlc_mutex_init( p_mi->p_libvlc_instance->p_libvlc_int,
155                     &p_mi->object_lock );
156
157     return p_mi;
158 }
159
160 /**************************************************************************
161  * Create a new media instance object from an input_thread (Libvlc Internal)
162  **************************************************************************/
163 libvlc_media_instance_t * libvlc_media_instance_new_from_input_thread(
164                                    struct libvlc_instance_t *p_libvlc_instance,
165                                    input_thread_t *p_input,
166                                    libvlc_exception_t *p_e )
167 {
168     libvlc_media_instance_t * p_mi;
169
170     if( !p_input )
171     {
172         libvlc_exception_raise( p_e, "invalid input thread" );
173         return NULL;
174     }
175
176     p_mi = malloc( sizeof(libvlc_media_instance_t) );
177     p_mi->p_md = libvlc_media_descriptor_new_from_input_item(
178                     p_libvlc_instance,
179                     p_input->p->input.p_item, p_e );
180
181     if( !p_mi->p_md )
182     {
183         free( p_mi );
184         return NULL;
185     }
186
187     p_mi->p_libvlc_instance = p_libvlc_instance;
188     p_mi->i_input_id = p_input->i_object_id;
189
190     /* will be released in media_instance_release() */
191     vlc_object_yield( p_input );
192
193     return p_mi;
194 }
195
196 /**************************************************************************
197  * Destroy a Media Instance object (libvlc internal)
198  *
199  * Warning: No lock held here, but hey, this is internal.
200  **************************************************************************/
201 void libvlc_media_instance_destroy( libvlc_media_instance_t *p_mi )
202 {
203     input_thread_t *p_input_thread;
204     libvlc_exception_t p_e;
205
206     libvlc_exception_init( &p_e );
207
208     if( !p_mi )
209         return;
210
211     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
212
213     if( libvlc_exception_raised( &p_e ) )
214         return; /* no need to worry about no input thread */
215     
216     input_DestroyThread( p_input_thread );
217
218     libvlc_media_descriptor_destroy( p_mi->p_md );
219
220     free( p_mi );
221 }
222
223 /**************************************************************************
224  * Release a Media Instance object
225  **************************************************************************/
226 void libvlc_media_instance_release( libvlc_media_instance_t *p_mi )
227 {
228     if( !p_mi )
229         return;
230
231     vlc_mutex_lock( &p_mi->object_lock );
232     
233     p_mi->i_refcount--;
234
235     /* We hold the mutex, as a waiter to make sure pending operations
236      * are finished. We can't hold it longer as the get_input_thread
237      * function holds a lock.  */
238
239     vlc_mutex_unlock( &p_mi->object_lock );
240     
241     if( p_mi->i_refcount > 0 )
242         return;
243
244     release_input_thread( p_mi );
245
246     libvlc_media_descriptor_destroy( p_mi->p_md );
247
248     vlc_mutex_unlock( &p_mi->object_lock );
249     free( p_mi );
250 }
251
252 /**************************************************************************
253  * Set the Media descriptor associated with the instance
254  **************************************************************************/
255 void libvlc_media_instance_set_media_descriptor(
256                             libvlc_media_instance_t *p_mi,
257                             libvlc_media_descriptor_t *p_md,
258                             libvlc_exception_t *p_e )
259 {
260     (void)p_e;
261
262     if( !p_mi )
263         return;
264
265     vlc_mutex_lock( &p_mi->object_lock );
266     
267     release_input_thread( p_mi );
268
269     libvlc_media_descriptor_destroy( p_mi->p_md );
270
271     if( !p_md )
272     {
273         p_mi->p_md = NULL;
274         vlc_mutex_unlock( &p_mi->object_lock );
275         return; /* It is ok to pass a NULL md */
276     }
277
278     p_mi->p_md = libvlc_media_descriptor_duplicate( p_md );
279     
280     /* The policy here is to ignore that we were created using a different
281      * libvlc_instance, because we don't really care */
282     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
283
284     vlc_mutex_unlock( &p_mi->object_lock );
285 }
286
287 /**************************************************************************
288  * Set the Media descriptor associated with the instance
289  **************************************************************************/
290 libvlc_media_descriptor_t *
291 libvlc_media_instance_get_media_descriptor(
292                             libvlc_media_instance_t *p_mi,
293                             libvlc_exception_t *p_e )
294 {
295     (void)p_e;
296
297     if( !p_mi->p_md )
298         return NULL;
299
300     return libvlc_media_descriptor_duplicate( p_mi->p_md );
301 }
302
303 /**************************************************************************
304  * Play
305  **************************************************************************/
306 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
307                                  libvlc_exception_t *p_e )
308 {
309     input_thread_t * p_input_thread;
310
311     vlc_mutex_lock( &p_mi->object_lock );
312
313     if( p_input_thread = libvlc_get_input_thread( p_mi, p_e ) ) 
314     {
315         /* A thread alread exists, send it a play message */        
316         vlc_value_t val;
317         val.i_int = PLAYING_S;
318
319         input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, PLAYING_S );
320         vlc_object_release( p_input_thread );
321         return;
322     }
323
324     vlc_mutex_lock( &p_mi->object_lock );
325     
326     if( !p_mi->p_md )
327     {
328         libvlc_exception_raise( p_e, "no associated media descriptor" );
329         vlc_mutex_unlock( &p_mi->object_lock );
330         return;
331     }
332
333     p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
334                                          p_mi->p_md->p_input_item );
335     p_mi->i_input_id = p_input_thread->i_object_id;
336
337     /* will be released in media_instance_release() */
338     vlc_object_yield( p_input_thread );
339
340     vlc_mutex_unlock( &p_mi->object_lock );
341 }
342
343 /**************************************************************************
344  * Pause
345  **************************************************************************/
346 void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
347                                   libvlc_exception_t *p_e )
348 {
349     input_thread_t * p_input_thread;
350     vlc_value_t val;
351     val.i_int = PAUSE_S;
352
353     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
354
355     if( !p_input_thread )
356         return;
357
358     input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, val );
359     vlc_object_release( p_input_thread );
360 }
361
362 /**************************************************************************
363  * Getters for stream information
364  **************************************************************************/
365 vlc_int64_t libvlc_media_instance_get_length(
366                              libvlc_media_instance_t *p_mi,
367                              libvlc_exception_t *p_e )
368 {
369     input_thread_t *p_input_thread;
370     vlc_value_t val;
371
372     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
373     if( !p_input_thread )
374         return -1;
375
376     var_Get( p_input_thread, "length", &val );
377     vlc_object_release( p_input_thread );
378
379     return (val.i_time+500LL)/1000LL;
380 }
381
382 vlc_int64_t libvlc_media_instance_get_time(
383                                    libvlc_media_instance_t *p_mi,
384                                    libvlc_exception_t *p_e )
385 {
386     input_thread_t *p_input_thread;
387     vlc_value_t val;
388
389     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
390     if( !p_input_thread )
391         return -1;
392
393     var_Get( p_input_thread , "time", &val );
394     vlc_object_release( p_input_thread );
395     return (val.i_time+500LL)/1000LL;
396 }
397
398 void libvlc_media_instance_set_time(
399                                  libvlc_media_instance_t *p_mi,
400                                  vlc_int64_t time,
401                                  libvlc_exception_t *p_e )
402 {
403     input_thread_t *p_input_thread;
404     vlc_value_t value;
405
406     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
407     if( !p_input_thread )
408         return;
409
410     value.i_time = time*1000LL;
411     var_Set( p_input_thread, "time", value );
412     vlc_object_release( p_input_thread );
413 }
414
415 void libvlc_media_instance_set_position(
416                                 libvlc_media_instance_t *p_mi,
417                                 float position,
418                                 libvlc_exception_t *p_e ) 
419 {
420     input_thread_t *p_input_thread;
421     vlc_value_t val;
422     val.f_float = position;
423
424     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
425     if( !p_input_thread )
426         return;
427
428     var_Set( p_input_thread, "position", val );
429     vlc_object_release( p_input_thread );
430 }
431
432 float libvlc_media_instance_get_position(
433                                  libvlc_media_instance_t *p_mi,
434                                  libvlc_exception_t *p_e )
435 {
436     input_thread_t *p_input_thread;
437     vlc_value_t val;
438
439     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
440     if( !p_input_thread )
441         return -1.0;
442
443     var_Get( p_input_thread, "position", &val );
444     vlc_object_release( p_input_thread );
445
446     return val.f_float;
447 }
448
449 float libvlc_media_instance_get_fps(
450                                  libvlc_media_instance_t *p_mi,
451                                  libvlc_exception_t *p_e) 
452 {
453     double f_fps = 0.0;
454     input_thread_t *p_input_thread;
455
456     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
457     if( !p_input_thread )
458         return 0.0;
459
460     if( (NULL == p_input_thread->p->input.p_demux)
461         || demux2_Control( p_input_thread->p->input.p_demux, DEMUX_GET_FPS, &f_fps )
462         || f_fps < 0.1 )
463     {
464         vlc_object_release( p_input_thread );
465         return 0.0;
466     }
467     else
468     {
469         vlc_object_release( p_input_thread );
470         return( f_fps );
471     }
472 }
473
474 vlc_bool_t libvlc_media_instance_will_play(
475                                  libvlc_media_instance_t *p_mi,
476                                  libvlc_exception_t *p_e) 
477 {
478     input_thread_t *p_input_thread =
479                             libvlc_get_input_thread ( p_mi, p_e);
480     if ( !p_input_thread )
481         return VLC_FALSE;
482
483     if ( !p_input_thread->b_die && !p_input_thread->b_dead ) 
484     {
485         vlc_object_release( p_input_thread );
486         return VLC_TRUE;
487     }
488     vlc_object_release( p_input_thread );
489     return VLC_FALSE;
490 }
491
492 void libvlc_media_instance_set_rate(
493                                  libvlc_media_instance_t *p_mi,
494                                  float rate,
495                                  libvlc_exception_t *p_e ) 
496 {
497     input_thread_t *p_input_thread;
498     vlc_value_t val;
499
500     if( rate <= 0 )
501         RAISEVOID( "Rate value is invalid" );
502
503     val.i_int = 1000.0f/rate;
504
505     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
506     if ( !p_input_thread )
507         return;
508
509     var_Set( p_input_thread, "rate", val );
510     vlc_object_release( p_input_thread );
511 }
512
513 float libvlc_media_instance_get_rate(
514                                  libvlc_media_instance_t *p_mi,
515                                  libvlc_exception_t *p_e )
516 {
517     input_thread_t *p_input_thread;
518     vlc_value_t val;
519
520     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
521     if ( !p_input_thread )
522         return -1.0;
523
524     var_Get( p_input_thread, "rate", &val );
525     vlc_object_release( p_input_thread );
526
527     return (float)1000.0f/val.i_int;
528 }
529
530 int libvlc_media_instance_get_state(
531                                  libvlc_media_instance_t *p_mi,
532                                  libvlc_exception_t *p_e )
533 {
534     input_thread_t *p_input_thread;
535     vlc_value_t val;
536
537     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
538     if ( !p_input_thread )
539         return 0;
540
541     var_Get( p_input_thread, "state", &val );
542     vlc_object_release( p_input_thread );
543
544     return val.i_int;
545 }
546