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 switch ( newval
.i_int
)
138 libvlc_media_descriptor_set_state( p_mi
->p_md
, libvlc_NothingSpecial
, NULL
);
139 event
.type
= libvlc_MediaInstanceReachedEnd
;
142 libvlc_media_descriptor_set_state( p_mi
->p_md
, libvlc_Playing
, NULL
);
143 event
.type
= libvlc_MediaInstancePaused
;
146 libvlc_media_descriptor_set_state( p_mi
->p_md
, libvlc_Playing
, NULL
);
147 event
.type
= libvlc_MediaInstancePlayed
;
150 libvlc_media_descriptor_set_state( p_mi
->p_md
, libvlc_Error
, NULL
);
151 event
.type
= libvlc_MediaInstanceReachedEnd
; /* Because ERROR_S is buggy */
157 libvlc_event_send( p_mi
->p_event_manager
, &event
);
162 * input_position_changed (Private) (input var "intf-change" Callback)
165 input_position_changed( vlc_object_t
* p_this
, char const * psz_cmd
,
166 vlc_value_t oldval
, vlc_value_t newval
,
169 libvlc_media_instance_t
* p_mi
= p_userdata
;
172 if (!strncmp(psz_cmd
, "intf", 4 /* "-change" no need to go further */))
174 input_thread_t
* p_input
= (input_thread_t
*)p_this
;
176 var_Get( p_input
, "state", &val
);
177 if( val
.i_int
!= PLAYING_S
)
178 return VLC_SUCCESS
; /* Don't send the position while stopped */
180 var_Get( p_input
, "position", &val
);
183 val
.i_time
= newval
.i_time
;
185 libvlc_event_t event
;
186 event
.type
= libvlc_MediaInstancePositionChanged
;
187 event
.u
.media_instance_position_changed
.new_position
= val
.f_float
;
189 libvlc_event_send( p_mi
->p_event_manager
, &event
);
194 * input_time_changed (Private) (input var "intf-change" Callback)
197 input_time_changed( vlc_object_t
* p_this
, char const * psz_cmd
,
198 vlc_value_t oldval
, vlc_value_t newval
,
201 libvlc_media_instance_t
* p_mi
= p_userdata
;
204 if (!strncmp(psz_cmd
, "intf", 4 /* "-change" no need to go further */))
206 input_thread_t
* p_input
= (input_thread_t
*)p_this
;
208 var_Get( p_input
, "state", &val
);
209 if( val
.i_int
!= PLAYING_S
)
210 return VLC_SUCCESS
; /* Don't send the position while stopped */
212 var_Get( p_input
, "time", &val
);
215 val
.i_time
= newval
.i_time
;
217 libvlc_event_t event
;
218 event
.type
= libvlc_MediaInstanceTimeChanged
;
219 event
.u
.media_instance_time_changed
.new_time
= val
.i_time
;
221 libvlc_event_send( p_mi
->p_event_manager
, &event
);
225 /**************************************************************************
226 * Create a Media Instance object
227 **************************************************************************/
228 libvlc_media_instance_t
*
229 libvlc_media_instance_new( libvlc_instance_t
* p_libvlc_instance
,
230 libvlc_exception_t
* p_e
)
232 libvlc_media_instance_t
* p_mi
;
234 if( !p_libvlc_instance
)
236 libvlc_exception_raise( p_e
, "invalid libvlc instance" );
240 p_mi
= malloc( sizeof(libvlc_media_instance_t
) );
243 p_mi
->p_libvlc_instance
= p_libvlc_instance
;
244 p_mi
->i_input_id
= -1;
245 /* refcount strategy:
246 * - All items created by _new start with a refcount set to 1
247 * - Accessor _release decrease the refcount by 1, if after that
248 * operation the refcount is 0, the object is destroyed.
249 * - Accessor _retain increase the refcount by 1 (XXX: to implement) */
250 p_mi
->i_refcount
= 1;
251 p_mi
->b_own_its_input_thread
= VLC_TRUE
;
252 /* object_lock strategy:
253 * - No lock held in constructor
254 * - Lock when accessing all variable this lock is held
255 * - Lock when attempting to destroy the object the lock is also held */
256 vlc_mutex_init( p_mi
->p_libvlc_instance
->p_libvlc_int
,
257 &p_mi
->object_lock
);
258 p_mi
->p_event_manager
= libvlc_event_manager_new( p_mi
,
259 p_libvlc_instance
, p_e
);
260 if( libvlc_exception_raised( p_e
) )
266 libvlc_event_manager_register_event_type( p_mi
->p_event_manager
,
267 libvlc_MediaInstanceReachedEnd
, p_e
);
268 libvlc_event_manager_register_event_type( p_mi
->p_event_manager
,
269 libvlc_MediaInstancePaused
, p_e
);
270 libvlc_event_manager_register_event_type( p_mi
->p_event_manager
,
271 libvlc_MediaInstancePlayed
, p_e
);
272 libvlc_event_manager_register_event_type( p_mi
->p_event_manager
,
273 libvlc_MediaInstancePositionChanged
, p_e
);
274 libvlc_event_manager_register_event_type( p_mi
->p_event_manager
,
275 libvlc_MediaInstanceTimeChanged
, p_e
);
280 /**************************************************************************
281 * Create a Media Instance object with a media descriptor
282 **************************************************************************/
283 libvlc_media_instance_t
*
284 libvlc_media_instance_new_from_media_descriptor(
285 libvlc_media_descriptor_t
* p_md
,
286 libvlc_exception_t
*p_e
)
288 libvlc_media_instance_t
* p_mi
;
289 p_mi
= libvlc_media_instance_new( p_md
->p_libvlc_instance
, p_e
);
294 libvlc_media_descriptor_retain( p_md
);
300 /**************************************************************************
301 * Create a new media instance object from an input_thread (Libvlc Internal)
302 **************************************************************************/
303 libvlc_media_instance_t
* libvlc_media_instance_new_from_input_thread(
304 struct libvlc_instance_t
*p_libvlc_instance
,
305 input_thread_t
*p_input
,
306 libvlc_exception_t
*p_e
)
308 libvlc_media_instance_t
* p_mi
;
312 libvlc_exception_raise( p_e
, "invalid input thread" );
316 p_mi
= libvlc_media_instance_new( p_libvlc_instance
, p_e
);
321 p_mi
->p_md
= libvlc_media_descriptor_new_from_input_item(
323 input_GetItem( p_input
), p_e
);
327 libvlc_media_instance_destroy( p_mi
);
331 p_mi
->i_input_id
= p_input
->i_object_id
;
332 p_mi
->b_own_its_input_thread
= VLC_FALSE
;
334 /* will be released in media_instance_release() */
335 vlc_object_yield( p_input
);
337 /* XXX: Hack as the playlist doesn't yield the input thread we retain
338 * the input for the playlist. (see corresponding hack in _release) */
339 vlc_object_yield( p_input
);
344 /**************************************************************************
345 * Destroy a Media Instance object (libvlc internal)
347 * Warning: No lock held here, but hey, this is internal.
348 **************************************************************************/
349 void libvlc_media_instance_destroy( libvlc_media_instance_t
*p_mi
)
351 input_thread_t
*p_input_thread
;
352 libvlc_exception_t p_e
;
354 libvlc_exception_init( &p_e
);
359 p_input_thread
= libvlc_get_input_thread( p_mi
, &p_e
);
361 if( libvlc_exception_raised( &p_e
) )
363 libvlc_event_manager_release( p_mi
->p_event_manager
);
364 libvlc_exception_clear( &p_e
);
366 return; /* no need to worry about no input thread */
368 vlc_mutex_destroy( &p_mi
->object_lock
);
370 input_DestroyThread( p_input_thread
);
372 libvlc_media_descriptor_release( p_mi
->p_md
);
377 /**************************************************************************
378 * Release a Media Instance object
379 **************************************************************************/
380 void libvlc_media_instance_release( libvlc_media_instance_t
*p_mi
)
385 vlc_mutex_lock( &p_mi
->object_lock
);
389 if( p_mi
->i_refcount
> 0 )
391 vlc_mutex_unlock( &p_mi
->object_lock
);
394 vlc_mutex_unlock( &p_mi
->object_lock
);
395 vlc_mutex_destroy( &p_mi
->object_lock
);
397 release_input_thread( p_mi
);
399 libvlc_event_manager_release( p_mi
->p_event_manager
);
401 libvlc_media_descriptor_release( p_mi
->p_md
);
406 /**************************************************************************
407 * Retain a Media Instance object
408 **************************************************************************/
409 void libvlc_media_instance_retain( libvlc_media_instance_t
*p_mi
)
417 /**************************************************************************
418 * Set the Media descriptor associated with the instance
419 **************************************************************************/
420 void libvlc_media_instance_set_media_descriptor(
421 libvlc_media_instance_t
*p_mi
,
422 libvlc_media_descriptor_t
*p_md
,
423 libvlc_exception_t
*p_e
)
430 vlc_mutex_lock( &p_mi
->object_lock
);
432 release_input_thread( p_mi
);
434 libvlc_media_descriptor_release( p_mi
->p_md
);
439 vlc_mutex_unlock( &p_mi
->object_lock
);
440 return; /* It is ok to pass a NULL md */
443 libvlc_media_descriptor_retain( p_md
);
446 /* The policy here is to ignore that we were created using a different
447 * libvlc_instance, because we don't really care */
448 p_mi
->p_libvlc_instance
= p_md
->p_libvlc_instance
;
450 vlc_mutex_unlock( &p_mi
->object_lock
);
453 /**************************************************************************
454 * Get the Media descriptor associated with the instance
455 **************************************************************************/
456 libvlc_media_descriptor_t
*
457 libvlc_media_instance_get_media_descriptor(
458 libvlc_media_instance_t
*p_mi
,
459 libvlc_exception_t
*p_e
)
466 libvlc_media_descriptor_retain( p_mi
->p_md
);
470 /**************************************************************************
471 * Get the event Manager
472 **************************************************************************/
473 libvlc_event_manager_t
*
474 libvlc_media_instance_event_manager(
475 libvlc_media_instance_t
*p_mi
,
476 libvlc_exception_t
*p_e
)
480 return p_mi
->p_event_manager
;
483 /**************************************************************************
485 **************************************************************************/
486 void libvlc_media_instance_play( libvlc_media_instance_t
*p_mi
,
487 libvlc_exception_t
*p_e
)
489 input_thread_t
* p_input_thread
;
491 if( (p_input_thread
= libvlc_get_input_thread( p_mi
, p_e
)) )
493 /* A thread alread exists, send it a play message */
494 input_Control( p_input_thread
, INPUT_SET_STATE
, PLAYING_S
);
495 vlc_object_release( p_input_thread
);
499 /* Ignore previous exception */
500 libvlc_exception_clear( p_e
);
502 vlc_mutex_lock( &p_mi
->object_lock
);
506 libvlc_exception_raise( p_e
, "no associated media descriptor" );
507 vlc_mutex_unlock( &p_mi
->object_lock
);
511 p_input_thread
= input_CreateThread( p_mi
->p_libvlc_instance
->p_libvlc_int
,
512 p_mi
->p_md
->p_input_item
);
513 p_mi
->i_input_id
= p_input_thread
->i_object_id
;
518 val
.i_int
= p_mi
->drawable
;
519 var_Create( p_input_thread
, "drawable", VLC_VAR_DOINHERIT
);
520 var_Set( p_input_thread
, "drawable", val
);
522 var_AddCallback( p_input_thread
, "state", input_state_changed
, p_mi
);
523 var_AddCallback( p_input_thread
, "seekable", input_state_changed
, p_mi
);
524 var_AddCallback( p_input_thread
, "intf-change", input_position_changed
, p_mi
);
525 var_AddCallback( p_input_thread
, "intf-change", input_time_changed
, p_mi
);
527 /* will be released in media_instance_release() */
528 vlc_object_yield( p_input_thread
);
530 vlc_mutex_unlock( &p_mi
->object_lock
);
533 /**************************************************************************
535 **************************************************************************/
536 void libvlc_media_instance_pause( libvlc_media_instance_t
*p_mi
,
537 libvlc_exception_t
*p_e
)
539 input_thread_t
* p_input_thread
= libvlc_get_input_thread( p_mi
, p_e
);
541 if( !p_input_thread
)
544 input_Control( p_input_thread
, INPUT_SET_STATE
, PAUSE_S
);
545 vlc_object_release( p_input_thread
);
548 /**************************************************************************
550 **************************************************************************/
551 void libvlc_media_instance_stop( libvlc_media_instance_t
*p_mi
,
552 libvlc_exception_t
*p_e
)
554 if( p_mi
->b_own_its_input_thread
)
555 release_input_thread( p_mi
); /* This will stop the input thread */
558 input_thread_t
* p_input_thread
= libvlc_get_input_thread( p_mi
, p_e
);
560 if( !p_input_thread
)
563 input_StopThread( p_input_thread
);
564 vlc_object_release( p_input_thread
);
568 /**************************************************************************
570 **************************************************************************/
571 void libvlc_media_instance_set_drawable( libvlc_media_instance_t
*p_mi
,
572 libvlc_drawable_t drawable
,
573 libvlc_exception_t
*p_e
)
575 p_mi
->drawable
= drawable
;
578 /**************************************************************************
579 * Getters for stream information
580 **************************************************************************/
581 vlc_int64_t
libvlc_media_instance_get_length(
582 libvlc_media_instance_t
*p_mi
,
583 libvlc_exception_t
*p_e
)
585 input_thread_t
*p_input_thread
;
588 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
589 if( !p_input_thread
)
592 var_Get( p_input_thread
, "length", &val
);
593 vlc_object_release( p_input_thread
);
595 return (val
.i_time
+500LL)/1000LL;
598 vlc_int64_t
libvlc_media_instance_get_time(
599 libvlc_media_instance_t
*p_mi
,
600 libvlc_exception_t
*p_e
)
602 input_thread_t
*p_input_thread
;
605 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
606 if( !p_input_thread
)
609 var_Get( p_input_thread
, "time", &val
);
610 vlc_object_release( p_input_thread
);
611 return (val
.i_time
+500LL)/1000LL;
614 void libvlc_media_instance_set_time(
615 libvlc_media_instance_t
*p_mi
,
617 libvlc_exception_t
*p_e
)
619 input_thread_t
*p_input_thread
;
622 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
623 if( !p_input_thread
)
626 value
.i_time
= time
*1000LL;
627 var_Set( p_input_thread
, "time", value
);
628 vlc_object_release( p_input_thread
);
631 void libvlc_media_instance_set_position(
632 libvlc_media_instance_t
*p_mi
,
634 libvlc_exception_t
*p_e
)
636 input_thread_t
*p_input_thread
;
638 val
.f_float
= position
;
640 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
641 if( !p_input_thread
)
644 var_Set( p_input_thread
, "position", val
);
645 vlc_object_release( p_input_thread
);
648 float libvlc_media_instance_get_position(
649 libvlc_media_instance_t
*p_mi
,
650 libvlc_exception_t
*p_e
)
652 input_thread_t
*p_input_thread
;
655 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
656 if( !p_input_thread
)
659 var_Get( p_input_thread
, "position", &val
);
660 vlc_object_release( p_input_thread
);
665 void libvlc_media_instance_set_chapter(
666 libvlc_media_instance_t
*p_mi
,
668 libvlc_exception_t
*p_e
)
670 input_thread_t
*p_input_thread
;
674 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
675 if( !p_input_thread
)
678 var_Set( p_input_thread
, "chapter", val
);
679 vlc_object_release( p_input_thread
);
682 int libvlc_media_instance_get_chapter(
683 libvlc_media_instance_t
*p_mi
,
684 libvlc_exception_t
*p_e
)
686 input_thread_t
*p_input_thread
;
689 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
690 if( !p_input_thread
)
693 var_Get( p_input_thread
, "chapter", &val
);
694 vlc_object_release( p_input_thread
);
699 int libvlc_media_instance_get_chapter_count(
700 libvlc_media_instance_t
*p_mi
,
701 libvlc_exception_t
*p_e
)
703 input_thread_t
*p_input_thread
;
706 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
707 if( !p_input_thread
)
710 var_Change( p_input_thread
, "chapter", VLC_VAR_CHOICESCOUNT
, &val
, NULL
);
711 vlc_object_release( p_input_thread
);
716 float libvlc_media_instance_get_fps(
717 libvlc_media_instance_t
*p_mi
,
718 libvlc_exception_t
*p_e
)
720 input_thread_t
*p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
725 if( input_Control( p_input_thread
, INPUT_GET_VIDEO_FPS
, &f_fps
) )
727 vlc_object_release( p_input_thread
);
732 vlc_bool_t
libvlc_media_instance_will_play(
733 libvlc_media_instance_t
*p_mi
,
734 libvlc_exception_t
*p_e
)
736 input_thread_t
*p_input_thread
=
737 libvlc_get_input_thread ( p_mi
, p_e
);
738 if ( !p_input_thread
)
741 if ( !p_input_thread
->b_die
&& !p_input_thread
->b_dead
)
743 vlc_object_release( p_input_thread
);
746 vlc_object_release( p_input_thread
);
750 void libvlc_media_instance_set_rate(
751 libvlc_media_instance_t
*p_mi
,
753 libvlc_exception_t
*p_e
)
755 input_thread_t
*p_input_thread
;
759 RAISEVOID( "Rate value is invalid" );
761 val
.i_int
= 1000.0f
/rate
;
763 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
764 if ( !p_input_thread
)
767 var_Set( p_input_thread
, "rate", val
);
768 vlc_object_release( p_input_thread
);
771 float libvlc_media_instance_get_rate(
772 libvlc_media_instance_t
*p_mi
,
773 libvlc_exception_t
*p_e
)
775 input_thread_t
*p_input_thread
;
778 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
779 if ( !p_input_thread
)
782 var_Get( p_input_thread
, "rate", &val
);
783 vlc_object_release( p_input_thread
);
785 return (float)1000.0f
/val
.i_int
;
788 libvlc_state_t
libvlc_media_instance_get_state(
789 libvlc_media_instance_t
*p_mi
,
790 libvlc_exception_t
*p_e
)
792 input_thread_t
*p_input_thread
;
795 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
796 if ( !p_input_thread
)
798 /* We do return the right value, no need to throw an exception */
799 if( libvlc_exception_raised( p_e
) )
800 libvlc_exception_clear( p_e
);
801 return libvlc_Stopped
;
804 var_Get( p_input_thread
, "state", &val
);
805 vlc_object_release( p_input_thread
);
807 return vlc_to_libvlc_state(val
.i_int
);
810 vlc_bool_t
libvlc_media_instance_is_seekable(
811 libvlc_media_instance_t
*p_mi
,
812 libvlc_exception_t
*p_e
)
814 input_thread_t
*p_input_thread
;
817 p_input_thread
= libvlc_get_input_thread ( p_mi
, p_e
);
818 if ( !p_input_thread
)
820 /* We do return the right value, no need to throw an exception */
821 if( libvlc_exception_raised( p_e
) )
822 libvlc_exception_clear( p_e
);
825 var_Get( p_input_thread
, "seekable", &val
);
826 vlc_object_release( p_input_thread
);