]> git.sesse.net Git - vlc/blob - src/control/media_instance.c
Removes trailing spaces. Removes tabs.
[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 <vlc/libvlc.h>
25 #include <vlc_demux.h>
26 #include <vlc_input.h>
27 #include "input/input_internal.h"
28 #include "libvlc_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         input_StopThread( p_input_thread );
65         var_Destroy( p_input_thread, "drawable" );
66         input_DestroyThread( p_input_thread );
67     }
68     else
69     {
70         /* XXX: hack the playlist doesn't retain the input thread,
71          * so we did it for the playlist (see _new_from_input_thread),
72          * revert that here. */
73         vlc_object_release( p_input_thread );
74     }
75 }
76
77 /*
78  * Retrieve the input thread. Be sure to release the object
79  * once you are done with it. (libvlc Internal)
80  *
81  * Object lock is held.
82  */
83 input_thread_t *libvlc_get_input_thread( libvlc_media_instance_t *p_mi,
84                                          libvlc_exception_t *p_e )
85 {
86     input_thread_t *p_input_thread;
87
88     vlc_mutex_lock( &p_mi->object_lock );
89
90     if( !p_mi || p_mi->i_input_id == -1 )
91     {
92         vlc_mutex_unlock( &p_mi->object_lock );
93         RAISENULL( "Input is NULL" );
94     }
95
96     p_input_thread = (input_thread_t*)vlc_object_get(
97                                              p_mi->p_libvlc_instance->p_libvlc_int,
98                                              p_mi->i_input_id );
99     if( !p_input_thread )
100     {
101         vlc_mutex_unlock( &p_mi->object_lock );
102         RAISENULL( "Input does not exist" );
103     }
104
105     vlc_mutex_unlock( &p_mi->object_lock );
106     return p_input_thread;
107 }
108
109 /*
110  * input_state_changed (Private) (input var "state" Callback)
111  */
112 static int
113 input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
114                      vlc_value_t oldval, vlc_value_t newval,
115                      void * p_userdata )
116 {
117     libvlc_media_instance_t * p_mi = p_userdata;
118     libvlc_event_t event;
119
120     if( newval.i_int == oldval.i_int )
121         return VLC_SUCCESS; /* No change since last time, don't propagate */
122
123     switch ( newval.i_int )
124     {
125         case END_S:
126             event.type = libvlc_MediaInstanceReachedEnd;
127             break;
128         case PAUSE_S:
129             event.type = libvlc_MediaInstancePaused;
130             break;
131         case PLAYING_S:
132             event.type = libvlc_MediaInstancePlayed;
133             break;
134         default:
135             return VLC_SUCCESS;
136     }
137
138     libvlc_event_send( p_mi->p_event_manager, &event );
139     return VLC_SUCCESS;
140 }
141
142 /*
143  * input_position_changed (Private) (input var "intf-change" Callback)
144  */
145 static int
146 input_position_changed( vlc_object_t * p_this, char const * psz_cmd,
147                      vlc_value_t oldval, vlc_value_t newval,
148                      void * p_userdata )
149 {
150     libvlc_media_instance_t * p_mi = p_userdata;
151     vlc_value_t val;
152  
153     if (!strcmp(psz_cmd, "intf" /* "-change" no need to go further */))
154     {
155         input_thread_t * p_input = (input_thread_t *)p_this;
156         var_Get( p_input, "position", &val );
157
158         if ((val.i_time % I64C(500000)) != 0)
159             return VLC_SUCCESS; /* No need to have a better precision */
160         var_Get( p_input, "state", &val );
161
162         if( val.i_int != PLAYING_S )
163             return VLC_SUCCESS; /* Don't send the position while stopped */
164     }
165     else
166         val.i_time = newval.i_time;
167
168     libvlc_event_t event;
169     event.type = libvlc_MediaInstancePositionChanged;
170     event.u.media_instance_position_changed.new_position = val.i_time;
171
172     libvlc_event_send( p_mi->p_event_manager, &event );
173     return VLC_SUCCESS;
174 }
175
176 /**************************************************************************
177  * Create a Media Instance object
178  **************************************************************************/
179 libvlc_media_instance_t *
180 libvlc_media_instance_new( libvlc_instance_t * p_libvlc_instance,
181                            libvlc_exception_t * p_e )
182 {
183     libvlc_media_instance_t * p_mi;
184
185     if( !p_libvlc_instance )
186     {
187         libvlc_exception_raise( p_e, "invalid libvlc instance" );
188         return NULL;
189     }
190
191     p_mi = malloc( sizeof(libvlc_media_instance_t) );
192     p_mi->p_md = NULL;
193     p_mi->drawable = 0;
194     p_mi->p_libvlc_instance = p_libvlc_instance;
195     p_mi->i_input_id = -1;
196     /* refcount strategy:
197      * - All items created by _new start with a refcount set to 1
198      * - Accessor _release decrease the refcount by 1, if after that
199      *   operation the refcount is 0, the object is destroyed.
200      * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
201     p_mi->i_refcount = 1;
202     /* object_lock strategy:
203      * - No lock held in constructor
204      * - Lock when accessing all variable this lock is held
205      * - Lock when attempting to destroy the object the lock is also held */
206     vlc_mutex_init( p_mi->p_libvlc_instance->p_libvlc_int,
207                     &p_mi->object_lock );
208     p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
209             p_libvlc_instance, p_e );
210     if( libvlc_exception_raised( p_e ) )
211     {
212         free( p_mi );
213         return NULL;
214     }
215  
216     libvlc_event_manager_register_event_type( p_mi->p_event_manager,
217             libvlc_MediaInstanceReachedEnd, p_e );
218
219     return p_mi;
220 }
221
222 /**************************************************************************
223  * Create a Media Instance object with a media descriptor
224  **************************************************************************/
225 libvlc_media_instance_t *
226 libvlc_media_instance_new_from_media_descriptor(
227                                     libvlc_media_descriptor_t * p_md,
228                                     libvlc_exception_t *p_e )
229 {
230     libvlc_media_instance_t * p_mi;
231     p_mi = libvlc_media_instance_new( p_md->p_libvlc_instance, p_e );
232
233     if( !p_mi )
234         return NULL;
235
236     libvlc_media_descriptor_retain( p_md );
237     p_mi->p_md = p_md;
238
239     return p_mi;
240 }
241
242 /**************************************************************************
243  * Create a new media instance object from an input_thread (Libvlc Internal)
244  **************************************************************************/
245 libvlc_media_instance_t * libvlc_media_instance_new_from_input_thread(
246                                    struct libvlc_instance_t *p_libvlc_instance,
247                                    input_thread_t *p_input,
248                                    libvlc_exception_t *p_e )
249 {
250     libvlc_media_instance_t * p_mi;
251
252     if( !p_input )
253     {
254         libvlc_exception_raise( p_e, "invalid input thread" );
255         return NULL;
256     }
257
258     p_mi = libvlc_media_instance_new( p_libvlc_instance, p_e );
259
260     if( !p_mi )
261         return NULL;
262
263     p_mi->p_md = libvlc_media_descriptor_new_from_input_item(
264                     p_libvlc_instance,
265                     p_input->p->input.p_item, p_e );
266
267     if( !p_mi->p_md )
268     {
269         libvlc_media_instance_destroy( p_mi );
270         return NULL;
271     }
272
273     p_mi->i_input_id = p_input->i_object_id;
274  
275     /* will be released in media_instance_release() */
276     vlc_object_yield( p_input );
277
278     /* XXX: Hack as the playlist doesn't yield the input thread we retain
279      * the input for the playlist. (see corresponding hack in _release) */
280     vlc_object_yield( p_input );
281
282     return p_mi;
283 }
284
285 /**************************************************************************
286  * Destroy a Media Instance object (libvlc internal)
287  *
288  * Warning: No lock held here, but hey, this is internal.
289  **************************************************************************/
290 void libvlc_media_instance_destroy( libvlc_media_instance_t *p_mi )
291 {
292     input_thread_t *p_input_thread;
293     libvlc_exception_t p_e;
294
295     libvlc_exception_init( &p_e );
296
297     if( !p_mi )
298         return;
299
300     p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
301
302     if( libvlc_exception_raised( &p_e ) )
303     {
304         libvlc_event_manager_release( p_mi->p_event_manager );
305         free( p_mi );
306         return; /* no need to worry about no input thread */
307     }
308     vlc_mutex_destroy( &p_mi->object_lock );
309
310     input_DestroyThread( p_input_thread );
311
312     libvlc_media_descriptor_release( p_mi->p_md );
313
314     free( p_mi );
315 }
316
317 /**************************************************************************
318  * Release a Media Instance object
319  **************************************************************************/
320 void libvlc_media_instance_release( libvlc_media_instance_t *p_mi )
321 {
322     if( !p_mi )
323         return;
324
325     vlc_mutex_lock( &p_mi->object_lock );
326  
327     p_mi->i_refcount--;
328
329     if( p_mi->i_refcount > 0 )
330     {
331         vlc_mutex_unlock( &p_mi->object_lock );
332         return;
333     }
334     vlc_mutex_unlock( &p_mi->object_lock );
335     vlc_mutex_destroy( &p_mi->object_lock );
336
337     libvlc_event_manager_release( p_mi->p_event_manager );
338  
339     release_input_thread( p_mi );
340
341     libvlc_media_descriptor_release( p_mi->p_md );
342
343     free( p_mi );
344 }
345
346 /**************************************************************************
347  * Retain a Media Instance object
348  **************************************************************************/
349 void libvlc_media_instance_retain( libvlc_media_instance_t *p_mi )
350 {
351     if( !p_mi )
352         return;
353
354     p_mi->i_refcount++;
355 }
356 /**************************************************************************
357  * Set the Media descriptor associated with the instance
358  **************************************************************************/
359 void libvlc_media_instance_set_media_descriptor(
360                             libvlc_media_instance_t *p_mi,
361                             libvlc_media_descriptor_t *p_md,
362                             libvlc_exception_t *p_e )
363 {
364     (void)p_e;
365
366     if( !p_mi )
367         return;
368
369     vlc_mutex_lock( &p_mi->object_lock );
370
371     release_input_thread( p_mi );
372
373     libvlc_media_descriptor_release( p_mi->p_md );
374
375     if( !p_md )
376     {
377         p_mi->p_md = NULL;
378         vlc_mutex_unlock( &p_mi->object_lock );
379         return; /* It is ok to pass a NULL md */
380     }
381
382     libvlc_media_descriptor_retain( p_md );
383     p_mi->p_md = p_md;
384  
385     /* The policy here is to ignore that we were created using a different
386      * libvlc_instance, because we don't really care */
387     p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
388
389     vlc_mutex_unlock( &p_mi->object_lock );
390 }
391
392 /**************************************************************************
393  * Get the Media descriptor associated with the instance
394  **************************************************************************/
395 libvlc_media_descriptor_t *
396 libvlc_media_instance_get_media_descriptor(
397                             libvlc_media_instance_t *p_mi,
398                             libvlc_exception_t *p_e )
399 {
400     (void)p_e;
401
402     if( !p_mi->p_md )
403         return NULL;
404
405     libvlc_media_descriptor_retain( p_mi->p_md );
406     return p_mi->p_md;
407 }
408
409 /**************************************************************************
410  * Get the event Manager
411  **************************************************************************/
412 libvlc_event_manager_t *
413 libvlc_media_instance_event_manager(
414                             libvlc_media_instance_t *p_mi,
415                             libvlc_exception_t *p_e )
416 {
417     (void)p_e;
418
419     return p_mi->p_event_manager;
420 }
421
422 /**************************************************************************
423  * Play
424  **************************************************************************/
425 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
426                                  libvlc_exception_t *p_e )
427 {
428     input_thread_t * p_input_thread;
429
430     if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
431     {
432         /* A thread alread exists, send it a play message */
433         vlc_value_t val;
434         val.i_int = PLAYING_S;
435
436         input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, PLAYING_S );
437         vlc_object_release( p_input_thread );
438         return;
439     }
440
441     /* Ignore previous exception */
442     libvlc_exception_clear( p_e );
443
444     vlc_mutex_lock( &p_mi->object_lock );
445  
446     if( !p_mi->p_md )
447     {
448         libvlc_exception_raise( p_e, "no associated media descriptor" );
449         vlc_mutex_unlock( &p_mi->object_lock );
450         return;
451     }
452
453     p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
454                                          p_mi->p_md->p_input_item );
455     p_mi->i_input_id = p_input_thread->i_object_id;
456
457     if( p_mi->drawable )
458     {
459         vlc_value_t val;
460         val.i_int = p_mi->drawable;
461         var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
462         var_Set( p_input_thread, "drawable", val );
463     }
464     var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
465     var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
466
467     /* will be released in media_instance_release() */
468     vlc_object_yield( p_input_thread );
469
470     vlc_mutex_unlock( &p_mi->object_lock );
471 }
472
473 /**************************************************************************
474  * Pause
475  **************************************************************************/
476 void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
477                                   libvlc_exception_t *p_e )
478 {
479     input_thread_t * p_input_thread;
480     vlc_value_t val;
481     val.i_int = PAUSE_S;
482
483     p_input_thread = libvlc_get_input_thread( p_mi, p_e );
484
485     if( !p_input_thread )
486         return;
487
488     input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, val );
489     vlc_object_release( p_input_thread );
490 }
491
492 /**************************************************************************
493  * Stop
494  **************************************************************************/
495 void libvlc_media_instance_stop( libvlc_media_instance_t *p_mi,
496                                  libvlc_exception_t *p_e )
497 {
498     //libvlc_exception_raise( p_e, "Not implemented" );
499 }
500
501 /**************************************************************************
502  * Set Drawable
503  **************************************************************************/
504 void libvlc_media_instance_set_drawable( libvlc_media_instance_t *p_mi,
505                                          libvlc_drawable_t drawable,
506                                          libvlc_exception_t *p_e )
507 {
508     p_mi->drawable = drawable;
509 }
510
511 /**************************************************************************
512  * Getters for stream information
513  **************************************************************************/
514 vlc_int64_t libvlc_media_instance_get_length(
515                              libvlc_media_instance_t *p_mi,
516                              libvlc_exception_t *p_e )
517 {
518     input_thread_t *p_input_thread;
519     vlc_value_t val;
520
521     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
522     if( !p_input_thread )
523         return -1;
524
525     var_Get( p_input_thread, "length", &val );
526     vlc_object_release( p_input_thread );
527
528     return (val.i_time+500LL)/1000LL;
529 }
530
531 vlc_int64_t libvlc_media_instance_get_time(
532                                    libvlc_media_instance_t *p_mi,
533                                    libvlc_exception_t *p_e )
534 {
535     input_thread_t *p_input_thread;
536     vlc_value_t val;
537
538     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
539     if( !p_input_thread )
540         return -1;
541
542     var_Get( p_input_thread , "time", &val );
543     vlc_object_release( p_input_thread );
544     return (val.i_time+500LL)/1000LL;
545 }
546
547 void libvlc_media_instance_set_time(
548                                  libvlc_media_instance_t *p_mi,
549                                  vlc_int64_t time,
550                                  libvlc_exception_t *p_e )
551 {
552     input_thread_t *p_input_thread;
553     vlc_value_t value;
554
555     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
556     if( !p_input_thread )
557         return;
558
559     value.i_time = time*1000LL;
560     var_Set( p_input_thread, "time", value );
561     vlc_object_release( p_input_thread );
562 }
563
564 void libvlc_media_instance_set_position(
565                                 libvlc_media_instance_t *p_mi,
566                                 float position,
567                                 libvlc_exception_t *p_e )
568 {
569     input_thread_t *p_input_thread;
570     vlc_value_t val;
571     val.f_float = position;
572
573     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
574     if( !p_input_thread )
575         return;
576
577     var_Set( p_input_thread, "position", val );
578     vlc_object_release( p_input_thread );
579 }
580
581 float libvlc_media_instance_get_position(
582                                  libvlc_media_instance_t *p_mi,
583                                  libvlc_exception_t *p_e )
584 {
585     input_thread_t *p_input_thread;
586     vlc_value_t val;
587
588     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
589     if( !p_input_thread )
590         return -1.0;
591
592     var_Get( p_input_thread, "position", &val );
593     vlc_object_release( p_input_thread );
594
595     return val.f_float;
596 }
597
598 float libvlc_media_instance_get_fps(
599                                  libvlc_media_instance_t *p_mi,
600                                  libvlc_exception_t *p_e)
601 {
602     double f_fps = 0.0;
603     input_thread_t *p_input_thread;
604
605     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
606     if( !p_input_thread )
607         return 0.0;
608
609     if( (NULL == p_input_thread->p->input.p_demux)
610         || demux2_Control( p_input_thread->p->input.p_demux, DEMUX_GET_FPS, &f_fps )
611         || f_fps < 0.1 )
612     {
613         vlc_object_release( p_input_thread );
614         return 0.0;
615     }
616     else
617     {
618         vlc_object_release( p_input_thread );
619         return( f_fps );
620     }
621 }
622
623 vlc_bool_t libvlc_media_instance_will_play(
624                                  libvlc_media_instance_t *p_mi,
625                                  libvlc_exception_t *p_e)
626 {
627     input_thread_t *p_input_thread =
628                             libvlc_get_input_thread ( p_mi, p_e);
629     if ( !p_input_thread )
630         return VLC_FALSE;
631
632     if ( !p_input_thread->b_die && !p_input_thread->b_dead )
633     {
634         vlc_object_release( p_input_thread );
635         return VLC_TRUE;
636     }
637     vlc_object_release( p_input_thread );
638     return VLC_FALSE;
639 }
640
641 void libvlc_media_instance_set_rate(
642                                  libvlc_media_instance_t *p_mi,
643                                  float rate,
644                                  libvlc_exception_t *p_e )
645 {
646     input_thread_t *p_input_thread;
647     vlc_value_t val;
648
649     if( rate <= 0 )
650         RAISEVOID( "Rate value is invalid" );
651
652     val.i_int = 1000.0f/rate;
653
654     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
655     if ( !p_input_thread )
656         return;
657
658     var_Set( p_input_thread, "rate", val );
659     vlc_object_release( p_input_thread );
660 }
661
662 float libvlc_media_instance_get_rate(
663                                  libvlc_media_instance_t *p_mi,
664                                  libvlc_exception_t *p_e )
665 {
666     input_thread_t *p_input_thread;
667     vlc_value_t val;
668
669     p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
670     if ( !p_input_thread )
671         return -1.0;
672
673     var_Get( p_input_thread, "rate", &val );
674     vlc_object_release( p_input_thread );
675
676     return (float)1000.0f/val.i_int;
677 }
678
679 int libvlc_media_instance_get_state(
680                                  libvlc_media_instance_t *p_mi,
681                                  libvlc_exception_t *p_e )
682 {
683     input_thread_t *p_input_thread;
684     vlc_value_t val;
685
686     p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
687     if ( !p_input_thread )
688         return 0;
689
690     var_Get( p_input_thread, "state", &val );
691     vlc_object_release( p_input_thread );
692
693     return val.i_int;
694 }