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