]> git.sesse.net Git - vlc/blob - src/control/media_instance.c
src/control/media_instance.c: Create and Destroy the drawable variable.
[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
267     input_DestroyThread( p_input_thread );
268
269     libvlc_media_descriptor_release( p_mi->p_md );
270
271     free( p_mi );
272 }
273
274 /**************************************************************************
275  * Release a Media Instance object
276  **************************************************************************/
277 void libvlc_media_instance_release( libvlc_media_instance_t *p_mi )
278 {
279     if( !p_mi )
280         return;
281
282     vlc_mutex_lock( &p_mi->object_lock );
283     
284     p_mi->i_refcount--;
285
286     if( p_mi->i_refcount > 0 )
287     {
288         vlc_mutex_unlock( &p_mi->object_lock );
289         return;
290     }
291     vlc_mutex_unlock( &p_mi->object_lock );
292
293     libvlc_event_manager_release( p_mi->p_event_manager );
294     
295     release_input_thread( p_mi );
296
297     libvlc_media_descriptor_release( p_mi->p_md );
298
299     free( p_mi );
300 }
301
302 /**************************************************************************
303  * Retain a Media Instance object
304  **************************************************************************/
305 void libvlc_media_instance_retain( libvlc_media_instance_t *p_mi )
306 {
307     if( !p_mi )
308         return;
309
310     p_mi->i_refcount++;
311 }
312 /**************************************************************************
313  * Set the Media descriptor associated with the instance
314  **************************************************************************/
315 void libvlc_media_instance_set_media_descriptor(
316                             libvlc_media_instance_t *p_mi,
317                             libvlc_media_descriptor_t *p_md,
318                             libvlc_exception_t *p_e )
319 {
320     (void)p_e;
321
322     if( !p_mi )
323         return;
324
325     vlc_mutex_lock( &p_mi->object_lock );
326     
327     release_input_thread( p_mi );
328
329     libvlc_media_descriptor_release( p_mi->p_md );
330
331     if( !p_md )
332     {
333         p_mi->p_md = NULL;
334         vlc_mutex_unlock( &p_mi->object_lock );
335         return; /* It is ok to pass a NULL md */
336     }
337
338     p_mi->p_md = libvlc_media_descriptor_duplicate( p_md );
339     
340     /* The policy here is to ignore that we were created using a different
341      * libvlc_instance, because we don't really care */
342     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
343
344     vlc_mutex_unlock( &p_mi->object_lock );
345 }
346
347 /**************************************************************************
348  * Get the Media descriptor associated with the instance
349  **************************************************************************/
350 libvlc_media_descriptor_t *
351 libvlc_media_instance_get_media_descriptor(
352                             libvlc_media_instance_t *p_mi,
353                             libvlc_exception_t *p_e )
354 {
355     (void)p_e;
356
357     if( !p_mi->p_md )
358         return NULL;
359
360     return libvlc_media_descriptor_duplicate( p_mi->p_md );
361 }
362
363 /**************************************************************************
364  * Get the event Manager
365  **************************************************************************/
366 libvlc_event_manager_t *
367 libvlc_media_instance_event_manager(
368                             libvlc_media_instance_t *p_mi,
369                             libvlc_exception_t *p_e )
370 {
371     (void)p_e;
372
373     return p_mi->p_event_manager;
374 }
375
376 /**************************************************************************
377  * Play
378  **************************************************************************/
379 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
380                                  libvlc_exception_t *p_e )
381 {
382     input_thread_t * p_input_thread;
383
384     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) ) 
385     {
386         /* A thread alread exists, send it a play message */        
387         vlc_value_t val;
388         val.i_int = PLAYING_S;
389
390         input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, PLAYING_S );
391         vlc_object_release( p_input_thread );
392         return;
393     }
394
395     /* Ignore previous exception */
396     libvlc_exception_clear( p_e );
397
398     vlc_mutex_lock( &p_mi->object_lock );
399     
400     if( !p_mi->p_md )
401     {
402         libvlc_exception_raise( p_e, "no associated media descriptor" );
403         vlc_mutex_unlock( &p_mi->object_lock );
404         return;
405     }
406
407     p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
408                                          p_mi->p_md->p_input_item );
409     p_mi->i_input_id = p_input_thread->i_object_id;
410
411     if( p_mi->drawable )
412     {
413         vlc_value_t val;
414         val.i_int = p_mi->drawable;
415         var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
416         var_Set( p_input_thread, "drawable", val );
417     }
418     var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
419
420     /* will be released in media_instance_release() */
421     vlc_object_yield( p_input_thread );
422
423     vlc_mutex_unlock( &p_mi->object_lock );
424 }
425
426 /**************************************************************************
427  * Pause
428  **************************************************************************/
429 void libvlc_media_instance_pause( 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     val.i_int = PAUSE_S;
435
436     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
437
438     if( !p_input_thread )
439         return;
440
441     input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, val );
442     vlc_object_release( p_input_thread );
443 }
444
445 /**************************************************************************
446  * Stop
447  **************************************************************************/
448 void libvlc_media_instance_stop( libvlc_media_instance_t *p_mi,
449                                  libvlc_exception_t *p_e )
450 {
451     //libvlc_exception_raise( p_e, "Not implemented" );
452 }
453
454 /**************************************************************************
455  * Set Drawable
456  **************************************************************************/
457 void libvlc_media_instance_set_drawable( libvlc_media_instance_t *p_mi,
458                                          libvlc_drawable_t drawable,
459                                          libvlc_exception_t *p_e )
460 {
461     p_mi->drawable = drawable;
462 }
463
464 /**************************************************************************
465  * Getters for stream information
466  **************************************************************************/
467 vlc_int64_t libvlc_media_instance_get_length(
468                              libvlc_media_instance_t *p_mi,
469                              libvlc_exception_t *p_e )
470 {
471     input_thread_t *p_input_thread;
472     vlc_value_t val;
473
474     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
475     if( !p_input_thread )
476         return -1;
477
478     var_Get( p_input_thread, "length", &val );
479     vlc_object_release( p_input_thread );
480
481     return (val.i_time+500LL)/1000LL;
482 }
483
484 vlc_int64_t libvlc_media_instance_get_time(
485                                    libvlc_media_instance_t *p_mi,
486                                    libvlc_exception_t *p_e )
487 {
488     input_thread_t *p_input_thread;
489     vlc_value_t val;
490
491     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
492     if( !p_input_thread )
493         return -1;
494
495     var_Get( p_input_thread , "time", &val );
496     vlc_object_release( p_input_thread );
497     return (val.i_time+500LL)/1000LL;
498 }
499
500 void libvlc_media_instance_set_time(
501                                  libvlc_media_instance_t *p_mi,
502                                  vlc_int64_t time,
503                                  libvlc_exception_t *p_e )
504 {
505     input_thread_t *p_input_thread;
506     vlc_value_t value;
507
508     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
509     if( !p_input_thread )
510         return;
511
512     value.i_time = time*1000LL;
513     var_Set( p_input_thread, "time", value );
514     vlc_object_release( p_input_thread );
515 }
516
517 void libvlc_media_instance_set_position(
518                                 libvlc_media_instance_t *p_mi,
519                                 float position,
520                                 libvlc_exception_t *p_e ) 
521 {
522     input_thread_t *p_input_thread;
523     vlc_value_t val;
524     val.f_float = position;
525
526     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
527     if( !p_input_thread )
528         return;
529
530     var_Set( p_input_thread, "position", val );
531     vlc_object_release( p_input_thread );
532 }
533
534 float libvlc_media_instance_get_position(
535                                  libvlc_media_instance_t *p_mi,
536                                  libvlc_exception_t *p_e )
537 {
538     input_thread_t *p_input_thread;
539     vlc_value_t val;
540
541     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
542     if( !p_input_thread )
543         return -1.0;
544
545     var_Get( p_input_thread, "position", &val );
546     vlc_object_release( p_input_thread );
547
548     return val.f_float;
549 }
550
551 float libvlc_media_instance_get_fps(
552                                  libvlc_media_instance_t *p_mi,
553                                  libvlc_exception_t *p_e) 
554 {
555     double f_fps = 0.0;
556     input_thread_t *p_input_thread;
557
558     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
559     if( !p_input_thread )
560         return 0.0;
561
562     if( (NULL == p_input_thread->p->input.p_demux)
563         || demux2_Control( p_input_thread->p->input.p_demux, DEMUX_GET_FPS, &f_fps )
564         || f_fps < 0.1 )
565     {
566         vlc_object_release( p_input_thread );
567         return 0.0;
568     }
569     else
570     {
571         vlc_object_release( p_input_thread );
572         return( f_fps );
573     }
574 }
575
576 vlc_bool_t libvlc_media_instance_will_play(
577                                  libvlc_media_instance_t *p_mi,
578                                  libvlc_exception_t *p_e) 
579 {
580     input_thread_t *p_input_thread =
581                             libvlc_get_input_thread ( p_mi, p_e);
582     if ( !p_input_thread )
583         return VLC_FALSE;
584
585     if ( !p_input_thread->b_die && !p_input_thread->b_dead ) 
586     {
587         vlc_object_release( p_input_thread );
588         return VLC_TRUE;
589     }
590     vlc_object_release( p_input_thread );
591     return VLC_FALSE;
592 }
593
594 void libvlc_media_instance_set_rate(
595                                  libvlc_media_instance_t *p_mi,
596                                  float rate,
597                                  libvlc_exception_t *p_e ) 
598 {
599     input_thread_t *p_input_thread;
600     vlc_value_t val;
601
602     if( rate <= 0 )
603         RAISEVOID( "Rate value is invalid" );
604
605     val.i_int = 1000.0f/rate;
606
607     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
608     if ( !p_input_thread )
609         return;
610
611     var_Set( p_input_thread, "rate", val );
612     vlc_object_release( p_input_thread );
613 }
614
615 float libvlc_media_instance_get_rate(
616                                  libvlc_media_instance_t *p_mi,
617                                  libvlc_exception_t *p_e )
618 {
619     input_thread_t *p_input_thread;
620     vlc_value_t val;
621
622     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
623     if ( !p_input_thread )
624         return -1.0;
625
626     var_Get( p_input_thread, "rate", &val );
627     vlc_object_release( p_input_thread );
628
629     return (float)1000.0f/val.i_int;
630 }
631
632 int libvlc_media_instance_get_state(
633                                  libvlc_media_instance_t *p_mi,
634                                  libvlc_exception_t *p_e )
635 {
636     input_thread_t *p_input_thread;
637     vlc_value_t val;
638
639     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
640     if ( !p_input_thread )
641         return 0;
642
643     var_Get( p_input_thread, "state", &val );
644     vlc_object_release( p_input_thread );
645
646     return val.i_int;
647 }