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
;
145 static void DestroyVout( input_resource_t
*p_resource
)
147 assert( vlc_list_is_empty( &p_resource
->vout_rscs
)
148 || p_resource
->vout_rsc_free
== resource_GetFirstVoutRsc( p_resource
) );
150 if( p_resource
->vout_rsc_free
)
152 vlc_mutex_lock(&p_resource
->lock_hold
);
153 vout_thread_t
*vout
= vout_resource_Remove( p_resource
->vout_rsc_free
);
154 vlc_mutex_unlock(&p_resource
->lock_hold
);
157 p_resource
->vout_rsc_free
= NULL
;
161 static void DisplayVoutTitle( input_resource_t
*p_resource
,
162 vout_thread_t
*p_vout
)
164 if( p_resource
->p_input
== NULL
)
167 /* TODO display the title only one time for the same input ? */
169 input_item_t
*p_item
= input_GetItem( p_resource
->p_input
);
171 char *psz_nowplaying
= input_item_GetNowPlayingFb( p_item
);
172 if( psz_nowplaying
&& *psz_nowplaying
)
174 vout_DisplayTitle( p_vout
, psz_nowplaying
);
178 char *psz_artist
= input_item_GetArtist( p_item
);
179 char *psz_name
= input_item_GetTitle( p_item
);
181 if( !psz_name
|| *psz_name
== '\0' )
184 psz_name
= input_item_GetName( p_item
);
186 if( psz_artist
&& *psz_artist
)
189 if( asprintf( &psz_string
, "%s - %s", psz_name
, psz_artist
) != -1 )
191 vout_DisplayTitle( p_vout
, psz_string
);
197 vout_DisplayTitle( p_vout
, psz_name
);
202 free( psz_nowplaying
);
206 audio_output_t
*input_resource_GetAout( input_resource_t
*p_resource
)
208 audio_output_t
*p_aout
;
210 vlc_mutex_lock( &p_resource
->lock_hold
);
211 p_aout
= p_resource
->p_aout
;
213 if( p_aout
== NULL
|| p_resource
->b_aout_busy
)
215 msg_Dbg( p_resource
->p_parent
, "creating audio output" );
216 vlc_mutex_unlock( &p_resource
->lock_hold
);
218 p_aout
= aout_New( p_resource
->p_parent
);
220 return NULL
; /* failed */
222 vlc_mutex_lock( &p_resource
->lock_hold
);
223 if( p_resource
->p_aout
== NULL
)
224 p_resource
->p_aout
= p_aout
;
227 msg_Dbg( p_resource
->p_parent
, "reusing audio output" );
229 if( p_resource
->p_aout
== p_aout
)
231 assert( !p_resource
->b_aout_busy
);
232 p_resource
->b_aout_busy
= true;
234 vlc_mutex_unlock( &p_resource
->lock_hold
);
238 void input_resource_PutAout( input_resource_t
*p_resource
,
239 audio_output_t
*p_aout
)
241 assert( p_aout
!= NULL
);
243 vlc_mutex_lock( &p_resource
->lock_hold
);
244 if( p_aout
== p_resource
->p_aout
)
246 assert( p_resource
->b_aout_busy
);
247 p_resource
->b_aout_busy
= false;
248 msg_Dbg( p_resource
->p_parent
, "keeping audio output" );
252 msg_Dbg( p_resource
->p_parent
, "destroying extra audio output" );
253 vlc_mutex_unlock( &p_resource
->lock_hold
);
256 aout_Destroy( p_aout
);
259 audio_output_t
*input_resource_HoldAout( input_resource_t
*p_resource
)
261 audio_output_t
*p_aout
;
263 vlc_mutex_lock( &p_resource
->lock_hold
);
264 p_aout
= p_resource
->p_aout
;
267 vlc_mutex_unlock( &p_resource
->lock_hold
);
272 void input_resource_ResetAout( input_resource_t
*p_resource
)
274 audio_output_t
*p_aout
= NULL
;
276 vlc_mutex_lock( &p_resource
->lock_hold
);
277 if( !p_resource
->b_aout_busy
)
278 p_aout
= p_resource
->p_aout
;
280 p_resource
->p_aout
= NULL
;
281 p_resource
->b_aout_busy
= false;
282 vlc_mutex_unlock( &p_resource
->lock_hold
);
285 aout_Destroy( p_aout
);
289 input_resource_t
*input_resource_New( vlc_object_t
*p_parent
)
291 input_resource_t
*p_resource
= calloc( 1, sizeof(*p_resource
) );
295 p_resource
->p_vout_dummy
= vout_CreateDummy(p_parent
);
296 if( !p_resource
->p_vout_dummy
)
302 vlc_list_init( &p_resource
->vout_rscs
);
304 vlc_atomic_rc_init( &p_resource
->rc
);
305 p_resource
->p_parent
= p_parent
;
306 vlc_mutex_init( &p_resource
->lock
);
307 vlc_mutex_init( &p_resource
->lock_hold
);
311 void input_resource_Release( input_resource_t
*p_resource
)
313 if( !vlc_atomic_rc_dec( &p_resource
->rc
) )
316 DestroySout( p_resource
);
317 DestroyVout( p_resource
);
318 if( p_resource
->p_aout
!= NULL
)
319 aout_Destroy( p_resource
->p_aout
);
321 vout_Release( p_resource
->p_vout_dummy
);
325 input_resource_t
*input_resource_Hold( input_resource_t
*p_resource
)
327 vlc_atomic_rc_inc( &p_resource
->rc
);
331 void input_resource_SetInput( input_resource_t
*p_resource
, input_thread_t
*p_input
)
333 vlc_mutex_lock( &p_resource
->lock
);
335 if( p_resource
->p_input
&& !p_input
)
336 assert( vlc_list_is_empty( &p_resource
->vout_rscs
)
337 || p_resource
->vout_rsc_free
== resource_GetFirstVoutRsc( p_resource
) );
340 p_resource
->p_input
= p_input
;
342 vlc_mutex_unlock( &p_resource
->lock
);
345 static void input_resource_PutVoutLocked(input_resource_t
*p_resource
,
346 vout_thread_t
*vout
, bool *has_stopped
)
348 assert(vout
!= NULL
);
349 struct vout_resource
*vout_rsc
= resource_GetVoutRsc(p_resource
, vout
);
350 assert(vout_rsc
!= NULL
);
352 if (has_stopped
!= NULL
)
353 *has_stopped
= vout_rsc
->started
;
355 if (vout_rsc
->started
)
357 vout_StopDisplay(vout_rsc
->vout
);
358 vout_rsc
->started
= false;
361 if (vout_rsc
== resource_GetFirstVoutRsc(p_resource
))
363 assert(p_resource
->vout_rsc_free
== NULL
|| p_resource
->vout_rsc_free
== vout_rsc
);
365 msg_Dbg(p_resource
->p_parent
, "saving a free vout");
366 p_resource
->vout_rsc_free
= vout_rsc
;
370 msg_Dbg(p_resource
->p_parent
, "destroying vout (already one saved or active)");
372 vlc_mutex_lock(&p_resource
->lock_hold
);
373 vout_resource_Remove(vout_rsc
);
374 vlc_mutex_unlock(&p_resource
->lock_hold
);
381 void input_resource_PutVout(input_resource_t
*p_resource
,
382 vout_thread_t
*vout
, bool *stopped
)
384 vlc_mutex_lock( &p_resource
->lock
);
385 input_resource_PutVoutLocked( p_resource
, vout
, stopped
);
386 vlc_mutex_unlock( &p_resource
->lock
);
389 static struct vout_resource
*
390 RequestVoutRsc(input_resource_t
*p_resource
)
392 struct vout_resource
*vout_rsc
= NULL
;
394 if( p_resource
->vout_rsc_free
!= NULL
)
396 /* The free vout is always the first one */
397 msg_Dbg(p_resource
->p_parent
, "trying to reuse free vout");
399 vout_rsc
= p_resource
->vout_rsc_free
;
400 p_resource
->vout_rsc_free
= NULL
;
404 /* Use the dummy vout as the parent of the future main vout. This
405 * will allow the future vout to inherit all parameters
406 * pre-configured on this dummy vout. */
407 vlc_object_t
*parent
= vlc_list_is_empty( &p_resource
->vout_rscs
) ?
408 VLC_OBJECT(p_resource
->p_vout_dummy
) : p_resource
->p_parent
;
409 vout_thread_t
*vout
= vout_Create(parent
);
413 vout_rsc
= vout_resource_Create(vout
);
414 if (vout_rsc
== NULL
)
420 vout_rsc
->order
= vlc_list_is_empty( &p_resource
->vout_rscs
) ?
421 VLC_VOUT_ORDER_PRIMARY
: VLC_VOUT_ORDER_SECONDARY
;
423 vlc_mutex_lock(&p_resource
->lock_hold
);
424 vout_resource_Add(vout_rsc
, p_resource
);
425 vlc_mutex_unlock(&p_resource
->lock_hold
);
431 vout_thread_t
*input_resource_RequestVout(input_resource_t
*p_resource
,
432 vlc_video_context
*vctx
,
433 const vout_configuration_t
*cfg
,
434 enum vlc_vout_order
*order
,
437 vlc_mutex_lock( &p_resource
->lock
);
438 struct vout_resource
*vout_rsc
= NULL
;
440 if (has_started
!= NULL
)
441 *has_started
= false;
443 vout_configuration_t dcfg
= *cfg
;
444 if (dcfg
.vout
== NULL
)
446 vout_rsc
= RequestVoutRsc(p_resource
);
447 if (vout_rsc
== NULL
)
449 vlc_mutex_unlock( &p_resource
->lock
);
452 dcfg
.vout
= vout_rsc
->vout
;
456 vout_rsc
= resource_GetVoutRsc(p_resource
, dcfg
.vout
);
457 assert(vout_rsc
!= NULL
);
459 /* the caller is going to reuse the free vout, it's not free anymore */
460 if (p_resource
->vout_rsc_free
== vout_rsc
)
461 p_resource
->vout_rsc_free
= NULL
;
465 *order
= vout_rsc
->order
;
467 if (dcfg
.fmt
== NULL
)
469 /* A NULL fmt means that only the vout creation is requested, do not
470 * start it with vout_Request(). */
471 vlc_mutex_unlock(&p_resource
->lock
);
475 if (vout_rsc
->started
)
477 assert(cfg
->vout
!= NULL
);
478 int ret
= vout_ChangeSource(dcfg
.vout
, dcfg
.fmt
);
481 vlc_mutex_unlock(&p_resource
->lock
);
486 if (vout_Request(&dcfg
, vctx
, p_resource
->p_input
)) {
487 input_resource_PutVoutLocked(p_resource
, dcfg
.vout
, NULL
);
488 vlc_mutex_unlock(&p_resource
->lock
);
492 vout_rsc
->started
= true;
493 if (has_started
!= NULL
)
496 DisplayVoutTitle(p_resource
, cfg
->vout
);
498 /* Send original viewpoint to the input in order to update other ESes */
499 if (p_resource
->p_input
!= NULL
)
501 input_control_param_t param
= { .viewpoint
= cfg
->fmt
->pose
};
502 input_ControlPush(p_resource
->p_input
, INPUT_CONTROL_SET_INITIAL_VIEWPOINT
,
505 vlc_mutex_unlock( &p_resource
->lock
);
510 vout_thread_t
*input_resource_HoldVout( input_resource_t
*p_resource
)
512 vlc_mutex_lock( &p_resource
->lock_hold
);
514 struct vout_resource
*vout_rsc
= resource_GetFirstVoutRsc( p_resource
);
515 vout_thread_t
*p_vout
= vout_rsc
!= NULL
? vout_rsc
->vout
: NULL
;
519 vlc_mutex_unlock( &p_resource
->lock_hold
);
524 vout_thread_t
*input_resource_HoldDummyVout( input_resource_t
*p_resource
)
526 return vout_Hold(p_resource
->p_vout_dummy
);
529 void input_resource_HoldVouts( input_resource_t
*p_resource
, vout_thread_t
***ppp_vout
,
532 vout_thread_t
**pp_vout
;
537 vlc_mutex_lock( &p_resource
->lock_hold
);
539 if( vlc_list_is_empty( &p_resource
->vout_rscs
) )
543 struct vout_resource
*vout_rsc
;
544 vlc_list_foreach( vout_rsc
, &p_resource
->vout_rscs
, node
)
547 pp_vout
= vlc_alloc( count
, sizeof(*pp_vout
) );
555 vlc_list_foreach( vout_rsc
, &p_resource
->vout_rscs
, node
)
556 pp_vout
[count
++] = vout_Hold( vout_rsc
->vout
);
559 vlc_mutex_unlock( &p_resource
->lock_hold
);
562 void input_resource_StopFreeVout(input_resource_t
*p_resource
)
564 vlc_mutex_lock(&p_resource
->lock
);
565 if (p_resource
->vout_rsc_free
!= NULL
)
567 msg_Dbg(p_resource
->vout_rsc_free
->vout
, "stop free vout");
568 vout_Stop(p_resource
->vout_rsc_free
->vout
);
570 vlc_mutex_unlock(&p_resource
->lock
);
574 sout_instance_t
*input_resource_RequestSout( input_resource_t
*p_resource
, const char *psz_sout
)
576 sout_instance_t
*sout
;
578 assert(psz_sout
!= NULL
);
579 vlc_mutex_lock( &p_resource
->lock
);
581 /* Check the validity of the sout */
582 if (p_resource
->p_sout
!= NULL
583 && strcmp(p_resource
->p_sout
->psz_sout
, psz_sout
) != 0)
585 msg_Dbg(p_resource
->p_parent
, "destroying unusable sout");
586 DestroySout(p_resource
);
589 sout
= p_resource
->p_sout
;
594 msg_Dbg(p_resource
->p_parent
, "reusing sout");
595 msg_Dbg(p_resource
->p_parent
, "you probably want to use gather stream_out");
596 p_resource
->p_sout
= NULL
;
600 /* Create a new one */
601 sout
= sout_NewInstance(p_resource
->p_parent
, psz_sout
);
606 vlc_mutex_unlock( &p_resource
->lock
);
610 void input_resource_PutSout(input_resource_t
*resource
, sout_instance_t
*sout
)
614 input_resource_TerminateSout(resource
);
618 vlc_mutex_lock(&resource
->lock
);
619 assert(resource
->p_sout
== NULL
);
620 resource
->p_sout
= sout
;
621 vlc_mutex_unlock(&resource
->lock
);
624 void input_resource_TerminateSout( input_resource_t
*p_resource
)
626 vlc_mutex_lock( &p_resource
->lock
);
627 DestroySout(p_resource
);
628 vlc_mutex_unlock( &p_resource
->lock
);