1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2008 Laurent Aimar
6 * Authors: Laurent Aimar < fenrir _AT_ videolan _DOT_ org >
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
32 #include <vlc_common.h>
33 #include <vlc_atomic.h>
34 #include <vlc_decoder.h>
39 #include "../libvlc.h"
40 #include "../stream_output/stream_output.h"
41 #include "../audio_output/aout_internal.h"
42 #include "../video_output/vout_internal.h"
43 #include "input_interface.h"
50 enum vlc_vout_order order;
56 struct input_resource_t
60 vlc_object_t *p_parent;
62 /* This lock is used to serialize request and protect
67 input_thread_t *p_input;
69 sout_instance_t *p_sout;
70 vout_thread_t *p_vout_dummy;
71 struct vout_resource *vout_rsc_free;
73 /* This lock is used to protect vout resources access (for hold)
74 * It is a special case because of embed video (possible deadlock
75 * between vout window request and vout holds in some(qt) interface)
77 vlc_mutex_t lock_hold;
79 /* You need lock+lock_hold to write to the following variables and
80 * only lock or lock_hold to read them */
81 struct vlc_list vout_rscs;
84 audio_output_t *p_aout;
87 #define resource_GetFirstVoutRsc(resource) \
88 vlc_list_first_entry_or_null(&resource->vout_rscs, struct vout_resource, node)
90 static inline struct vout_resource *
91 resource_GetVoutRsc(input_resource_t *resource, vout_thread_t *vout)
93 struct vout_resource *vout_rsc;
95 vlc_list_foreach(vout_rsc, &resource->vout_rscs, node)
96 if (vout_rsc->vout == vout)
99 vlc_assert_unreachable();
102 static inline struct vout_resource *
103 vout_resource_Create(vout_thread_t *vout)
105 struct vout_resource *vout_rsc = malloc(sizeof(*vout_rsc));
106 if (unlikely(vout_rsc == NULL))
109 vout_rsc->started = false;
110 vout_rsc->vout = vout;
115 vout_resource_Add(struct vout_resource *vout_rsc, input_resource_t *resource)
117 vlc_list_append(&vout_rsc->node, &resource->vout_rscs);
120 static inline vout_thread_t *
121 vout_resource_Remove(struct vout_resource *vout_rsc)
123 vlc_list_remove(&vout_rsc->node);
124 vout_thread_t *vout = vout_rsc->vout;
132 static void DestroySout( input_resource_t *p_resource )
135 if( p_resource->p_sout )
137 msg_Dbg( p_resource->p_parent, "destroying stream output" );
138 sout_DeleteInstance( p_resource->p_sout );
141 p_resource->p_sout = NULL;
144 static sout_instance_t *RequestSout( input_resource_t *p_resource,
145 sout_instance_t *p_sout, const char *psz_sout )
150 /* Check the validity of the sout */
151 if( p_resource->p_sout &&
152 strcmp( p_resource->p_sout->psz_sout, psz_sout ) )
154 msg_Dbg( p_resource->p_parent, "destroying unusable sout" );
155 DestroySout( p_resource );
158 if( p_resource->p_sout )
161 msg_Dbg( p_resource->p_parent, "reusing sout" );
162 msg_Dbg( p_resource->p_parent, "you probably want to use gather stream_out" );
166 /* Create a new one */
167 p_resource->p_sout = sout_NewInstance( p_resource->p_parent, psz_sout );
170 p_sout = p_resource->p_sout;
171 p_resource->p_sout = NULL;
175 VLC_UNUSED (p_resource); VLC_UNUSED (p_sout); VLC_UNUSED (psz_sout);
181 static void DestroyVout( input_resource_t *p_resource )
183 assert( vlc_list_is_empty( &p_resource->vout_rscs )
184 || p_resource->vout_rsc_free == resource_GetFirstVoutRsc( p_resource ) );
186 if( p_resource->vout_rsc_free )
188 vlc_mutex_lock(&p_resource->lock_hold);
189 vout_thread_t *vout = vout_resource_Remove( p_resource->vout_rsc_free );
190 vlc_mutex_unlock(&p_resource->lock_hold);
193 p_resource->vout_rsc_free = NULL;
197 static void DisplayVoutTitle( input_resource_t *p_resource,
198 vout_thread_t *p_vout )
200 if( p_resource->p_input == NULL )
203 /* TODO display the title only one time for the same input ? */
205 input_item_t *p_item = input_GetItem( p_resource->p_input );
207 char *psz_nowplaying = input_item_GetNowPlayingFb( p_item );
208 if( psz_nowplaying && *psz_nowplaying )
210 vout_DisplayTitle( p_vout, psz_nowplaying );
214 char *psz_artist = input_item_GetArtist( p_item );
215 char *psz_name = input_item_GetTitle( p_item );
217 if( !psz_name || *psz_name == '\0' )
220 psz_name = input_item_GetName( p_item );
222 if( psz_artist && *psz_artist )
225 if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
227 vout_DisplayTitle( p_vout, psz_string );
233 vout_DisplayTitle( p_vout, psz_name );
238 free( psz_nowplaying );
242 audio_output_t *input_resource_GetAout( input_resource_t *p_resource )
244 audio_output_t *p_aout;
246 vlc_mutex_lock( &p_resource->lock_hold );
247 p_aout = p_resource->p_aout;
249 if( p_aout == NULL || p_resource->b_aout_busy )
251 msg_Dbg( p_resource->p_parent, "creating audio output" );
252 vlc_mutex_unlock( &p_resource->lock_hold );
254 p_aout = aout_New( p_resource->p_parent );
256 return NULL; /* failed */
258 vlc_mutex_lock( &p_resource->lock_hold );
259 if( p_resource->p_aout == NULL )
260 p_resource->p_aout = p_aout;
263 msg_Dbg( p_resource->p_parent, "reusing audio output" );
265 if( p_resource->p_aout == p_aout )
267 assert( !p_resource->b_aout_busy );
268 p_resource->b_aout_busy = true;
270 vlc_mutex_unlock( &p_resource->lock_hold );
274 void input_resource_PutAout( input_resource_t *p_resource,
275 audio_output_t *p_aout )
277 assert( p_aout != NULL );
279 vlc_mutex_lock( &p_resource->lock_hold );
280 if( p_aout == p_resource->p_aout )
282 assert( p_resource->b_aout_busy );
283 p_resource->b_aout_busy = false;
284 msg_Dbg( p_resource->p_parent, "keeping audio output" );
288 msg_Dbg( p_resource->p_parent, "destroying extra audio output" );
289 vlc_mutex_unlock( &p_resource->lock_hold );
292 aout_Destroy( p_aout );
295 audio_output_t *input_resource_HoldAout( input_resource_t *p_resource )
297 audio_output_t *p_aout;
299 vlc_mutex_lock( &p_resource->lock_hold );
300 p_aout = p_resource->p_aout;
303 vlc_mutex_unlock( &p_resource->lock_hold );
308 void input_resource_ResetAout( input_resource_t *p_resource )
310 audio_output_t *p_aout = NULL;
312 vlc_mutex_lock( &p_resource->lock_hold );
313 if( !p_resource->b_aout_busy )
314 p_aout = p_resource->p_aout;
316 p_resource->p_aout = NULL;
317 p_resource->b_aout_busy = false;
318 vlc_mutex_unlock( &p_resource->lock_hold );
321 aout_Destroy( p_aout );
325 input_resource_t *input_resource_New( vlc_object_t *p_parent )
327 input_resource_t *p_resource = calloc( 1, sizeof(*p_resource) );
331 p_resource->p_vout_dummy = vout_CreateDummy(p_parent);
332 if( !p_resource->p_vout_dummy )
338 vlc_list_init( &p_resource->vout_rscs );
340 vlc_atomic_rc_init( &p_resource->rc );
341 p_resource->p_parent = p_parent;
342 vlc_mutex_init( &p_resource->lock );
343 vlc_mutex_init( &p_resource->lock_hold );
347 void input_resource_Release( input_resource_t *p_resource )
349 if( !vlc_atomic_rc_dec( &p_resource->rc ) )
352 DestroySout( p_resource );
353 DestroyVout( p_resource );
354 if( p_resource->p_aout != NULL )
355 aout_Destroy( p_resource->p_aout );
357 vout_Release( p_resource->p_vout_dummy );
361 input_resource_t *input_resource_Hold( input_resource_t *p_resource )
363 vlc_atomic_rc_inc( &p_resource->rc );
367 void input_resource_SetInput( input_resource_t *p_resource, input_thread_t *p_input )
369 vlc_mutex_lock( &p_resource->lock );
371 if( p_resource->p_input && !p_input )
372 assert( vlc_list_is_empty( &p_resource->vout_rscs )
373 || p_resource->vout_rsc_free == resource_GetFirstVoutRsc( p_resource ) );
376 p_resource->p_input = p_input;
378 vlc_mutex_unlock( &p_resource->lock );
381 static void input_resource_PutVoutLocked(input_resource_t *p_resource,
382 vout_thread_t *vout, bool *has_stopped)
384 assert(vout != NULL);
385 struct vout_resource *vout_rsc = resource_GetVoutRsc(p_resource, vout);
386 assert(vout_rsc != NULL);
388 if (has_stopped != NULL)
389 *has_stopped = vout_rsc->started;
391 if (vout_rsc->started)
393 vout_StopDisplay(vout_rsc->vout);
394 vout_rsc->started = false;
397 if (vout_rsc == resource_GetFirstVoutRsc(p_resource))
399 assert(p_resource->vout_rsc_free == NULL || p_resource->vout_rsc_free == vout_rsc);
401 msg_Dbg(p_resource->p_parent, "saving a free vout");
402 p_resource->vout_rsc_free = vout_rsc;
406 msg_Dbg(p_resource->p_parent, "destroying vout (already one saved or active)");
408 vlc_mutex_lock(&p_resource->lock_hold);
409 vout_resource_Remove(vout_rsc);
410 vlc_mutex_unlock(&p_resource->lock_hold);
417 void input_resource_PutVout(input_resource_t *p_resource,
418 vout_thread_t *vout, bool *stopped)
420 vlc_mutex_lock( &p_resource->lock );
421 input_resource_PutVoutLocked( p_resource, vout, stopped );
422 vlc_mutex_unlock( &p_resource->lock );
425 static struct vout_resource *
426 RequestVoutRsc(input_resource_t *p_resource)
428 struct vout_resource *vout_rsc = NULL;
430 if( p_resource->vout_rsc_free != NULL )
432 /* The free vout is always the first one */
433 msg_Dbg(p_resource->p_parent, "trying to reuse free vout");
435 vout_rsc = p_resource->vout_rsc_free;
436 p_resource->vout_rsc_free = NULL;
440 /* Use the dummy vout as the parent of the future main vout. This
441 * will allow the future vout to inherit all parameters
442 * pre-configured on this dummy vout. */
443 vlc_object_t *parent = vlc_list_is_empty( &p_resource->vout_rscs ) ?
444 VLC_OBJECT(p_resource->p_vout_dummy) : p_resource->p_parent;
445 vout_thread_t *vout = vout_Create(parent);
449 vout_rsc = vout_resource_Create(vout);
450 if (vout_rsc == NULL)
456 vout_rsc->order = vlc_list_is_empty( &p_resource->vout_rscs ) ?
457 VLC_VOUT_ORDER_PRIMARY : VLC_VOUT_ORDER_SECONDARY;
459 vlc_mutex_lock(&p_resource->lock_hold);
460 vout_resource_Add(vout_rsc, p_resource);
461 vlc_mutex_unlock(&p_resource->lock_hold);
467 vout_thread_t *input_resource_RequestVout(input_resource_t *p_resource,
468 vlc_video_context *vctx,
469 const vout_configuration_t *cfg,
470 enum vlc_vout_order *order,
473 vlc_mutex_lock( &p_resource->lock );
474 struct vout_resource *vout_rsc = NULL;
476 if (has_started != NULL)
477 *has_started = false;
479 vout_configuration_t dcfg = *cfg;
480 if (dcfg.vout == NULL)
482 vout_rsc = RequestVoutRsc(p_resource);
483 if (vout_rsc == NULL)
485 vlc_mutex_unlock( &p_resource->lock );
488 dcfg.vout = vout_rsc->vout;
492 vout_rsc = resource_GetVoutRsc(p_resource, dcfg.vout);
493 assert(vout_rsc != NULL);
495 /* the caller is going to reuse the free vout, it's not free anymore */
496 if (p_resource->vout_rsc_free == vout_rsc)
497 p_resource->vout_rsc_free = NULL;
501 *order = vout_rsc->order;
503 if (dcfg.fmt == NULL)
505 /* A NULL fmt means that only the vout creation is requested, do not
506 * start it with vout_Request(). */
507 vlc_mutex_unlock(&p_resource->lock);
511 if (vout_rsc->started)
513 assert(cfg->vout != NULL);
514 int ret = vout_ChangeSource(dcfg.vout, dcfg.fmt);
517 vlc_mutex_unlock(&p_resource->lock);
522 if (vout_Request(&dcfg, vctx, p_resource->p_input)) {
523 input_resource_PutVoutLocked(p_resource, dcfg.vout, NULL);
524 vlc_mutex_unlock(&p_resource->lock);
528 vout_rsc->started = true;
529 if (has_started != NULL)
532 DisplayVoutTitle(p_resource, cfg->vout);
534 /* Send original viewpoint to the input in order to update other ESes */
535 if (p_resource->p_input != NULL)
537 input_control_param_t param = { .viewpoint = cfg->fmt->pose };
538 input_ControlPush(p_resource->p_input, INPUT_CONTROL_SET_INITIAL_VIEWPOINT,
541 vlc_mutex_unlock( &p_resource->lock );
546 vout_thread_t *input_resource_HoldVout( input_resource_t *p_resource )
548 vlc_mutex_lock( &p_resource->lock_hold );
550 struct vout_resource *vout_rsc = resource_GetFirstVoutRsc( p_resource );
551 vout_thread_t *p_vout = vout_rsc != NULL ? vout_rsc->vout : NULL;
555 vlc_mutex_unlock( &p_resource->lock_hold );
560 vout_thread_t *input_resource_HoldDummyVout( input_resource_t *p_resource )
562 return vout_Hold(p_resource->p_vout_dummy);
565 void input_resource_HoldVouts( input_resource_t *p_resource, vout_thread_t ***ppp_vout,
568 vout_thread_t **pp_vout;
573 vlc_mutex_lock( &p_resource->lock_hold );
575 if( vlc_list_is_empty( &p_resource->vout_rscs ) )
579 struct vout_resource *vout_rsc;
580 vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
583 pp_vout = vlc_alloc( count, sizeof(*pp_vout) );
591 vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
592 pp_vout[count++] = vout_Hold( vout_rsc->vout );
595 vlc_mutex_unlock( &p_resource->lock_hold );
598 void input_resource_StopFreeVout(input_resource_t *p_resource)
600 vlc_mutex_lock(&p_resource->lock);
601 if (p_resource->vout_rsc_free != NULL)
603 msg_Dbg(p_resource->vout_rsc_free->vout, "stop free vout");
604 vout_Stop(p_resource->vout_rsc_free->vout);
606 vlc_mutex_unlock(&p_resource->lock);
610 sout_instance_t *input_resource_RequestSout( input_resource_t *p_resource, const char *psz_sout )
612 assert(psz_sout != NULL);
613 vlc_mutex_lock( &p_resource->lock );
614 sout_instance_t *p_ret = RequestSout( p_resource, NULL, psz_sout );
615 vlc_mutex_unlock( &p_resource->lock );
620 void input_resource_PutSout(input_resource_t *resource, sout_instance_t *sout)
624 input_resource_TerminateSout(resource);
628 vlc_mutex_lock(&resource->lock);
629 assert(resource->p_sout == NULL);
630 resource->p_sout = sout;
631 vlc_mutex_unlock(&resource->lock);
634 void input_resource_TerminateSout( input_resource_t *p_resource )
636 vlc_mutex_lock( &p_resource->lock );
637 DestroySout(p_resource);
638 vlc_mutex_unlock( &p_resource->lock );