1 /*****************************************************************************
2 * media_instance.c: Libvlc API Media Instance management functions
3 *****************************************************************************
4 * Copyright (C) 2005 the VideoLAN team
7 * Authors: Clément Stenac <zorglub@videolan.org>
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.
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.
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 *****************************************************************************/
24 #include <vlc/libvlc.h>
25 #include <vlc_demux.h>
26 #include <vlc_input.h>
27 #include "libvlc_internal.h"
30 static const libvlc_state_t vlc_to_libvlc_state_array[] =
32 [INIT_S] = libvlc_Opening,
33 [OPENING_S] = libvlc_Opening,
34 [BUFFERING_S] = libvlc_Buffering,
35 [PLAYING_S] = libvlc_Playing,
36 [PAUSE_S] = libvlc_Paused,
37 [END_S] = libvlc_Ended,
38 [ERROR_S] = libvlc_Error,
40 static inline libvlc_state_t vlc_to_libvlc_state( int vlc_state )
42 if( vlc_state < 0 || vlc_state > 6 )
43 return libvlc_Stopped;
45 return vlc_to_libvlc_state_array[vlc_state];
49 * Release the associated input thread
51 * Object lock is NOT held.
53 static void release_input_thread( libvlc_media_instance_t *p_mi )
55 input_thread_t *p_input_thread;
57 if( !p_mi || p_mi->i_input_id == -1 )
60 p_input_thread = (input_thread_t*)vlc_object_get(
61 p_mi->p_libvlc_instance->p_libvlc_int,
64 p_mi->i_input_id = -1;
69 /* release for previous vlc_object_get */
70 vlc_object_release( p_input_thread );
72 /* release for initial p_input_thread yield (see _new()) */
73 vlc_object_release( p_input_thread );
75 /* No one is tracking this input_thread appart us. Destroy it */
76 if( p_mi->b_own_its_input_thread )
78 /* We owned this one */
79 input_StopThread( p_input_thread );
80 var_Destroy( p_input_thread, "drawable" );
81 input_DestroyThread( p_input_thread );
85 /* XXX: hack the playlist doesn't retain the input thread,
86 * so we did it for the playlist (see _new_from_input_thread),
87 * revert that here. This will be deleted with the playlist API */
88 vlc_object_release( p_input_thread );
93 * Retrieve the input thread. Be sure to release the object
94 * once you are done with it. (libvlc Internal)
96 * Object lock is held.
98 input_thread_t *libvlc_get_input_thread( libvlc_media_instance_t *p_mi,
99 libvlc_exception_t *p_e )
101 input_thread_t *p_input_thread;
103 vlc_mutex_lock( &p_mi->object_lock );
105 if( !p_mi || p_mi->i_input_id == -1 )
107 vlc_mutex_unlock( &p_mi->object_lock );
108 RAISENULL( "Input is NULL" );
111 p_input_thread = (input_thread_t*)vlc_object_get(
112 p_mi->p_libvlc_instance->p_libvlc_int,
114 if( !p_input_thread )
116 vlc_mutex_unlock( &p_mi->object_lock );
117 RAISENULL( "Input does not exist" );
120 vlc_mutex_unlock( &p_mi->object_lock );
121 return p_input_thread;
125 * input_state_changed (Private) (input var "state" Callback)
128 input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
129 vlc_value_t oldval, vlc_value_t newval,
132 libvlc_media_instance_t * p_mi = p_userdata;
133 libvlc_event_t event;
135 printf("input_state_changed!!!!!!!!\n");
136 if( newval.i_int == oldval.i_int )
137 return VLC_SUCCESS; /* No change since last time, don't propagate */
139 switch ( newval.i_int )
142 libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_NothingSpecial, NULL);
143 event.type = libvlc_MediaInstanceReachedEnd;
146 libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Playing, NULL);
147 event.type = libvlc_MediaInstancePaused;
150 printf("PLAYING_S!!!!!!!!\n");
151 libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Playing, NULL);
152 event.type = libvlc_MediaInstancePlayed;
155 libvlc_media_descriptor_set_state( p_mi->p_md, libvlc_Error, NULL);
156 event.type = libvlc_MediaInstancePlayed;
162 libvlc_event_send( p_mi->p_event_manager, &event );
167 * input_position_changed (Private) (input var "intf-change" Callback)
170 input_position_changed( vlc_object_t * p_this, char const * psz_cmd,
171 vlc_value_t oldval, vlc_value_t newval,
174 libvlc_media_instance_t * p_mi = p_userdata;
177 if (!strncmp(psz_cmd, "intf", 4 /* "-change" no need to go further */))
180 input_thread_t * p_input = (input_thread_t *)p_this;
182 var_Get( p_input, "time", &val );
183 if ((val.i_time % I64C(500000)) != 0)
184 return VLC_SUCCESS; /* No need to have a better precision */
186 var_Get( p_input, "state", &val2 );
187 if( val2.i_int != PLAYING_S )
188 return VLC_SUCCESS; /* Don't send the position while stopped */
191 val.i_time = newval.i_time;
193 libvlc_event_t event;
194 event.type = libvlc_MediaInstancePositionChanged;
195 event.u.media_instance_position_changed.new_position = val.i_time;
197 libvlc_event_send( p_mi->p_event_manager, &event );
201 /**************************************************************************
202 * Create a Media Instance object
203 **************************************************************************/
204 libvlc_media_instance_t *
205 libvlc_media_instance_new( libvlc_instance_t * p_libvlc_instance,
206 libvlc_exception_t * p_e )
208 libvlc_media_instance_t * p_mi;
210 if( !p_libvlc_instance )
212 libvlc_exception_raise( p_e, "invalid libvlc instance" );
216 p_mi = malloc( sizeof(libvlc_media_instance_t) );
219 p_mi->p_libvlc_instance = p_libvlc_instance;
220 p_mi->i_input_id = -1;
221 /* refcount strategy:
222 * - All items created by _new start with a refcount set to 1
223 * - Accessor _release decrease the refcount by 1, if after that
224 * operation the refcount is 0, the object is destroyed.
225 * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
226 p_mi->i_refcount = 1;
227 p_mi->b_own_its_input_thread = VLC_TRUE;
228 /* object_lock strategy:
229 * - No lock held in constructor
230 * - Lock when accessing all variable this lock is held
231 * - Lock when attempting to destroy the object the lock is also held */
232 vlc_mutex_init( p_mi->p_libvlc_instance->p_libvlc_int,
233 &p_mi->object_lock );
234 p_mi->p_event_manager = libvlc_event_manager_new( p_mi,
235 p_libvlc_instance, p_e );
236 if( libvlc_exception_raised( p_e ) )
242 libvlc_event_manager_register_event_type( p_mi->p_event_manager,
243 libvlc_MediaInstanceReachedEnd, p_e );
244 libvlc_event_manager_register_event_type( p_mi->p_event_manager,
245 libvlc_MediaInstancePaused, p_e );
246 libvlc_event_manager_register_event_type( p_mi->p_event_manager,
247 libvlc_MediaInstancePlayed, p_e );
248 libvlc_event_manager_register_event_type( p_mi->p_event_manager,
249 libvlc_MediaInstancePositionChanged, p_e );
254 /**************************************************************************
255 * Create a Media Instance object with a media descriptor
256 **************************************************************************/
257 libvlc_media_instance_t *
258 libvlc_media_instance_new_from_media_descriptor(
259 libvlc_media_descriptor_t * p_md,
260 libvlc_exception_t *p_e )
262 libvlc_media_instance_t * p_mi;
263 p_mi = libvlc_media_instance_new( p_md->p_libvlc_instance, p_e );
268 libvlc_media_descriptor_retain( p_md );
274 /**************************************************************************
275 * Create a new media instance object from an input_thread (Libvlc Internal)
276 **************************************************************************/
277 libvlc_media_instance_t * libvlc_media_instance_new_from_input_thread(
278 struct libvlc_instance_t *p_libvlc_instance,
279 input_thread_t *p_input,
280 libvlc_exception_t *p_e )
282 libvlc_media_instance_t * p_mi;
286 libvlc_exception_raise( p_e, "invalid input thread" );
290 p_mi = libvlc_media_instance_new( p_libvlc_instance, p_e );
295 p_mi->p_md = libvlc_media_descriptor_new_from_input_item(
297 input_GetItem( p_input ), p_e );
301 libvlc_media_instance_destroy( p_mi );
305 p_mi->i_input_id = p_input->i_object_id;
306 p_mi->b_own_its_input_thread = VLC_FALSE;
308 /* will be released in media_instance_release() */
309 vlc_object_yield( p_input );
311 /* XXX: Hack as the playlist doesn't yield the input thread we retain
312 * the input for the playlist. (see corresponding hack in _release) */
313 vlc_object_yield( p_input );
318 /**************************************************************************
319 * Destroy a Media Instance object (libvlc internal)
321 * Warning: No lock held here, but hey, this is internal.
322 **************************************************************************/
323 void libvlc_media_instance_destroy( libvlc_media_instance_t *p_mi )
325 input_thread_t *p_input_thread;
326 libvlc_exception_t p_e;
328 libvlc_exception_init( &p_e );
333 p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
335 if( libvlc_exception_raised( &p_e ) )
337 libvlc_event_manager_release( p_mi->p_event_manager );
338 libvlc_exception_clear( &p_e );
340 return; /* no need to worry about no input thread */
342 vlc_mutex_destroy( &p_mi->object_lock );
344 input_DestroyThread( p_input_thread );
346 libvlc_media_descriptor_release( p_mi->p_md );
351 /**************************************************************************
352 * Release a Media Instance object
353 **************************************************************************/
354 void libvlc_media_instance_release( libvlc_media_instance_t *p_mi )
359 vlc_mutex_lock( &p_mi->object_lock );
363 if( p_mi->i_refcount > 0 )
365 vlc_mutex_unlock( &p_mi->object_lock );
368 vlc_mutex_unlock( &p_mi->object_lock );
369 vlc_mutex_destroy( &p_mi->object_lock );
371 release_input_thread( p_mi );
373 libvlc_event_manager_release( p_mi->p_event_manager );
375 libvlc_media_descriptor_release( p_mi->p_md );
380 /**************************************************************************
381 * Retain a Media Instance object
382 **************************************************************************/
383 void libvlc_media_instance_retain( libvlc_media_instance_t *p_mi )
391 /**************************************************************************
392 * Set the Media descriptor associated with the instance
393 **************************************************************************/
394 void libvlc_media_instance_set_media_descriptor(
395 libvlc_media_instance_t *p_mi,
396 libvlc_media_descriptor_t *p_md,
397 libvlc_exception_t *p_e )
404 vlc_mutex_lock( &p_mi->object_lock );
406 release_input_thread( p_mi );
408 libvlc_media_descriptor_release( p_mi->p_md );
413 vlc_mutex_unlock( &p_mi->object_lock );
414 return; /* It is ok to pass a NULL md */
417 libvlc_media_descriptor_retain( p_md );
420 /* The policy here is to ignore that we were created using a different
421 * libvlc_instance, because we don't really care */
422 p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
424 vlc_mutex_unlock( &p_mi->object_lock );
427 /**************************************************************************
428 * Get the Media descriptor associated with the instance
429 **************************************************************************/
430 libvlc_media_descriptor_t *
431 libvlc_media_instance_get_media_descriptor(
432 libvlc_media_instance_t *p_mi,
433 libvlc_exception_t *p_e )
440 libvlc_media_descriptor_retain( p_mi->p_md );
444 /**************************************************************************
445 * Get the event Manager
446 **************************************************************************/
447 libvlc_event_manager_t *
448 libvlc_media_instance_event_manager(
449 libvlc_media_instance_t *p_mi,
450 libvlc_exception_t *p_e )
454 return p_mi->p_event_manager;
457 /**************************************************************************
459 **************************************************************************/
460 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
461 libvlc_exception_t *p_e )
463 input_thread_t * p_input_thread;
465 if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
467 /* A thread alread exists, send it a play message */
468 input_Control( p_input_thread, INPUT_SET_STATE, PLAYING_S );
469 vlc_object_release( p_input_thread );
473 /* Ignore previous exception */
474 libvlc_exception_clear( p_e );
476 vlc_mutex_lock( &p_mi->object_lock );
480 libvlc_exception_raise( p_e, "no associated media descriptor" );
481 vlc_mutex_unlock( &p_mi->object_lock );
485 p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
486 p_mi->p_md->p_input_item );
487 p_mi->i_input_id = p_input_thread->i_object_id;
492 val.i_int = p_mi->drawable;
493 var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
494 var_Set( p_input_thread, "drawable", val );
496 var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
497 var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
499 /* will be released in media_instance_release() */
500 vlc_object_yield( p_input_thread );
502 vlc_mutex_unlock( &p_mi->object_lock );
505 /**************************************************************************
507 **************************************************************************/
508 void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
509 libvlc_exception_t *p_e )
511 input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
513 if( !p_input_thread )
516 input_Control( p_input_thread, INPUT_SET_STATE, PAUSE_S );
517 vlc_object_release( p_input_thread );
520 /**************************************************************************
522 **************************************************************************/
523 void libvlc_media_instance_stop( libvlc_media_instance_t *p_mi,
524 libvlc_exception_t *p_e )
526 if( p_mi->b_own_its_input_thread )
527 release_input_thread( p_mi ); /* This will stop the input thread */
530 input_thread_t * p_input_thread = libvlc_get_input_thread( p_mi, p_e );
532 if( !p_input_thread )
535 input_StopThread( p_input_thread );
536 vlc_object_release( p_input_thread );
540 /**************************************************************************
542 **************************************************************************/
543 void libvlc_media_instance_set_drawable( libvlc_media_instance_t *p_mi,
544 libvlc_drawable_t drawable,
545 libvlc_exception_t *p_e )
547 p_mi->drawable = drawable;
550 /**************************************************************************
551 * Getters for stream information
552 **************************************************************************/
553 vlc_int64_t libvlc_media_instance_get_length(
554 libvlc_media_instance_t *p_mi,
555 libvlc_exception_t *p_e )
557 input_thread_t *p_input_thread;
560 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
561 if( !p_input_thread )
564 var_Get( p_input_thread, "length", &val );
565 vlc_object_release( p_input_thread );
567 return (val.i_time+500LL)/1000LL;
570 vlc_int64_t libvlc_media_instance_get_time(
571 libvlc_media_instance_t *p_mi,
572 libvlc_exception_t *p_e )
574 input_thread_t *p_input_thread;
577 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
578 if( !p_input_thread )
581 var_Get( p_input_thread , "time", &val );
582 vlc_object_release( p_input_thread );
583 return (val.i_time+500LL)/1000LL;
586 void libvlc_media_instance_set_time(
587 libvlc_media_instance_t *p_mi,
589 libvlc_exception_t *p_e )
591 input_thread_t *p_input_thread;
594 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
595 if( !p_input_thread )
598 value.i_time = time*1000LL;
599 var_Set( p_input_thread, "time", value );
600 vlc_object_release( p_input_thread );
603 void libvlc_media_instance_set_position(
604 libvlc_media_instance_t *p_mi,
606 libvlc_exception_t *p_e )
608 input_thread_t *p_input_thread;
610 val.f_float = position;
612 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
613 if( !p_input_thread )
616 var_Set( p_input_thread, "position", val );
617 vlc_object_release( p_input_thread );
620 float libvlc_media_instance_get_position(
621 libvlc_media_instance_t *p_mi,
622 libvlc_exception_t *p_e )
624 input_thread_t *p_input_thread;
627 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
628 if( !p_input_thread )
631 var_Get( p_input_thread, "position", &val );
632 vlc_object_release( p_input_thread );
637 void libvlc_media_instance_set_chapter(
638 libvlc_media_instance_t *p_mi,
640 libvlc_exception_t *p_e )
642 input_thread_t *p_input_thread;
646 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
647 if( !p_input_thread )
650 var_Set( p_input_thread, "chapter", val );
651 vlc_object_release( p_input_thread );
654 int libvlc_media_instance_get_chapter(
655 libvlc_media_instance_t *p_mi,
656 libvlc_exception_t *p_e )
658 input_thread_t *p_input_thread;
661 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
662 if( !p_input_thread )
665 var_Get( p_input_thread, "chapter", &val );
666 vlc_object_release( p_input_thread );
671 int libvlc_media_instance_get_chapter_count(
672 libvlc_media_instance_t *p_mi,
673 libvlc_exception_t *p_e )
675 input_thread_t *p_input_thread;
678 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
679 if( !p_input_thread )
682 var_Change( p_input_thread, "chapter", VLC_VAR_CHOICESCOUNT, &val, NULL );
683 vlc_object_release( p_input_thread );
688 float libvlc_media_instance_get_fps(
689 libvlc_media_instance_t *p_mi,
690 libvlc_exception_t *p_e)
692 input_thread_t *p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
697 if( input_Control( p_input_thread, INPUT_GET_VIDEO_FPS, &f_fps ) )
699 vlc_object_release( p_input_thread );
704 vlc_bool_t libvlc_media_instance_will_play(
705 libvlc_media_instance_t *p_mi,
706 libvlc_exception_t *p_e)
708 input_thread_t *p_input_thread =
709 libvlc_get_input_thread ( p_mi, p_e);
710 if ( !p_input_thread )
713 if ( !p_input_thread->b_die && !p_input_thread->b_dead )
715 vlc_object_release( p_input_thread );
718 vlc_object_release( p_input_thread );
722 void libvlc_media_instance_set_rate(
723 libvlc_media_instance_t *p_mi,
725 libvlc_exception_t *p_e )
727 input_thread_t *p_input_thread;
731 RAISEVOID( "Rate value is invalid" );
733 val.i_int = 1000.0f/rate;
735 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
736 if ( !p_input_thread )
739 var_Set( p_input_thread, "rate", val );
740 vlc_object_release( p_input_thread );
743 float libvlc_media_instance_get_rate(
744 libvlc_media_instance_t *p_mi,
745 libvlc_exception_t *p_e )
747 input_thread_t *p_input_thread;
750 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
751 if ( !p_input_thread )
754 var_Get( p_input_thread, "rate", &val );
755 vlc_object_release( p_input_thread );
757 return (float)1000.0f/val.i_int;
760 libvlc_state_t libvlc_media_instance_get_state(
761 libvlc_media_instance_t *p_mi,
762 libvlc_exception_t *p_e )
764 input_thread_t *p_input_thread;
767 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
768 if ( !p_input_thread )
769 return libvlc_Stopped;
771 var_Get( p_input_thread, "state", &val );
772 vlc_object_release( p_input_thread );
774 return vlc_to_libvlc_state(val.i_int);