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