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
)
136 sout_DeleteInstance( p_resource
->p_sout
);
138 p_resource
->p_sout
= NULL
;
141 static sout_instance_t
*RequestSout( input_resource_t
*p_resource
,
142 sout_instance_t
*p_sout
, const char *psz_sout
)
145 if( !p_sout
&& !psz_sout
)
147 if( p_resource
->p_sout
)
149 msg_Dbg( p_resource
->p_parent
, "destroying useless sout" );
150 DestroySout( p_resource
);
155 assert( !p_sout
|| ( !p_resource
->p_sout
&& !psz_sout
) );
157 /* Check the validity of the sout */
158 if( p_resource
->p_sout
&&
159 strcmp( p_resource
->p_sout
->psz_sout
, psz_sout
) )
161 msg_Dbg( p_resource
->p_parent
, "destroying unusable sout" );
162 DestroySout( p_resource
);
167 if( p_resource
->p_sout
)
170 msg_Dbg( p_resource
->p_parent
, "reusing sout" );
171 msg_Dbg( p_resource
->p_parent
, "you probably want to use gather stream_out" );
175 /* Create a new one */
176 p_resource
->p_sout
= sout_NewInstance( p_resource
->p_parent
, psz_sout
);
179 p_sout
= p_resource
->p_sout
;
180 p_resource
->p_sout
= NULL
;
186 p_resource
->p_sout
= p_sout
;
190 VLC_UNUSED (p_resource
); VLC_UNUSED (p_sout
); VLC_UNUSED (psz_sout
);
196 static void DestroyVout( input_resource_t
*p_resource
)
198 assert( vlc_list_is_empty( &p_resource
->vout_rscs
)
199 || p_resource
->vout_rsc_free
== resource_GetFirstVoutRsc( p_resource
) );
201 if( p_resource
->vout_rsc_free
)
203 vlc_mutex_lock(&p_resource
->lock_hold
);
204 vout_thread_t
*vout
= vout_resource_Remove( p_resource
->vout_rsc_free
);
205 vlc_mutex_unlock(&p_resource
->lock_hold
);
208 p_resource
->vout_rsc_free
= NULL
;
212 static void DisplayVoutTitle( input_resource_t
*p_resource
,
213 vout_thread_t
*p_vout
)
215 if( p_resource
->p_input
== NULL
)
218 /* TODO display the title only one time for the same input ? */
220 input_item_t
*p_item
= input_GetItem( p_resource
->p_input
);
222 char *psz_nowplaying
= input_item_GetNowPlayingFb( p_item
);
223 if( psz_nowplaying
&& *psz_nowplaying
)
225 vout_DisplayTitle( p_vout
, psz_nowplaying
);
229 char *psz_artist
= input_item_GetArtist( p_item
);
230 char *psz_name
= input_item_GetTitle( p_item
);
232 if( !psz_name
|| *psz_name
== '\0' )
235 psz_name
= input_item_GetName( p_item
);
237 if( psz_artist
&& *psz_artist
)
240 if( asprintf( &psz_string
, "%s - %s", psz_name
, psz_artist
) != -1 )
242 vout_DisplayTitle( p_vout
, psz_string
);
248 vout_DisplayTitle( p_vout
, psz_name
);
253 free( psz_nowplaying
);
257 audio_output_t
*input_resource_GetAout( input_resource_t
*p_resource
)
259 audio_output_t
*p_aout
;
261 vlc_mutex_lock( &p_resource
->lock_hold
);
262 p_aout
= p_resource
->p_aout
;
264 if( p_aout
== NULL
|| p_resource
->b_aout_busy
)
266 msg_Dbg( p_resource
->p_parent
, "creating audio output" );
267 vlc_mutex_unlock( &p_resource
->lock_hold
);
269 p_aout
= aout_New( p_resource
->p_parent
);
271 return NULL
; /* failed */
273 vlc_mutex_lock( &p_resource
->lock_hold
);
274 if( p_resource
->p_aout
== NULL
)
275 p_resource
->p_aout
= p_aout
;
278 msg_Dbg( p_resource
->p_parent
, "reusing audio output" );
280 if( p_resource
->p_aout
== p_aout
)
282 assert( !p_resource
->b_aout_busy
);
283 p_resource
->b_aout_busy
= true;
285 vlc_mutex_unlock( &p_resource
->lock_hold
);
289 void input_resource_PutAout( input_resource_t
*p_resource
,
290 audio_output_t
*p_aout
)
292 assert( p_aout
!= NULL
);
294 vlc_mutex_lock( &p_resource
->lock_hold
);
295 if( p_aout
== p_resource
->p_aout
)
297 assert( p_resource
->b_aout_busy
);
298 p_resource
->b_aout_busy
= false;
299 msg_Dbg( p_resource
->p_parent
, "keeping audio output" );
303 msg_Dbg( p_resource
->p_parent
, "destroying extra audio output" );
304 vlc_mutex_unlock( &p_resource
->lock_hold
);
307 aout_Destroy( p_aout
);
310 audio_output_t
*input_resource_HoldAout( input_resource_t
*p_resource
)
312 audio_output_t
*p_aout
;
314 vlc_mutex_lock( &p_resource
->lock_hold
);
315 p_aout
= p_resource
->p_aout
;
318 vlc_mutex_unlock( &p_resource
->lock_hold
);
323 void input_resource_ResetAout( input_resource_t
*p_resource
)
325 audio_output_t
*p_aout
= NULL
;
327 vlc_mutex_lock( &p_resource
->lock_hold
);
328 if( !p_resource
->b_aout_busy
)
329 p_aout
= p_resource
->p_aout
;
331 p_resource
->p_aout
= NULL
;
332 p_resource
->b_aout_busy
= false;
333 vlc_mutex_unlock( &p_resource
->lock_hold
);
336 aout_Destroy( p_aout
);
340 input_resource_t
*input_resource_New( vlc_object_t
*p_parent
)
342 input_resource_t
*p_resource
= calloc( 1, sizeof(*p_resource
) );
346 p_resource
->p_vout_dummy
= vout_CreateDummy(p_parent
);
347 if( !p_resource
->p_vout_dummy
)
353 vlc_list_init( &p_resource
->vout_rscs
);
355 vlc_atomic_rc_init( &p_resource
->rc
);
356 p_resource
->p_parent
= p_parent
;
357 vlc_mutex_init( &p_resource
->lock
);
358 vlc_mutex_init( &p_resource
->lock_hold
);
362 void input_resource_Release( input_resource_t
*p_resource
)
364 if( !vlc_atomic_rc_dec( &p_resource
->rc
) )
367 DestroySout( p_resource
);
368 DestroyVout( p_resource
);
369 if( p_resource
->p_aout
!= NULL
)
370 aout_Destroy( p_resource
->p_aout
);
372 vout_Release( p_resource
->p_vout_dummy
);
376 input_resource_t
*input_resource_Hold( input_resource_t
*p_resource
)
378 vlc_atomic_rc_inc( &p_resource
->rc
);
382 void input_resource_SetInput( input_resource_t
*p_resource
, input_thread_t
*p_input
)
384 vlc_mutex_lock( &p_resource
->lock
);
386 if( p_resource
->p_input
&& !p_input
)
387 assert( vlc_list_is_empty( &p_resource
->vout_rscs
)
388 || p_resource
->vout_rsc_free
== resource_GetFirstVoutRsc( p_resource
) );
391 p_resource
->p_input
= p_input
;
393 vlc_mutex_unlock( &p_resource
->lock
);
396 static void input_resource_PutVoutLocked(input_resource_t
*p_resource
,
397 vout_thread_t
*vout
, bool *has_stopped
)
399 assert(vout
!= NULL
);
400 struct vout_resource
*vout_rsc
= resource_GetVoutRsc(p_resource
, vout
);
401 assert(vout_rsc
!= NULL
);
403 if (has_stopped
!= NULL
)
404 *has_stopped
= vout_rsc
->started
;
406 if (vout_rsc
->started
)
408 vout_StopDisplay(vout_rsc
->vout
);
409 vout_rsc
->started
= false;
412 if (vout_rsc
== resource_GetFirstVoutRsc(p_resource
))
414 assert(p_resource
->vout_rsc_free
== NULL
|| p_resource
->vout_rsc_free
== vout_rsc
);
416 msg_Dbg(p_resource
->p_parent
, "saving a free vout");
417 p_resource
->vout_rsc_free
= vout_rsc
;
421 msg_Dbg(p_resource
->p_parent
, "destroying vout (already one saved or active)");
423 vlc_mutex_lock(&p_resource
->lock_hold
);
424 vout_resource_Remove(vout_rsc
);
425 vlc_mutex_unlock(&p_resource
->lock_hold
);
432 void input_resource_PutVout(input_resource_t
*p_resource
,
433 vout_thread_t
*vout
, bool *stopped
)
435 vlc_mutex_lock( &p_resource
->lock
);
436 input_resource_PutVoutLocked( p_resource
, vout
, stopped
);
437 vlc_mutex_unlock( &p_resource
->lock
);
440 static struct vout_resource
*
441 RequestVoutRsc(input_resource_t
*p_resource
)
443 struct vout_resource
*vout_rsc
= NULL
;
445 if( p_resource
->vout_rsc_free
!= NULL
)
447 /* The free vout is always the first one */
448 msg_Dbg(p_resource
->p_parent
, "trying to reuse free vout");
450 vout_rsc
= p_resource
->vout_rsc_free
;
451 p_resource
->vout_rsc_free
= NULL
;
455 /* Use the dummy vout as the parent of the future main vout. This
456 * will allow the future vout to inherit all parameters
457 * pre-configured on this dummy vout. */
458 vlc_object_t
*parent
= vlc_list_is_empty( &p_resource
->vout_rscs
) ?
459 VLC_OBJECT(p_resource
->p_vout_dummy
) : p_resource
->p_parent
;
460 vout_thread_t
*vout
= vout_Create(parent
);
464 vout_rsc
= vout_resource_Create(vout
);
465 if (vout_rsc
== NULL
)
471 vout_rsc
->order
= vlc_list_is_empty( &p_resource
->vout_rscs
) ?
472 VLC_VOUT_ORDER_PRIMARY
: VLC_VOUT_ORDER_SECONDARY
;
474 vlc_mutex_lock(&p_resource
->lock_hold
);
475 vout_resource_Add(vout_rsc
, p_resource
);
476 vlc_mutex_unlock(&p_resource
->lock_hold
);
482 vout_thread_t
*input_resource_RequestVout(input_resource_t
*p_resource
,
483 vlc_video_context
*vctx
,
484 const vout_configuration_t
*cfg
,
485 enum vlc_vout_order
*order
,
488 vlc_mutex_lock( &p_resource
->lock
);
489 struct vout_resource
*vout_rsc
= NULL
;
491 if (has_started
!= NULL
)
492 *has_started
= false;
494 vout_configuration_t dcfg
= *cfg
;
495 if (dcfg
.vout
== NULL
)
497 vout_rsc
= RequestVoutRsc(p_resource
);
498 if (vout_rsc
== NULL
)
500 vlc_mutex_unlock( &p_resource
->lock
);
503 dcfg
.vout
= vout_rsc
->vout
;
507 vout_rsc
= resource_GetVoutRsc(p_resource
, dcfg
.vout
);
508 assert(vout_rsc
!= NULL
);
510 /* the caller is going to reuse the free vout, it's not free anymore */
511 if (p_resource
->vout_rsc_free
== vout_rsc
)
512 p_resource
->vout_rsc_free
= NULL
;
516 *order
= vout_rsc
->order
;
518 if (dcfg
.fmt
== NULL
)
520 /* A NULL fmt means that only the vout creation is requested, do not
521 * start it with vout_Request(). */
522 vlc_mutex_unlock(&p_resource
->lock
);
526 if (vout_rsc
->started
)
528 assert(cfg
->vout
!= NULL
);
529 int ret
= vout_ChangeSource(dcfg
.vout
, dcfg
.fmt
);
532 vlc_mutex_unlock(&p_resource
->lock
);
537 if (vout_Request(&dcfg
, vctx
, p_resource
->p_input
)) {
538 input_resource_PutVoutLocked(p_resource
, dcfg
.vout
, NULL
);
539 vlc_mutex_unlock(&p_resource
->lock
);
543 vout_rsc
->started
= true;
544 if (has_started
!= NULL
)
547 DisplayVoutTitle(p_resource
, cfg
->vout
);
549 /* Send original viewpoint to the input in order to update other ESes */
550 if (p_resource
->p_input
!= NULL
)
552 input_control_param_t param
= { .viewpoint
= cfg
->fmt
->pose
};
553 input_ControlPush(p_resource
->p_input
, INPUT_CONTROL_SET_INITIAL_VIEWPOINT
,
556 vlc_mutex_unlock( &p_resource
->lock
);
561 vout_thread_t
*input_resource_HoldVout( input_resource_t
*p_resource
)
563 vlc_mutex_lock( &p_resource
->lock_hold
);
565 struct vout_resource
*vout_rsc
= resource_GetFirstVoutRsc( p_resource
);
566 vout_thread_t
*p_vout
= vout_rsc
!= NULL
? vout_rsc
->vout
: NULL
;
570 vlc_mutex_unlock( &p_resource
->lock_hold
);
575 vout_thread_t
*input_resource_HoldDummyVout( input_resource_t
*p_resource
)
577 return vout_Hold(p_resource
->p_vout_dummy
);
580 void input_resource_HoldVouts( input_resource_t
*p_resource
, vout_thread_t
***ppp_vout
,
583 vout_thread_t
**pp_vout
;
588 vlc_mutex_lock( &p_resource
->lock_hold
);
590 if( vlc_list_is_empty( &p_resource
->vout_rscs
) )
594 struct vout_resource
*vout_rsc
;
595 vlc_list_foreach( vout_rsc
, &p_resource
->vout_rscs
, node
)
598 pp_vout
= vlc_alloc( count
, sizeof(*pp_vout
) );
606 vlc_list_foreach( vout_rsc
, &p_resource
->vout_rscs
, node
)
607 pp_vout
[count
++] = vout_Hold( vout_rsc
->vout
);
610 vlc_mutex_unlock( &p_resource
->lock_hold
);
613 void input_resource_StopFreeVout(input_resource_t
*p_resource
)
615 vlc_mutex_lock(&p_resource
->lock
);
616 if (p_resource
->vout_rsc_free
!= NULL
)
618 msg_Dbg(p_resource
->vout_rsc_free
->vout
, "stop free vout");
619 vout_Stop(p_resource
->vout_rsc_free
->vout
);
621 vlc_mutex_unlock(&p_resource
->lock
);
625 sout_instance_t
*input_resource_RequestSout( input_resource_t
*p_resource
, sout_instance_t
*p_sout
, const char *psz_sout
)
627 vlc_mutex_lock( &p_resource
->lock
);
628 sout_instance_t
*p_ret
= RequestSout( p_resource
, p_sout
, psz_sout
);
629 vlc_mutex_unlock( &p_resource
->lock
);
633 void input_resource_TerminateSout( input_resource_t
*p_resource
)
635 input_resource_RequestSout( p_resource
, NULL
, NULL
);