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 "libvlc_internal.h"
25 #include <vlc/libvlc.h>
26 #include <vlc_demux.h>
27 #include <vlc_input.h>
28 #include "input/input_internal.h"
31 * Release the associated input thread
33 * Object lock is NOT held.
35 static void release_input_thread( libvlc_media_instance_t *p_mi )
37 input_thread_t *p_input_thread;
38 vlc_bool_t should_destroy;
40 if( !p_mi || p_mi->i_input_id == -1 )
43 p_input_thread = (input_thread_t*)vlc_object_get(
44 p_mi->p_libvlc_instance->p_libvlc_int,
47 p_mi->i_input_id = -1;
52 /* release for previous vlc_object_get */
53 vlc_object_release( p_input_thread );
55 should_destroy = p_input_thread->i_refcount == 1;
57 /* release for initial p_input_thread yield (see _new()) */
58 vlc_object_release( p_input_thread );
60 /* No one is tracking this input_thread appart us. Destroy it */
63 /* We owned this one */
64 input_StopThread( p_input_thread );
65 var_Destroy( p_input_thread, "drawable" );
66 input_DestroyThread( p_input_thread );
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 );
78 * Retrieve the input thread. Be sure to release the object
79 * once you are done with it. (libvlc Internal)
81 * Object lock is held.
83 input_thread_t *libvlc_get_input_thread( libvlc_media_instance_t *p_mi,
84 libvlc_exception_t *p_e )
86 input_thread_t *p_input_thread;
88 vlc_mutex_lock( &p_mi->object_lock );
90 if( !p_mi || p_mi->i_input_id == -1 )
92 vlc_mutex_unlock( &p_mi->object_lock );
93 RAISENULL( "Input is NULL" );
96 p_input_thread = (input_thread_t*)vlc_object_get(
97 p_mi->p_libvlc_instance->p_libvlc_int,
101 vlc_mutex_unlock( &p_mi->object_lock );
102 RAISENULL( "Input does not exist" );
105 vlc_mutex_unlock( &p_mi->object_lock );
106 return p_input_thread;
110 * input_state_changed (Private) (input var "state" Callback)
113 input_state_changed( vlc_object_t * p_this, char const * psz_cmd,
114 vlc_value_t oldval, vlc_value_t newval,
117 libvlc_media_instance_t * p_mi = p_userdata;
118 libvlc_event_t event;
120 if( newval.i_int == oldval.i_int )
121 return VLC_SUCCESS; /* No change since last time, don't propagate */
123 switch ( newval.i_int )
126 event.type = libvlc_MediaInstanceReachedEnd;
129 event.type = libvlc_MediaInstancePaused;
132 event.type = libvlc_MediaInstancePlayed;
138 libvlc_event_send( p_mi->p_event_manager, &event );
143 * input_position_changed (Private) (input var "intf-change" Callback)
146 input_position_changed( vlc_object_t * p_this, char const * psz_cmd,
147 vlc_value_t oldval, vlc_value_t newval,
150 libvlc_media_instance_t * p_mi = p_userdata;
153 if (!strcmp(psz_cmd, "intf" /* "-change" no need to go further */))
155 input_thread_t * p_input = (input_thread_t *)p_this;
156 var_Get( p_input, "position", &val );
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 );
162 if( val.i_int != PLAYING_S )
163 return VLC_SUCCESS; /* Don't send the position while stopped */
166 val.i_time = newval.i_time;
168 libvlc_event_t event;
169 event.type = libvlc_MediaInstancePositionChanged;
170 event.u.media_instance_position_changed.new_position = val.i_time;
172 libvlc_event_send( p_mi->p_event_manager, &event );
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 )
183 libvlc_media_instance_t * p_mi;
185 if( !p_libvlc_instance )
187 libvlc_exception_raise( p_e, "invalid libvlc instance" );
191 p_mi = malloc( sizeof(libvlc_media_instance_t) );
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 ) )
216 libvlc_event_manager_register_event_type( p_mi->p_event_manager,
217 libvlc_MediaInstanceReachedEnd, p_e );
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 )
230 libvlc_media_instance_t * p_mi;
231 p_mi = libvlc_media_instance_new( p_md->p_libvlc_instance, p_e );
236 p_mi->p_md = libvlc_media_descriptor_duplicate( p_md );
241 /**************************************************************************
242 * Create a new media instance object from an input_thread (Libvlc Internal)
243 **************************************************************************/
244 libvlc_media_instance_t * libvlc_media_instance_new_from_input_thread(
245 struct libvlc_instance_t *p_libvlc_instance,
246 input_thread_t *p_input,
247 libvlc_exception_t *p_e )
249 libvlc_media_instance_t * p_mi;
253 libvlc_exception_raise( p_e, "invalid input thread" );
257 p_mi = libvlc_media_instance_new( p_libvlc_instance, p_e );
262 p_mi->p_md = libvlc_media_descriptor_new_from_input_item(
264 p_input->p->input.p_item, p_e );
268 libvlc_media_instance_destroy( p_mi );
272 p_mi->i_input_id = p_input->i_object_id;
274 /* will be released in media_instance_release() */
275 vlc_object_yield( p_input );
277 /* XXX: Hack as the playlist doesn't yield the input thread we retain
278 * the input for the playlist. (see corresponding hack in _release) */
279 vlc_object_yield( p_input );
284 /**************************************************************************
285 * Destroy a Media Instance object (libvlc internal)
287 * Warning: No lock held here, but hey, this is internal.
288 **************************************************************************/
289 void libvlc_media_instance_destroy( libvlc_media_instance_t *p_mi )
291 input_thread_t *p_input_thread;
292 libvlc_exception_t p_e;
294 libvlc_exception_init( &p_e );
299 p_input_thread = libvlc_get_input_thread( p_mi, &p_e );
301 if( libvlc_exception_raised( &p_e ) )
303 libvlc_event_manager_release( p_mi->p_event_manager );
305 return; /* no need to worry about no input thread */
307 vlc_mutex_destroy( &p_mi->object_lock );
309 input_DestroyThread( p_input_thread );
311 libvlc_media_descriptor_release( p_mi->p_md );
316 /**************************************************************************
317 * Release a Media Instance object
318 **************************************************************************/
319 void libvlc_media_instance_release( libvlc_media_instance_t *p_mi )
324 vlc_mutex_lock( &p_mi->object_lock );
328 if( p_mi->i_refcount > 0 )
330 vlc_mutex_unlock( &p_mi->object_lock );
333 vlc_mutex_unlock( &p_mi->object_lock );
334 vlc_mutex_destroy( &p_mi->object_lock );
336 libvlc_event_manager_release( p_mi->p_event_manager );
338 release_input_thread( p_mi );
340 libvlc_media_descriptor_release( p_mi->p_md );
345 /**************************************************************************
346 * Retain a Media Instance object
347 **************************************************************************/
348 void libvlc_media_instance_retain( libvlc_media_instance_t *p_mi )
355 /**************************************************************************
356 * Set the Media descriptor associated with the instance
357 **************************************************************************/
358 void libvlc_media_instance_set_media_descriptor(
359 libvlc_media_instance_t *p_mi,
360 libvlc_media_descriptor_t *p_md,
361 libvlc_exception_t *p_e )
368 vlc_mutex_lock( &p_mi->object_lock );
370 release_input_thread( p_mi );
372 libvlc_media_descriptor_release( p_mi->p_md );
377 vlc_mutex_unlock( &p_mi->object_lock );
378 return; /* It is ok to pass a NULL md */
381 p_mi->p_md = libvlc_media_descriptor_duplicate( p_md );
383 /* The policy here is to ignore that we were created using a different
384 * libvlc_instance, because we don't really care */
385 p_mi->p_libvlc_instance = p_md->p_libvlc_instance;
387 vlc_mutex_unlock( &p_mi->object_lock );
390 /**************************************************************************
391 * Get the Media descriptor associated with the instance
392 **************************************************************************/
393 libvlc_media_descriptor_t *
394 libvlc_media_instance_get_media_descriptor(
395 libvlc_media_instance_t *p_mi,
396 libvlc_exception_t *p_e )
403 return libvlc_media_descriptor_duplicate( p_mi->p_md );
406 /**************************************************************************
407 * Get the event Manager
408 **************************************************************************/
409 libvlc_event_manager_t *
410 libvlc_media_instance_event_manager(
411 libvlc_media_instance_t *p_mi,
412 libvlc_exception_t *p_e )
416 return p_mi->p_event_manager;
419 /**************************************************************************
421 **************************************************************************/
422 void libvlc_media_instance_play( libvlc_media_instance_t *p_mi,
423 libvlc_exception_t *p_e )
425 input_thread_t * p_input_thread;
427 if( (p_input_thread = libvlc_get_input_thread( p_mi, p_e )) )
429 /* A thread alread exists, send it a play message */
431 val.i_int = PLAYING_S;
433 input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, PLAYING_S );
434 vlc_object_release( p_input_thread );
438 /* Ignore previous exception */
439 libvlc_exception_clear( p_e );
441 vlc_mutex_lock( &p_mi->object_lock );
445 libvlc_exception_raise( p_e, "no associated media descriptor" );
446 vlc_mutex_unlock( &p_mi->object_lock );
450 p_input_thread = input_CreateThread( p_mi->p_libvlc_instance->p_libvlc_int,
451 p_mi->p_md->p_input_item );
452 p_mi->i_input_id = p_input_thread->i_object_id;
457 val.i_int = p_mi->drawable;
458 var_Create( p_input_thread, "drawable", VLC_VAR_DOINHERIT );
459 var_Set( p_input_thread, "drawable", val );
461 var_AddCallback( p_input_thread, "state", input_state_changed, p_mi );
462 var_AddCallback( p_input_thread, "intf-change", input_position_changed, p_mi );
464 /* will be released in media_instance_release() */
465 vlc_object_yield( p_input_thread );
467 vlc_mutex_unlock( &p_mi->object_lock );
470 /**************************************************************************
472 **************************************************************************/
473 void libvlc_media_instance_pause( libvlc_media_instance_t *p_mi,
474 libvlc_exception_t *p_e )
476 input_thread_t * p_input_thread;
480 p_input_thread = libvlc_get_input_thread( p_mi, p_e );
482 if( !p_input_thread )
485 input_Control( p_input_thread, INPUT_CONTROL_SET_STATE, val );
486 vlc_object_release( p_input_thread );
489 /**************************************************************************
491 **************************************************************************/
492 void libvlc_media_instance_stop( libvlc_media_instance_t *p_mi,
493 libvlc_exception_t *p_e )
495 //libvlc_exception_raise( p_e, "Not implemented" );
498 /**************************************************************************
500 **************************************************************************/
501 void libvlc_media_instance_set_drawable( libvlc_media_instance_t *p_mi,
502 libvlc_drawable_t drawable,
503 libvlc_exception_t *p_e )
505 p_mi->drawable = drawable;
508 /**************************************************************************
509 * Getters for stream information
510 **************************************************************************/
511 vlc_int64_t libvlc_media_instance_get_length(
512 libvlc_media_instance_t *p_mi,
513 libvlc_exception_t *p_e )
515 input_thread_t *p_input_thread;
518 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
519 if( !p_input_thread )
522 var_Get( p_input_thread, "length", &val );
523 vlc_object_release( p_input_thread );
525 return (val.i_time+500LL)/1000LL;
528 vlc_int64_t libvlc_media_instance_get_time(
529 libvlc_media_instance_t *p_mi,
530 libvlc_exception_t *p_e )
532 input_thread_t *p_input_thread;
535 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
536 if( !p_input_thread )
539 var_Get( p_input_thread , "time", &val );
540 vlc_object_release( p_input_thread );
541 return (val.i_time+500LL)/1000LL;
544 void libvlc_media_instance_set_time(
545 libvlc_media_instance_t *p_mi,
547 libvlc_exception_t *p_e )
549 input_thread_t *p_input_thread;
552 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
553 if( !p_input_thread )
556 value.i_time = time*1000LL;
557 var_Set( p_input_thread, "time", value );
558 vlc_object_release( p_input_thread );
561 void libvlc_media_instance_set_position(
562 libvlc_media_instance_t *p_mi,
564 libvlc_exception_t *p_e )
566 input_thread_t *p_input_thread;
568 val.f_float = position;
570 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
571 if( !p_input_thread )
574 var_Set( p_input_thread, "position", val );
575 vlc_object_release( p_input_thread );
578 float libvlc_media_instance_get_position(
579 libvlc_media_instance_t *p_mi,
580 libvlc_exception_t *p_e )
582 input_thread_t *p_input_thread;
585 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
586 if( !p_input_thread )
589 var_Get( p_input_thread, "position", &val );
590 vlc_object_release( p_input_thread );
595 float libvlc_media_instance_get_fps(
596 libvlc_media_instance_t *p_mi,
597 libvlc_exception_t *p_e)
600 input_thread_t *p_input_thread;
602 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
603 if( !p_input_thread )
606 if( (NULL == p_input_thread->p->input.p_demux)
607 || demux2_Control( p_input_thread->p->input.p_demux, DEMUX_GET_FPS, &f_fps )
610 vlc_object_release( p_input_thread );
615 vlc_object_release( p_input_thread );
620 vlc_bool_t libvlc_media_instance_will_play(
621 libvlc_media_instance_t *p_mi,
622 libvlc_exception_t *p_e)
624 input_thread_t *p_input_thread =
625 libvlc_get_input_thread ( p_mi, p_e);
626 if ( !p_input_thread )
629 if ( !p_input_thread->b_die && !p_input_thread->b_dead )
631 vlc_object_release( p_input_thread );
634 vlc_object_release( p_input_thread );
638 void libvlc_media_instance_set_rate(
639 libvlc_media_instance_t *p_mi,
641 libvlc_exception_t *p_e )
643 input_thread_t *p_input_thread;
647 RAISEVOID( "Rate value is invalid" );
649 val.i_int = 1000.0f/rate;
651 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
652 if ( !p_input_thread )
655 var_Set( p_input_thread, "rate", val );
656 vlc_object_release( p_input_thread );
659 float libvlc_media_instance_get_rate(
660 libvlc_media_instance_t *p_mi,
661 libvlc_exception_t *p_e )
663 input_thread_t *p_input_thread;
666 p_input_thread = libvlc_get_input_thread ( p_mi, p_e);
667 if ( !p_input_thread )
670 var_Get( p_input_thread, "rate", &val );
671 vlc_object_release( p_input_thread );
673 return (float)1000.0f/val.i_int;
676 int libvlc_media_instance_get_state(
677 libvlc_media_instance_t *p_mi,
678 libvlc_exception_t *p_e )
680 input_thread_t *p_input_thread;
683 p_input_thread = libvlc_get_input_thread ( p_mi, p_e );
684 if ( !p_input_thread )
687 var_Get( p_input_thread, "state", &val );
688 vlc_object_release( p_input_thread );