Update Copyright Years to 2021
[vlc.git] / src / input / resource.c
blob530da5d6eb2bd68a63994d9efa099c09058c1853
1 /*****************************************************************************
2 * resource.c
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 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <assert.h>
32 #include <vlc_common.h>
33 #include <vlc_atomic.h>
34 #include <vlc_decoder.h>
35 #include <vlc_vout.h>
36 #include <vlc_spu.h>
37 #include <vlc_aout.h>
38 #include <vlc_sout.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"
44 #include "event.h"
45 #include "resource.h"
47 struct vout_resource
49 vout_thread_t *vout;
50 enum vlc_vout_order order;
51 bool started;
53 struct vlc_list node;
56 struct input_resource_t
58 vlc_atomic_rc_t rc;
60 vlc_object_t *p_parent;
62 /* This lock is used to serialize request and protect
63 * our variables */
64 vlc_mutex_t lock;
66 /* */
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;
83 bool b_aout_busy;
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)
97 return vout_rsc;
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))
107 return NULL;
109 vout_rsc->started = false;
110 vout_rsc->vout = vout;
111 return vout_rsc;
114 static inline void
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;
125 free(vout_rsc);
127 assert(vout);
128 return vout;
131 /* */
132 static void DestroySout( input_resource_t *p_resource )
134 #ifdef ENABLE_SOUT
135 if( p_resource->p_sout )
136 sout_DeleteInstance( p_resource->p_sout );
137 #endif
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 )
144 #ifdef ENABLE_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 );
152 return NULL;
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 );
165 if( psz_sout )
167 if( p_resource->p_sout )
169 /* Reuse it */
170 msg_Dbg( p_resource->p_parent, "reusing sout" );
171 msg_Dbg( p_resource->p_parent, "you probably want to use gather stream_out" );
173 else
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;
182 return p_sout;
184 else
186 p_resource->p_sout = p_sout;
187 return NULL;
189 #else
190 VLC_UNUSED (p_resource); VLC_UNUSED (p_sout); VLC_UNUSED (psz_sout);
191 return NULL;
192 #endif
195 /* */
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);
207 vout_Close( vout );
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 )
216 return;
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 );
227 else
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' )
234 free( psz_name );
235 psz_name = input_item_GetName( p_item );
237 if( psz_artist && *psz_artist )
239 char *psz_string;
240 if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
242 vout_DisplayTitle( p_vout, psz_string );
243 free( psz_string );
246 else if( psz_name )
248 vout_DisplayTitle( p_vout, psz_name );
250 free( psz_name );
251 free( psz_artist );
253 free( psz_nowplaying );
256 /* Audio output */
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 );
270 if( p_aout == NULL )
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;
277 else
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 );
286 return p_aout;
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" );
300 p_aout = NULL;
302 else
303 msg_Dbg( p_resource->p_parent, "destroying extra audio output" );
304 vlc_mutex_unlock( &p_resource->lock_hold );
306 if( p_aout != NULL )
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;
316 if( p_aout != NULL )
317 aout_Hold(p_aout);
318 vlc_mutex_unlock( &p_resource->lock_hold );
320 return p_aout;
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 );
335 if( p_aout != NULL )
336 aout_Destroy( p_aout );
339 /* Common */
340 input_resource_t *input_resource_New( vlc_object_t *p_parent )
342 input_resource_t *p_resource = calloc( 1, sizeof(*p_resource) );
343 if( !p_resource )
344 return NULL;
346 p_resource->p_vout_dummy = vout_CreateDummy(p_parent);
347 if( !p_resource->p_vout_dummy )
349 free( p_resource );
350 return NULL;
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 );
359 return p_resource;
362 void input_resource_Release( input_resource_t *p_resource )
364 if( !vlc_atomic_rc_dec( &p_resource->rc ) )
365 return;
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 );
373 free( p_resource );
376 input_resource_t *input_resource_Hold( input_resource_t *p_resource )
378 vlc_atomic_rc_inc( &p_resource->rc );
379 return p_resource;
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 ) );
390 /* */
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;
419 else
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);
427 vout_Stop(vout);
428 vout_Close(vout);
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;
453 else
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);
461 if (vout == NULL)
462 return NULL;
464 vout_rsc = vout_resource_Create(vout);
465 if (vout_rsc == NULL)
467 vout_Close(vout);
468 return 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);
479 return vout_rsc;
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,
486 bool *has_started)
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 );
501 return NULL;
503 dcfg.vout = vout_rsc->vout;
505 else
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;
515 if (order != 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);
523 return dcfg.vout;
526 if (vout_rsc->started)
528 assert(cfg->vout != NULL);
529 int ret = vout_ChangeSource(dcfg.vout, dcfg.fmt);
530 if (ret == 0)
532 vlc_mutex_unlock(&p_resource->lock);
533 return dcfg.vout;
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);
540 return NULL;
543 vout_rsc->started = true;
544 if (has_started != NULL)
545 *has_started = true;
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,
554 &param);
556 vlc_mutex_unlock( &p_resource->lock );
558 return dcfg.vout;
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;
567 if( p_vout )
568 vout_Hold(p_vout);
570 vlc_mutex_unlock( &p_resource->lock_hold );
572 return p_vout;
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,
581 size_t *pi_vout )
583 vout_thread_t **pp_vout;
585 *pi_vout = 0;
586 *ppp_vout = NULL;
588 vlc_mutex_lock( &p_resource->lock_hold );
590 if( vlc_list_is_empty( &p_resource->vout_rscs ) )
591 goto exit;
593 size_t count = 0;
594 struct vout_resource *vout_rsc;
595 vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
596 count ++;
598 pp_vout = vlc_alloc( count, sizeof(*pp_vout) );
599 if( !pp_vout )
600 goto exit;
602 *ppp_vout = pp_vout;
603 *pi_vout = count;;
605 count = 0;
606 vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
607 pp_vout[count++] = vout_Hold( vout_rsc->vout );
609 exit:
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);
624 /* */
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 );
631 return p_ret;
633 void input_resource_TerminateSout( input_resource_t *p_resource )
635 input_resource_RequestSout( p_resource, NULL, NULL );