resource: inline RequestSout()
[vlc.git] / src / input / resource.c
1 /*****************************************************************************
2  * resource.c
3  *****************************************************************************
4  * Copyright (C) 2008 Laurent Aimar
5  *
6  * Authors: Laurent Aimar < fenrir _AT_ videolan _DOT_ org >
7  *
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.
12  *
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.
17  *
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  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <assert.h>
31
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"
46
47 struct vout_resource
48 {
49     vout_thread_t *vout;
50     enum vlc_vout_order order;
51     bool started;
52
53     struct vlc_list node;
54 };
55
56 struct input_resource_t
57 {
58     vlc_atomic_rc_t rc;
59
60     vlc_object_t   *p_parent;
61
62     /* This lock is used to serialize request and protect
63      * our variables */
64     vlc_mutex_t    lock;
65
66     /* */
67     input_thread_t *p_input;
68
69     sout_instance_t *p_sout;
70     vout_thread_t   *p_vout_dummy;
71     struct vout_resource *vout_rsc_free;
72
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)
76      */
77     vlc_mutex_t    lock_hold;
78
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;
82
83     bool            b_aout_busy;
84     audio_output_t *p_aout;
85 };
86
87 #define resource_GetFirstVoutRsc(resource) \
88     vlc_list_first_entry_or_null(&resource->vout_rscs, struct vout_resource, node)
89
90 static inline struct vout_resource *
91 resource_GetVoutRsc(input_resource_t *resource, vout_thread_t *vout)
92 {
93     struct vout_resource *vout_rsc;
94
95     vlc_list_foreach(vout_rsc, &resource->vout_rscs, node)
96         if (vout_rsc->vout == vout)
97             return vout_rsc;
98
99     vlc_assert_unreachable();
100 }
101
102 static inline struct vout_resource *
103 vout_resource_Create(vout_thread_t *vout)
104 {
105     struct vout_resource *vout_rsc = malloc(sizeof(*vout_rsc));
106     if (unlikely(vout_rsc == NULL))
107         return NULL;
108
109     vout_rsc->started = false;
110     vout_rsc->vout = vout;
111     return vout_rsc;
112 }
113
114 static inline void
115 vout_resource_Add(struct vout_resource *vout_rsc, input_resource_t *resource)
116 {
117     vlc_list_append(&vout_rsc->node, &resource->vout_rscs);
118 }
119
120 static inline vout_thread_t *
121 vout_resource_Remove(struct vout_resource *vout_rsc)
122 {
123     vlc_list_remove(&vout_rsc->node);
124     vout_thread_t *vout = vout_rsc->vout;
125     free(vout_rsc);
126
127     assert(vout);
128     return vout;
129 }
130
131 /* */
132 static void DestroySout( input_resource_t *p_resource )
133 {
134 #ifdef ENABLE_SOUT
135     if( p_resource->p_sout )
136     {
137         msg_Dbg( p_resource->p_parent, "destroying stream output" );
138         sout_DeleteInstance( p_resource->p_sout );
139     }
140 #endif
141     p_resource->p_sout = NULL;
142 }
143
144 /* */
145 static void DestroyVout( input_resource_t *p_resource )
146 {
147     assert( vlc_list_is_empty( &p_resource->vout_rscs )
148          || p_resource->vout_rsc_free == resource_GetFirstVoutRsc( p_resource ) );
149
150     if( p_resource->vout_rsc_free )
151     {
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);
155
156         vout_Close( vout );
157         p_resource->vout_rsc_free = NULL;
158     }
159 }
160
161 static void DisplayVoutTitle( input_resource_t *p_resource,
162                               vout_thread_t *p_vout )
163 {
164     if( p_resource->p_input == NULL )
165         return;
166
167     /* TODO display the title only one time for the same input ? */
168
169     input_item_t *p_item = input_GetItem( p_resource->p_input );
170
171     char *psz_nowplaying = input_item_GetNowPlayingFb( p_item );
172     if( psz_nowplaying && *psz_nowplaying )
173     {
174         vout_DisplayTitle( p_vout, psz_nowplaying );
175     }
176     else
177     {
178         char *psz_artist = input_item_GetArtist( p_item );
179         char *psz_name = input_item_GetTitle( p_item );
180
181         if( !psz_name || *psz_name == '\0' )
182         {
183             free( psz_name );
184             psz_name = input_item_GetName( p_item );
185         }
186         if( psz_artist && *psz_artist )
187         {
188             char *psz_string;
189             if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
190             {
191                 vout_DisplayTitle( p_vout, psz_string );
192                 free( psz_string );
193             }
194         }
195         else if( psz_name )
196         {
197             vout_DisplayTitle( p_vout, psz_name );
198         }
199         free( psz_name );
200         free( psz_artist );
201     }
202     free( psz_nowplaying );
203 }
204
205 /* Audio output */
206 audio_output_t *input_resource_GetAout( input_resource_t *p_resource )
207 {
208     audio_output_t *p_aout;
209
210     vlc_mutex_lock( &p_resource->lock_hold );
211     p_aout = p_resource->p_aout;
212
213     if( p_aout == NULL || p_resource->b_aout_busy )
214     {
215         msg_Dbg( p_resource->p_parent, "creating audio output" );
216         vlc_mutex_unlock( &p_resource->lock_hold );
217
218         p_aout = aout_New( p_resource->p_parent );
219         if( p_aout == NULL )
220             return NULL; /* failed */
221
222         vlc_mutex_lock( &p_resource->lock_hold );
223         if( p_resource->p_aout == NULL )
224             p_resource->p_aout = p_aout;
225     }
226     else
227         msg_Dbg( p_resource->p_parent, "reusing audio output" );
228
229     if( p_resource->p_aout == p_aout )
230     {
231         assert( !p_resource->b_aout_busy );
232         p_resource->b_aout_busy = true;
233     }
234     vlc_mutex_unlock( &p_resource->lock_hold );
235     return p_aout;
236 }
237
238 void input_resource_PutAout( input_resource_t *p_resource,
239                              audio_output_t *p_aout )
240 {
241     assert( p_aout != NULL );
242
243     vlc_mutex_lock( &p_resource->lock_hold );
244     if( p_aout == p_resource->p_aout )
245     {
246         assert( p_resource->b_aout_busy );
247         p_resource->b_aout_busy = false;
248         msg_Dbg( p_resource->p_parent, "keeping audio output" );
249         p_aout = NULL;
250     }
251     else
252         msg_Dbg( p_resource->p_parent, "destroying extra audio output" );
253     vlc_mutex_unlock( &p_resource->lock_hold );
254
255     if( p_aout != NULL )
256         aout_Destroy( p_aout );
257 }
258
259 audio_output_t *input_resource_HoldAout( input_resource_t *p_resource )
260 {
261     audio_output_t *p_aout;
262
263     vlc_mutex_lock( &p_resource->lock_hold );
264     p_aout = p_resource->p_aout;
265     if( p_aout != NULL )
266         aout_Hold(p_aout);
267     vlc_mutex_unlock( &p_resource->lock_hold );
268
269     return p_aout;
270 }
271
272 void input_resource_ResetAout( input_resource_t *p_resource )
273 {
274     audio_output_t *p_aout = NULL;
275
276     vlc_mutex_lock( &p_resource->lock_hold );
277     if( !p_resource->b_aout_busy )
278         p_aout = p_resource->p_aout;
279
280     p_resource->p_aout = NULL;
281     p_resource->b_aout_busy = false;
282     vlc_mutex_unlock( &p_resource->lock_hold );
283
284     if( p_aout != NULL )
285         aout_Destroy( p_aout );
286 }
287
288 /* Common */
289 input_resource_t *input_resource_New( vlc_object_t *p_parent )
290 {
291     input_resource_t *p_resource = calloc( 1, sizeof(*p_resource) );
292     if( !p_resource )
293         return NULL;
294
295     p_resource->p_vout_dummy = vout_CreateDummy(p_parent);
296     if( !p_resource->p_vout_dummy )
297     {
298         free( p_resource );
299         return NULL;
300     }
301
302     vlc_list_init( &p_resource->vout_rscs );
303
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 );
308     return p_resource;
309 }
310
311 void input_resource_Release( input_resource_t *p_resource )
312 {
313     if( !vlc_atomic_rc_dec( &p_resource->rc ) )
314         return;
315
316     DestroySout( p_resource );
317     DestroyVout( p_resource );
318     if( p_resource->p_aout != NULL )
319         aout_Destroy( p_resource->p_aout );
320
321     vout_Release( p_resource->p_vout_dummy );
322     free( p_resource );
323 }
324
325 input_resource_t *input_resource_Hold( input_resource_t *p_resource )
326 {
327     vlc_atomic_rc_inc( &p_resource->rc );
328     return p_resource;
329 }
330
331 void input_resource_SetInput( input_resource_t *p_resource, input_thread_t *p_input )
332 {
333     vlc_mutex_lock( &p_resource->lock );
334
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 ) );
338
339     /* */
340     p_resource->p_input = p_input;
341
342     vlc_mutex_unlock( &p_resource->lock );
343 }
344
345 static void input_resource_PutVoutLocked(input_resource_t *p_resource,
346                                          vout_thread_t *vout, bool *has_stopped)
347 {
348     assert(vout != NULL);
349     struct vout_resource *vout_rsc = resource_GetVoutRsc(p_resource, vout);
350     assert(vout_rsc != NULL);
351
352     if (has_stopped != NULL)
353         *has_stopped = vout_rsc->started;
354
355     if (vout_rsc->started)
356     {
357         vout_StopDisplay(vout_rsc->vout);
358         vout_rsc->started = false;
359     }
360
361     if (vout_rsc == resource_GetFirstVoutRsc(p_resource))
362     {
363         assert(p_resource->vout_rsc_free == NULL || p_resource->vout_rsc_free == vout_rsc);
364
365         msg_Dbg(p_resource->p_parent, "saving a free vout");
366         p_resource->vout_rsc_free = vout_rsc;
367     }
368     else
369     {
370         msg_Dbg(p_resource->p_parent, "destroying vout (already one saved or active)");
371
372         vlc_mutex_lock(&p_resource->lock_hold);
373         vout_resource_Remove(vout_rsc);
374         vlc_mutex_unlock(&p_resource->lock_hold);
375
376         vout_Stop(vout);
377         vout_Close(vout);
378     }
379 }
380
381 void input_resource_PutVout(input_resource_t *p_resource,
382                                    vout_thread_t *vout, bool *stopped)
383 {
384     vlc_mutex_lock( &p_resource->lock );
385     input_resource_PutVoutLocked( p_resource, vout, stopped );
386     vlc_mutex_unlock( &p_resource->lock );
387 }
388
389 static struct vout_resource *
390 RequestVoutRsc(input_resource_t *p_resource)
391 {
392     struct vout_resource *vout_rsc = NULL;
393
394     if( p_resource->vout_rsc_free != NULL )
395     {
396         /* The free vout is always the first one */
397         msg_Dbg(p_resource->p_parent, "trying to reuse free vout");
398
399         vout_rsc = p_resource->vout_rsc_free;
400         p_resource->vout_rsc_free = NULL;
401     }
402     else
403     {
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);
410         if (vout == NULL)
411             return NULL;
412
413         vout_rsc = vout_resource_Create(vout);
414         if (vout_rsc == NULL)
415         {
416             vout_Close(vout);
417             return NULL;
418         }
419
420         vout_rsc->order = vlc_list_is_empty( &p_resource->vout_rscs ) ?
421             VLC_VOUT_ORDER_PRIMARY : VLC_VOUT_ORDER_SECONDARY;
422
423         vlc_mutex_lock(&p_resource->lock_hold);
424         vout_resource_Add(vout_rsc, p_resource);
425         vlc_mutex_unlock(&p_resource->lock_hold);
426     }
427
428     return vout_rsc;
429 }
430
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,
435                                           bool *has_started)
436 {
437     vlc_mutex_lock( &p_resource->lock );
438     struct vout_resource *vout_rsc = NULL;
439
440     if (has_started != NULL)
441         *has_started = false;
442
443     vout_configuration_t dcfg = *cfg;
444     if (dcfg.vout == NULL)
445     {
446         vout_rsc = RequestVoutRsc(p_resource);
447         if (vout_rsc == NULL)
448         {
449             vlc_mutex_unlock( &p_resource->lock );
450             return NULL;
451         }
452         dcfg.vout = vout_rsc->vout;
453     }
454     else
455     {
456         vout_rsc = resource_GetVoutRsc(p_resource, dcfg.vout);
457         assert(vout_rsc != NULL);
458
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;
462     }
463
464     if (order != NULL)
465         *order = vout_rsc->order;
466
467     if (dcfg.fmt == NULL)
468     {
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);
472         return dcfg.vout;
473     }
474
475     if (vout_rsc->started)
476     {
477         assert(cfg->vout != NULL);
478         int ret = vout_ChangeSource(dcfg.vout, dcfg.fmt);
479         if (ret == 0)
480         {
481             vlc_mutex_unlock(&p_resource->lock);
482             return dcfg.vout;
483         }
484     }
485
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);
489         return NULL;
490     }
491
492     vout_rsc->started = true;
493     if (has_started != NULL)
494         *has_started = true;
495
496     DisplayVoutTitle(p_resource, cfg->vout);
497
498     /* Send original viewpoint to the input in order to update other ESes */
499     if (p_resource->p_input != NULL)
500     {
501         input_control_param_t param = { .viewpoint = cfg->fmt->pose };
502         input_ControlPush(p_resource->p_input, INPUT_CONTROL_SET_INITIAL_VIEWPOINT,
503                           &param);
504     }
505     vlc_mutex_unlock( &p_resource->lock );
506
507     return dcfg.vout;
508 }
509
510 vout_thread_t *input_resource_HoldVout( input_resource_t *p_resource )
511 {
512     vlc_mutex_lock( &p_resource->lock_hold );
513
514     struct vout_resource *vout_rsc = resource_GetFirstVoutRsc( p_resource );
515     vout_thread_t *p_vout = vout_rsc != NULL ? vout_rsc->vout : NULL;
516     if( p_vout )
517         vout_Hold(p_vout);
518
519     vlc_mutex_unlock( &p_resource->lock_hold );
520
521     return p_vout;
522 }
523
524 vout_thread_t *input_resource_HoldDummyVout( input_resource_t *p_resource )
525 {
526     return vout_Hold(p_resource->p_vout_dummy);
527 }
528
529 void input_resource_HoldVouts( input_resource_t *p_resource, vout_thread_t ***ppp_vout,
530                                size_t *pi_vout )
531 {
532     vout_thread_t **pp_vout;
533
534     *pi_vout = 0;
535     *ppp_vout = NULL;
536
537     vlc_mutex_lock( &p_resource->lock_hold );
538
539     if( vlc_list_is_empty( &p_resource->vout_rscs ) )
540         goto exit;
541
542     size_t count = 0;
543     struct vout_resource *vout_rsc;
544     vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
545         count ++;
546
547     pp_vout = vlc_alloc( count, sizeof(*pp_vout) );
548     if( !pp_vout )
549         goto exit;
550
551     *ppp_vout = pp_vout;
552     *pi_vout = count;;
553
554     count = 0;
555     vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
556         pp_vout[count++] = vout_Hold( vout_rsc->vout );
557
558 exit:
559     vlc_mutex_unlock( &p_resource->lock_hold );
560 }
561
562 void input_resource_StopFreeVout(input_resource_t *p_resource)
563 {
564     vlc_mutex_lock(&p_resource->lock);
565     if (p_resource->vout_rsc_free != NULL)
566     {
567         msg_Dbg(p_resource->vout_rsc_free->vout, "stop free vout");
568         vout_Stop(p_resource->vout_rsc_free->vout);
569     }
570     vlc_mutex_unlock(&p_resource->lock);
571 }
572
573 /* */
574 sout_instance_t *input_resource_RequestSout( input_resource_t *p_resource, const char *psz_sout )
575 {
576     sout_instance_t *sout;
577
578     assert(psz_sout != NULL);
579     vlc_mutex_lock( &p_resource->lock );
580 #ifdef ENABLE_SOUT
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)
584     {
585         msg_Dbg(p_resource->p_parent, "destroying unusable sout");
586         DestroySout(p_resource);
587     }
588
589     sout = p_resource->p_sout;
590
591     if (sout != NULL)
592     {
593         /* Reuse it */
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;
597     }
598     else
599     {
600         /* Create a new one */
601         sout = sout_NewInstance(p_resource->p_parent, psz_sout);
602     }
603 #else
604     sout = NULL;
605 #endif
606     vlc_mutex_unlock( &p_resource->lock );
607     return sout;
608 }
609
610 void input_resource_PutSout(input_resource_t *resource, sout_instance_t *sout)
611 {
612     if (sout == NULL)
613     {
614         input_resource_TerminateSout(resource);
615         return;
616     }
617
618     vlc_mutex_lock(&resource->lock);
619     assert(resource->p_sout == NULL);
620     resource->p_sout = sout;
621     vlc_mutex_unlock(&resource->lock);
622 }
623
624 void input_resource_TerminateSout( input_resource_t *p_resource )
625 {
626     vlc_mutex_lock( &p_resource->lock );
627     DestroySout(p_resource);
628     vlc_mutex_unlock( &p_resource->lock );
629 }