1f8a5d5e0eec83580d7ea0c7583d66aba9a6d8a8
[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 static sout_instance_t *RequestSout( input_resource_t *p_resource,
145                                      sout_instance_t *p_sout, const char *psz_sout )
146 {
147 #ifdef ENABLE_SOUT
148     assert( !p_sout );
149
150     /* Check the validity of the sout */
151     if( p_resource->p_sout &&
152         strcmp( p_resource->p_sout->psz_sout, psz_sout ) )
153     {
154         msg_Dbg( p_resource->p_parent, "destroying unusable sout" );
155         DestroySout( p_resource );
156     }
157
158         if( p_resource->p_sout )
159         {
160             /* Reuse it */
161             msg_Dbg( p_resource->p_parent, "reusing sout" );
162             msg_Dbg( p_resource->p_parent, "you probably want to use gather stream_out" );
163         }
164         else
165         {
166             /* Create a new one */
167             p_resource->p_sout = sout_NewInstance( p_resource->p_parent, psz_sout );
168         }
169
170         p_sout = p_resource->p_sout;
171         p_resource->p_sout = NULL;
172
173         return p_sout;
174 #else
175     VLC_UNUSED (p_resource); VLC_UNUSED (p_sout); VLC_UNUSED (psz_sout);
176     return NULL;
177 #endif
178 }
179
180 /* */
181 static void DestroyVout( input_resource_t *p_resource )
182 {
183     assert( vlc_list_is_empty( &p_resource->vout_rscs )
184          || p_resource->vout_rsc_free == resource_GetFirstVoutRsc( p_resource ) );
185
186     if( p_resource->vout_rsc_free )
187     {
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);
191
192         vout_Close( vout );
193         p_resource->vout_rsc_free = NULL;
194     }
195 }
196
197 static void DisplayVoutTitle( input_resource_t *p_resource,
198                               vout_thread_t *p_vout )
199 {
200     if( p_resource->p_input == NULL )
201         return;
202
203     /* TODO display the title only one time for the same input ? */
204
205     input_item_t *p_item = input_GetItem( p_resource->p_input );
206
207     char *psz_nowplaying = input_item_GetNowPlayingFb( p_item );
208     if( psz_nowplaying && *psz_nowplaying )
209     {
210         vout_DisplayTitle( p_vout, psz_nowplaying );
211     }
212     else
213     {
214         char *psz_artist = input_item_GetArtist( p_item );
215         char *psz_name = input_item_GetTitle( p_item );
216
217         if( !psz_name || *psz_name == '\0' )
218         {
219             free( psz_name );
220             psz_name = input_item_GetName( p_item );
221         }
222         if( psz_artist && *psz_artist )
223         {
224             char *psz_string;
225             if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
226             {
227                 vout_DisplayTitle( p_vout, psz_string );
228                 free( psz_string );
229             }
230         }
231         else if( psz_name )
232         {
233             vout_DisplayTitle( p_vout, psz_name );
234         }
235         free( psz_name );
236         free( psz_artist );
237     }
238     free( psz_nowplaying );
239 }
240
241 /* Audio output */
242 audio_output_t *input_resource_GetAout( input_resource_t *p_resource )
243 {
244     audio_output_t *p_aout;
245
246     vlc_mutex_lock( &p_resource->lock_hold );
247     p_aout = p_resource->p_aout;
248
249     if( p_aout == NULL || p_resource->b_aout_busy )
250     {
251         msg_Dbg( p_resource->p_parent, "creating audio output" );
252         vlc_mutex_unlock( &p_resource->lock_hold );
253
254         p_aout = aout_New( p_resource->p_parent );
255         if( p_aout == NULL )
256             return NULL; /* failed */
257
258         vlc_mutex_lock( &p_resource->lock_hold );
259         if( p_resource->p_aout == NULL )
260             p_resource->p_aout = p_aout;
261     }
262     else
263         msg_Dbg( p_resource->p_parent, "reusing audio output" );
264
265     if( p_resource->p_aout == p_aout )
266     {
267         assert( !p_resource->b_aout_busy );
268         p_resource->b_aout_busy = true;
269     }
270     vlc_mutex_unlock( &p_resource->lock_hold );
271     return p_aout;
272 }
273
274 void input_resource_PutAout( input_resource_t *p_resource,
275                              audio_output_t *p_aout )
276 {
277     assert( p_aout != NULL );
278
279     vlc_mutex_lock( &p_resource->lock_hold );
280     if( p_aout == p_resource->p_aout )
281     {
282         assert( p_resource->b_aout_busy );
283         p_resource->b_aout_busy = false;
284         msg_Dbg( p_resource->p_parent, "keeping audio output" );
285         p_aout = NULL;
286     }
287     else
288         msg_Dbg( p_resource->p_parent, "destroying extra audio output" );
289     vlc_mutex_unlock( &p_resource->lock_hold );
290
291     if( p_aout != NULL )
292         aout_Destroy( p_aout );
293 }
294
295 audio_output_t *input_resource_HoldAout( input_resource_t *p_resource )
296 {
297     audio_output_t *p_aout;
298
299     vlc_mutex_lock( &p_resource->lock_hold );
300     p_aout = p_resource->p_aout;
301     if( p_aout != NULL )
302         aout_Hold(p_aout);
303     vlc_mutex_unlock( &p_resource->lock_hold );
304
305     return p_aout;
306 }
307
308 void input_resource_ResetAout( input_resource_t *p_resource )
309 {
310     audio_output_t *p_aout = NULL;
311
312     vlc_mutex_lock( &p_resource->lock_hold );
313     if( !p_resource->b_aout_busy )
314         p_aout = p_resource->p_aout;
315
316     p_resource->p_aout = NULL;
317     p_resource->b_aout_busy = false;
318     vlc_mutex_unlock( &p_resource->lock_hold );
319
320     if( p_aout != NULL )
321         aout_Destroy( p_aout );
322 }
323
324 /* Common */
325 input_resource_t *input_resource_New( vlc_object_t *p_parent )
326 {
327     input_resource_t *p_resource = calloc( 1, sizeof(*p_resource) );
328     if( !p_resource )
329         return NULL;
330
331     p_resource->p_vout_dummy = vout_CreateDummy(p_parent);
332     if( !p_resource->p_vout_dummy )
333     {
334         free( p_resource );
335         return NULL;
336     }
337
338     vlc_list_init( &p_resource->vout_rscs );
339
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 );
344     return p_resource;
345 }
346
347 void input_resource_Release( input_resource_t *p_resource )
348 {
349     if( !vlc_atomic_rc_dec( &p_resource->rc ) )
350         return;
351
352     DestroySout( p_resource );
353     DestroyVout( p_resource );
354     if( p_resource->p_aout != NULL )
355         aout_Destroy( p_resource->p_aout );
356
357     vout_Release( p_resource->p_vout_dummy );
358     free( p_resource );
359 }
360
361 input_resource_t *input_resource_Hold( input_resource_t *p_resource )
362 {
363     vlc_atomic_rc_inc( &p_resource->rc );
364     return p_resource;
365 }
366
367 void input_resource_SetInput( input_resource_t *p_resource, input_thread_t *p_input )
368 {
369     vlc_mutex_lock( &p_resource->lock );
370
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 ) );
374
375     /* */
376     p_resource->p_input = p_input;
377
378     vlc_mutex_unlock( &p_resource->lock );
379 }
380
381 static void input_resource_PutVoutLocked(input_resource_t *p_resource,
382                                          vout_thread_t *vout, bool *has_stopped)
383 {
384     assert(vout != NULL);
385     struct vout_resource *vout_rsc = resource_GetVoutRsc(p_resource, vout);
386     assert(vout_rsc != NULL);
387
388     if (has_stopped != NULL)
389         *has_stopped = vout_rsc->started;
390
391     if (vout_rsc->started)
392     {
393         vout_StopDisplay(vout_rsc->vout);
394         vout_rsc->started = false;
395     }
396
397     if (vout_rsc == resource_GetFirstVoutRsc(p_resource))
398     {
399         assert(p_resource->vout_rsc_free == NULL || p_resource->vout_rsc_free == vout_rsc);
400
401         msg_Dbg(p_resource->p_parent, "saving a free vout");
402         p_resource->vout_rsc_free = vout_rsc;
403     }
404     else
405     {
406         msg_Dbg(p_resource->p_parent, "destroying vout (already one saved or active)");
407
408         vlc_mutex_lock(&p_resource->lock_hold);
409         vout_resource_Remove(vout_rsc);
410         vlc_mutex_unlock(&p_resource->lock_hold);
411
412         vout_Stop(vout);
413         vout_Close(vout);
414     }
415 }
416
417 void input_resource_PutVout(input_resource_t *p_resource,
418                                    vout_thread_t *vout, bool *stopped)
419 {
420     vlc_mutex_lock( &p_resource->lock );
421     input_resource_PutVoutLocked( p_resource, vout, stopped );
422     vlc_mutex_unlock( &p_resource->lock );
423 }
424
425 static struct vout_resource *
426 RequestVoutRsc(input_resource_t *p_resource)
427 {
428     struct vout_resource *vout_rsc = NULL;
429
430     if( p_resource->vout_rsc_free != NULL )
431     {
432         /* The free vout is always the first one */
433         msg_Dbg(p_resource->p_parent, "trying to reuse free vout");
434
435         vout_rsc = p_resource->vout_rsc_free;
436         p_resource->vout_rsc_free = NULL;
437     }
438     else
439     {
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);
446         if (vout == NULL)
447             return NULL;
448
449         vout_rsc = vout_resource_Create(vout);
450         if (vout_rsc == NULL)
451         {
452             vout_Close(vout);
453             return NULL;
454         }
455
456         vout_rsc->order = vlc_list_is_empty( &p_resource->vout_rscs ) ?
457             VLC_VOUT_ORDER_PRIMARY : VLC_VOUT_ORDER_SECONDARY;
458
459         vlc_mutex_lock(&p_resource->lock_hold);
460         vout_resource_Add(vout_rsc, p_resource);
461         vlc_mutex_unlock(&p_resource->lock_hold);
462     }
463
464     return vout_rsc;
465 }
466
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,
471                                           bool *has_started)
472 {
473     vlc_mutex_lock( &p_resource->lock );
474     struct vout_resource *vout_rsc = NULL;
475
476     if (has_started != NULL)
477         *has_started = false;
478
479     vout_configuration_t dcfg = *cfg;
480     if (dcfg.vout == NULL)
481     {
482         vout_rsc = RequestVoutRsc(p_resource);
483         if (vout_rsc == NULL)
484         {
485             vlc_mutex_unlock( &p_resource->lock );
486             return NULL;
487         }
488         dcfg.vout = vout_rsc->vout;
489     }
490     else
491     {
492         vout_rsc = resource_GetVoutRsc(p_resource, dcfg.vout);
493         assert(vout_rsc != NULL);
494
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;
498     }
499
500     if (order != NULL)
501         *order = vout_rsc->order;
502
503     if (dcfg.fmt == NULL)
504     {
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);
508         return dcfg.vout;
509     }
510
511     if (vout_rsc->started)
512     {
513         assert(cfg->vout != NULL);
514         int ret = vout_ChangeSource(dcfg.vout, dcfg.fmt);
515         if (ret == 0)
516         {
517             vlc_mutex_unlock(&p_resource->lock);
518             return dcfg.vout;
519         }
520     }
521
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);
525         return NULL;
526     }
527
528     vout_rsc->started = true;
529     if (has_started != NULL)
530         *has_started = true;
531
532     DisplayVoutTitle(p_resource, cfg->vout);
533
534     /* Send original viewpoint to the input in order to update other ESes */
535     if (p_resource->p_input != NULL)
536     {
537         input_control_param_t param = { .viewpoint = cfg->fmt->pose };
538         input_ControlPush(p_resource->p_input, INPUT_CONTROL_SET_INITIAL_VIEWPOINT,
539                           &param);
540     }
541     vlc_mutex_unlock( &p_resource->lock );
542
543     return dcfg.vout;
544 }
545
546 vout_thread_t *input_resource_HoldVout( input_resource_t *p_resource )
547 {
548     vlc_mutex_lock( &p_resource->lock_hold );
549
550     struct vout_resource *vout_rsc = resource_GetFirstVoutRsc( p_resource );
551     vout_thread_t *p_vout = vout_rsc != NULL ? vout_rsc->vout : NULL;
552     if( p_vout )
553         vout_Hold(p_vout);
554
555     vlc_mutex_unlock( &p_resource->lock_hold );
556
557     return p_vout;
558 }
559
560 vout_thread_t *input_resource_HoldDummyVout( input_resource_t *p_resource )
561 {
562     return vout_Hold(p_resource->p_vout_dummy);
563 }
564
565 void input_resource_HoldVouts( input_resource_t *p_resource, vout_thread_t ***ppp_vout,
566                                size_t *pi_vout )
567 {
568     vout_thread_t **pp_vout;
569
570     *pi_vout = 0;
571     *ppp_vout = NULL;
572
573     vlc_mutex_lock( &p_resource->lock_hold );
574
575     if( vlc_list_is_empty( &p_resource->vout_rscs ) )
576         goto exit;
577
578     size_t count = 0;
579     struct vout_resource *vout_rsc;
580     vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
581         count ++;
582
583     pp_vout = vlc_alloc( count, sizeof(*pp_vout) );
584     if( !pp_vout )
585         goto exit;
586
587     *ppp_vout = pp_vout;
588     *pi_vout = count;;
589
590     count = 0;
591     vlc_list_foreach( vout_rsc, &p_resource->vout_rscs, node )
592         pp_vout[count++] = vout_Hold( vout_rsc->vout );
593
594 exit:
595     vlc_mutex_unlock( &p_resource->lock_hold );
596 }
597
598 void input_resource_StopFreeVout(input_resource_t *p_resource)
599 {
600     vlc_mutex_lock(&p_resource->lock);
601     if (p_resource->vout_rsc_free != NULL)
602     {
603         msg_Dbg(p_resource->vout_rsc_free->vout, "stop free vout");
604         vout_Stop(p_resource->vout_rsc_free->vout);
605     }
606     vlc_mutex_unlock(&p_resource->lock);
607 }
608
609 /* */
610 sout_instance_t *input_resource_RequestSout( input_resource_t *p_resource, const char *psz_sout )
611 {
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 );
616
617     return p_ret;
618 }
619
620 void input_resource_PutSout(input_resource_t *resource, sout_instance_t *sout)
621 {
622     if (sout == NULL)
623     {
624         input_resource_TerminateSout(resource);
625         return;
626     }
627
628     vlc_mutex_lock(&resource->lock);
629     assert(resource->p_sout == NULL);
630     resource->p_sout = sout;
631     vlc_mutex_unlock(&resource->lock);
632 }
633
634 void input_resource_TerminateSout( input_resource_t *p_resource )
635 {
636     vlc_mutex_lock( &p_resource->lock );
637     DestroySout(p_resource);
638     vlc_mutex_unlock( &p_resource->lock );
639 }