]> git.sesse.net Git - vlc/blob - src/control/media_instance.c
Libvlc: Make medi_instance thread safe and implement the basics for refcounting.
[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     if( p_mi->i_refcount > 0 )
235     {
236         vlc_mutex_unlock( &p_mi->object_lock );
237         return;
238     }
239
240     release_input_thread( p_mi );
241
242     libvlc_media_descriptor_destroy( p_mi->p_md );
243
244     vlc_mutex_unlock( &p_mi->object_lock );
245     free( p_mi );
246 }
247
248 /**************************************************************************
249  * Set the Media descriptor associated with the instance
250  **************************************************************************/
251 void libvlc_media_instance_set_media_descriptor(
252                             libvlc_media_instance_t *p_mi,
253                             libvlc_media_descriptor_t *p_md,
254                             libvlc_exception_t *p_e )
255 {
256     (void)p_e;
257
258     if( !p_mi )
259         return;
260
261     vlc_mutex_lock( &p_mi->object_lock );
262     
263     release_input_thread( p_mi );
264
265     libvlc_media_descriptor_destroy( p_mi->p_md );
266
267     if( !p_md )
268     {
269         p_mi->p_md = NULL;
270         vlc_mutex_unlock( &p_mi->object_lock );
271         return; /* It is ok to pass a NULL md */
272     }
273
274     p_mi->p_md = libvlc_media_descriptor_duplicate( p_md );
275     
276     /* The policy here is to ignore that we were created using a different
277      * libvlc_instance, because we don't really care */
278     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
279
280     vlc_mutex_unlock( &p_mi->object_lock );
281 }
282
283 /**************************************************************************
284  * Set the Media descriptor associated with the instance
285  **************************************************************************/
286 libvlc_media_descriptor_t *
287 libvlc_media_instance_get_media_descriptor(
288                             libvlc_media_instance_t *p_mi,
289                             libvlc_exception_t *p_e )
290 {
291     (void)p_e;
292
293     if( !p_mi->p_md )
294         return NULL;
295
296     return libvlc_media_descriptor_duplicate( p_mi->p_md );
297 }
298
299 /**************************************************************************
300  * Play
301  **************************************************************************/
302 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
303                                  libvlc_exception_t *p_e )
304 {
305     input_thread_t * p_input_thread;
306
307     vlc_mutex_lock( &p_mi->object_lock );
308
309     if( p_input_thread = libvlc_get_input_thread( p_mi, p_e ) ) 
310     {
311         /* A thread alread exists, send it a play message */        
312         vlc_value_t val;
313         val.i_int = PLAYING_S;
314
315         input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, PLAYING_S );
316         vlc_object_release( p_input_thread );
317         return;
318     }
319
320     vlc_mutex_lock( &p_mi->object_lock );
321     
322     if( !p_mi->p_md )
323     {
324         libvlc_exception_raise( p_e, "no associated media descriptor" );
325         vlc_mutex_unlock( &p_mi->object_lock );
326         return;
327     }
328
329     p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
330                                          p_mi->p_md->p_input_item );
331     p_mi->i_input_id = p_input_thread->i_object_id;
332
333     /* will be released in media_instance_release() */
334     vlc_object_yield( p_input_thread );
335
336     vlc_mutex_unlock( &p_mi->object_lock );
337 }
338
339 /**************************************************************************
340  * Pause
341  **************************************************************************/
342 void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
343                                   libvlc_exception_t *p_e )
344 {
345     input_thread_t * p_input_thread;
346     vlc_value_t val;
347     val.i_int = PAUSE_S;
348
349     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
350
351     if( !p_input_thread )
352         return;
353
354     input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, val );
355     vlc_object_release( p_input_thread );
356 }
357
358 /**************************************************************************
359  * Getters for stream information
360  **************************************************************************/
361 vlc_int64_t libvlc_media_instance_get_length(
362                              libvlc_media_instance_t *p_mi,
363                              libvlc_exception_t *p_e )
364 {
365     input_thread_t *p_input_thread;
366     vlc_value_t val;
367
368     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
369     if( !p_input_thread )
370         return -1;
371
372     var_Get( p_input_thread, "length", &val );
373     vlc_object_release( p_input_thread );
374
375     return (val.i_time+500LL)/1000LL;
376 }
377
378 vlc_int64_t libvlc_media_instance_get_time(
379                                    libvlc_media_instance_t *p_mi,
380                                    libvlc_exception_t *p_e )
381 {
382     input_thread_t *p_input_thread;
383     vlc_value_t val;
384
385     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
386     if( !p_input_thread )
387         return -1;
388
389     var_Get( p_input_thread , "time", &val );
390     vlc_object_release( p_input_thread );
391     return (val.i_time+500LL)/1000LL;
392 }
393
394 void libvlc_media_instance_set_time(
395                                  libvlc_media_instance_t *p_mi,
396                                  vlc_int64_t time,
397                                  libvlc_exception_t *p_e )
398 {
399     input_thread_t *p_input_thread;
400     vlc_value_t value;
401
402     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
403     if( !p_input_thread )
404         return;
405
406     value.i_time = time*1000LL;
407     var_Set( p_input_thread, "time", value );
408     vlc_object_release( p_input_thread );
409 }
410
411 void libvlc_media_instance_set_position(
412                                 libvlc_media_instance_t *p_mi,
413                                 float position,
414                                 libvlc_exception_t *p_e ) 
415 {
416     input_thread_t *p_input_thread;
417     vlc_value_t val;
418     val.f_float = position;
419
420     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
421     if( !p_input_thread )
422         return;
423
424     var_Set( p_input_thread, "position", val );
425     vlc_object_release( p_input_thread );
426 }
427
428 float libvlc_media_instance_get_position(
429                                  libvlc_media_instance_t *p_mi,
430                                  libvlc_exception_t *p_e )
431 {
432     input_thread_t *p_input_thread;
433     vlc_value_t val;
434
435     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
436     if( !p_input_thread )
437         return -1.0;
438
439     var_Get( p_input_thread, "position", &val );
440     vlc_object_release( p_input_thread );
441
442     return val.f_float;
443 }
444
445 float libvlc_media_instance_get_fps(
446                                  libvlc_media_instance_t *p_mi,
447                                  libvlc_exception_t *p_e) 
448 {
449     double f_fps = 0.0;
450     input_thread_t *p_input_thread;
451
452     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
453     if( !p_input_thread )
454         return 0.0;
455
456     if( (NULL == p_input_thread->p->input.p_demux)
457         || demux2_Control( p_input_thread->p->input.p_demux, DEMUX_GET_FPS, &f_fps )
458         || f_fps < 0.1 )
459     {
460         vlc_object_release( p_input_thread );
461         return 0.0;
462     }
463     else
464     {
465         vlc_object_release( p_input_thread );
466         return( f_fps );
467     }
468 }
469
470 vlc_bool_t libvlc_media_instance_will_play(
471                                  libvlc_media_instance_t *p_mi,
472                                  libvlc_exception_t *p_e) 
473 {
474     input_thread_t *p_input_thread =
475                             libvlc_get_input_thread ( p_mi, p_e);
476     if ( !p_input_thread )
477         return VLC_FALSE;
478
479     if ( !p_input_thread->b_die && !p_input_thread->b_dead ) 
480     {
481         vlc_object_release( p_input_thread );
482         return VLC_TRUE;
483     }
484     vlc_object_release( p_input_thread );
485     return VLC_FALSE;
486 }
487
488 void libvlc_media_instance_set_rate(
489                                  libvlc_media_instance_t *p_mi,
490                                  float rate,
491                                  libvlc_exception_t *p_e ) 
492 {
493     input_thread_t *p_input_thread;
494     vlc_value_t val;
495
496     if( rate <= 0 )
497         RAISEVOID( "Rate value is invalid" );
498
499     val.i_int = 1000.0f/rate;
500
501     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
502     if ( !p_input_thread )
503         return;
504
505     var_Set( p_input_thread, "rate", val );
506     vlc_object_release( p_input_thread );
507 }
508
509 float libvlc_media_instance_get_rate(
510                                  libvlc_media_instance_t *p_mi,
511                                  libvlc_exception_t *p_e )
512 {
513     input_thread_t *p_input_thread;
514     vlc_value_t val;
515
516     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
517     if ( !p_input_thread )
518         return -1.0;
519
520     var_Get( p_input_thread, "rate", &val );
521     vlc_object_release( p_input_thread );
522
523     return (float)1000.0f/val.i_int;
524 }
525
526 int libvlc_media_instance_get_state(
527                                  libvlc_media_instance_t *p_mi,
528                                  libvlc_exception_t *p_e )
529 {
530     input_thread_t *p_input_thread;
531     vlc_value_t val;
532
533     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
534     if ( !p_input_thread )
535         return 0;
536
537     var_Get( p_input_thread, "state", &val );
538     vlc_object_release( p_input_thread );
539
540     return val.i_int;
541 }
542