]> git.sesse.net Git - vlc/blob - src/input/resource.c
Fixed race condition with input ressource and input_Control.
[vlc] / src / input / resource.c
1 /*****************************************************************************
2  * resource.c
3  *****************************************************************************
4  * Copyright (C) 2008 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar < fenrir _AT_ videolan _DOT_ org >
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <vlc_common.h>
32 #include <vlc_vout.h>
33 #include <vlc_osd.h>
34 #include <vlc_aout.h>
35 #include <vlc_sout.h>
36 #include "../libvlc.h"
37 #include "../stream_output/stream_output.h"
38 #include "../audio_output/aout_internal.h"
39 #include "../video_output/vout_control.h"
40 #include "input_interface.h"
41 #include "resource.h"
42
43 struct input_resource_t
44 {
45     /* This lock is used to serialize request and protect
46      * our variables */
47     vlc_mutex_t    lock;
48
49     /* */
50     input_thread_t *p_input;
51
52     sout_instance_t *p_sout;
53     vout_thread_t   *p_vout_free;
54     aout_instance_t *p_aout;
55
56     /* This lock is used to protect vout resources access (for hold)
57      * It is a special case because of embed video (possible deadlock
58      * between vout window request and vout holds in some(qt4) interface) */
59     vlc_mutex_t    lock_vout;
60     int             i_vout;
61     vout_thread_t   **pp_vout;
62 };
63
64 /* */
65 static void DestroySout( input_resource_t *p_resource )
66 {
67 #ifdef ENABLE_SOUT
68     if( p_resource->p_sout )
69         sout_DeleteInstance( p_resource->p_sout );
70 #endif
71     p_resource->p_sout = NULL;
72 }
73
74 static sout_instance_t *DetachSout( input_resource_t *p_resource )
75 {
76     sout_instance_t *p_sout = p_resource->p_sout;
77     p_resource->p_sout = NULL;
78
79     return p_sout;
80 }
81
82 static sout_instance_t *RequestSout( input_resource_t *p_resource,
83                                      sout_instance_t *p_sout, const char *psz_sout )
84 {
85 #ifdef ENABLE_SOUT
86     if( !p_sout && !psz_sout )
87     {
88         if( p_resource->p_sout )
89             msg_Dbg( p_resource->p_sout, "destroying useless sout" );
90         DestroySout( p_resource );
91         return NULL;
92     }
93
94     assert( p_resource->p_input );
95     assert( !p_sout || ( !p_resource->p_sout && !psz_sout ) );
96
97     /* Check the validity of the sout */
98     if( p_resource->p_sout &&
99         strcmp( p_resource->p_sout->psz_sout, psz_sout ) )
100     {
101         msg_Dbg( p_resource->p_input, "destroying unusable sout" );
102         DestroySout( p_resource );
103     }
104
105     if( psz_sout )
106     {
107         if( p_resource->p_sout )
108         {
109             /* Reuse it */
110             msg_Dbg( p_resource->p_input, "reusing sout" );
111             msg_Dbg( p_resource->p_input, "you probably want to use gather stream_out" );
112             vlc_object_attach( p_resource->p_sout, p_resource->p_input );
113         }
114         else
115         {
116             /* Create a new one */
117             p_resource->p_sout = sout_NewInstance( p_resource->p_input, psz_sout );
118         }
119
120         p_sout = p_resource->p_sout;
121         p_resource->p_sout = NULL;
122
123         return p_sout;
124     }
125     else
126     {
127         vlc_object_detach( p_sout );
128         p_resource->p_sout = p_sout;
129
130         return NULL;
131     }
132 #else
133     return NULL;
134 #endif
135 }
136
137 /* */
138 static void DestroyVout( input_resource_t *p_resource )
139 {
140     assert( p_resource->i_vout == 0 );
141
142     if( p_resource->p_vout_free )
143         vout_CloseAndRelease( p_resource->p_vout_free );
144
145     p_resource->p_vout_free = NULL;
146 }
147 static vout_thread_t *DetachVout( input_resource_t *p_resource )
148 {
149     assert( p_resource->i_vout == 0 );
150     vout_thread_t *p_vout = p_resource->p_vout_free;
151     p_resource->p_vout_free = NULL;
152
153     return p_vout;
154 }
155
156 static void DisplayVoutTitle( input_resource_t *p_resource,
157                               vout_thread_t *p_vout )
158 {
159     assert( p_resource->p_input );
160
161     /* TODO display the title only one time for the same input ? */
162
163     input_item_t *p_item = input_GetItem( p_resource->p_input );
164
165     char *psz_nowplaying = input_item_GetNowPlaying( p_item );
166     if( psz_nowplaying && *psz_nowplaying )
167     {
168         vout_DisplayTitle( p_vout, psz_nowplaying );
169     }
170     else
171     {
172         char *psz_artist = input_item_GetArtist( p_item );
173         char *psz_name = input_item_GetTitle( p_item );
174
175         if( !psz_name || *psz_name == '\0' )
176         {
177             free( psz_name );
178             psz_name = input_item_GetName( p_item );
179         }
180         if( psz_artist && *psz_artist )
181         {
182             char *psz_string;
183             if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
184             {
185                 vout_DisplayTitle( p_vout, psz_string );
186                 free( psz_string );
187             }
188         }
189         else if( psz_name )
190         {
191             vout_DisplayTitle( p_vout, psz_name );
192         }
193         free( psz_name );
194         free( psz_artist );
195     }
196     free( psz_nowplaying );
197 }
198 static vout_thread_t *RequestVout( input_resource_t *p_resource,
199                                    vout_thread_t *p_vout, video_format_t *p_fmt,
200                                    bool b_recycle )
201 {
202     if( !p_vout && !p_fmt )
203     {
204         if( p_resource->p_vout_free )
205         {
206             msg_Dbg( p_resource->p_vout_free, "destroying useless vout" );
207             vout_CloseAndRelease( p_resource->p_vout_free );
208             p_resource->p_vout_free = NULL;
209         }
210         return NULL;
211     }
212
213     assert( p_resource->p_input );
214     if( p_fmt )
215     {
216         /* */
217         if( !p_vout && p_resource->p_vout_free )
218         {
219             msg_Dbg( p_resource->p_input, "trying to reuse free vout" );
220             p_vout = p_resource->p_vout_free;
221
222             p_resource->p_vout_free = NULL;
223         }
224         else if( p_vout )
225         {
226             assert( p_vout != p_resource->p_vout_free );
227
228             vlc_mutex_lock( &p_resource->lock_vout );
229             TAB_REMOVE( p_resource->i_vout, p_resource->pp_vout, p_vout );
230             vlc_mutex_unlock( &p_resource->lock_vout );
231         }
232
233         /* */
234         p_vout = vout_Request( p_resource->p_input, p_vout, p_fmt );
235         if( !p_vout )
236             return NULL;
237
238         DisplayVoutTitle( p_resource, p_vout );
239
240         vlc_mutex_lock( &p_resource->lock_vout );
241         TAB_APPEND( p_resource->i_vout, p_resource->pp_vout, p_vout );
242         vlc_mutex_unlock( &p_resource->lock_vout );
243
244         return p_vout;
245     }
246     else
247     {
248         assert( p_vout );
249
250         vlc_mutex_lock( &p_resource->lock_vout );
251         TAB_REMOVE( p_resource->i_vout, p_resource->pp_vout, p_vout );
252         const int i_vout_active = p_resource->i_vout;
253         vlc_mutex_unlock( &p_resource->lock_vout );
254
255         if( p_resource->p_vout_free || i_vout_active > 0 || !b_recycle )
256         {
257             if( b_recycle )
258                 msg_Dbg( p_resource->p_input, "detroying vout (already one saved or active)" );
259             vout_CloseAndRelease( p_vout );
260         }
261         else
262         {
263             msg_Dbg( p_resource->p_input, "saving a free vout" );
264             vout_Flush( p_vout, 1 );
265             spu_Control( p_vout->p_spu, SPU_CHANNEL_CLEAR, -1 );
266
267             p_resource->p_vout_free = p_vout;
268         }
269         return NULL;
270     }
271 }
272 static vout_thread_t *HoldVout( input_resource_t *p_resource )
273 {
274     if( p_resource->i_vout <= 0 )
275         return NULL;
276
277     /* TODO FIXME: p_resource->pp_vout order is NOT stable */
278     vlc_mutex_lock( &p_resource->lock_vout );
279
280     vout_thread_t *p_vout = p_resource->pp_vout[0];
281
282     vlc_object_hold( p_vout );
283
284     vlc_mutex_unlock( &p_resource->lock_vout );
285
286     return p_vout;
287 }
288 static void HoldVouts( input_resource_t *p_resource, vout_thread_t ***ppp_vout, int *pi_vout )
289 {
290     vout_thread_t **pp_vout;
291
292     *pi_vout = 0;
293     *ppp_vout = NULL;
294
295     vlc_mutex_lock( &p_resource->lock_vout );
296
297     if( p_resource->i_vout <= 0 )
298         goto exit;
299
300     pp_vout = calloc( p_resource->i_vout, sizeof(*pp_vout) );
301     if( !pp_vout )
302         goto exit;
303
304     *ppp_vout = pp_vout;
305     *pi_vout = p_resource->i_vout;
306
307     for( int i = 0; i < p_resource->i_vout; i++ )
308     {
309         pp_vout[i] = p_resource->pp_vout[i];
310         vlc_object_hold( pp_vout[i] );
311     }
312
313 exit:
314     vlc_mutex_unlock( &p_resource->lock_vout );
315 }
316
317 /* */
318 static void DestroyAout( input_resource_t *p_resource )
319 {
320     if( p_resource->p_aout )
321         vlc_object_release( p_resource->p_aout );
322     p_resource->p_aout = NULL;
323 }
324 static aout_instance_t *DetachAout( input_resource_t *p_resource )
325 {
326     aout_instance_t *p_aout = p_resource->p_aout;
327     p_resource->p_aout = NULL;
328
329     return p_aout;
330 }
331
332 static aout_instance_t *RequestAout( input_resource_t *p_resource, aout_instance_t *p_aout )
333 {
334     assert( p_resource->p_input );
335
336     if( p_aout )
337     {
338         msg_Dbg( p_resource->p_input, "releasing aout" );
339         vlc_object_release( p_aout );
340         return NULL;
341     }
342     else
343     {
344         if( !p_resource->p_aout )
345         {
346             msg_Dbg( p_resource->p_input, "creating aout" );
347             p_resource->p_aout = aout_New( p_resource->p_input );
348         }
349         else
350         {
351             msg_Dbg( p_resource->p_input, "reusing aout" );
352         }
353
354         if( !p_resource->p_aout )
355             return NULL;
356
357         vlc_object_detach( p_resource->p_aout );
358         vlc_object_attach( p_resource->p_aout, p_resource->p_input );
359         vlc_object_hold( p_resource->p_aout );
360         return p_resource->p_aout;
361     }
362 }
363 static aout_instance_t *HoldAout( input_resource_t *p_resource )
364 {
365     if( !p_resource->p_aout )
366         return NULL;
367
368     aout_instance_t *p_aout = p_resource->p_aout;
369
370     vlc_object_hold( p_aout );
371
372     return p_aout;
373 }
374
375 /* */
376 input_resource_t *input_resource_New( void )
377 {
378     input_resource_t *p_resource = calloc( 1, sizeof(*p_resource) );
379     if( !p_resource )
380         return NULL;
381
382     vlc_mutex_init( &p_resource->lock );
383     vlc_mutex_init( &p_resource->lock_vout );
384     return p_resource;
385 }
386
387 void input_resource_Delete( input_resource_t *p_resource )
388 {
389     DestroySout( p_resource );
390     DestroyVout( p_resource );
391     DestroyAout( p_resource );
392
393     vlc_mutex_destroy( &p_resource->lock_vout );
394     vlc_mutex_destroy( &p_resource->lock );
395     free( p_resource );
396 }
397
398 void input_resource_SetInput( input_resource_t *p_resource, input_thread_t *p_input )
399 {
400     vlc_mutex_lock( &p_resource->lock );
401
402     if( p_resource->p_input && !p_input )
403     {
404         if( p_resource->p_aout )
405             vlc_object_detach( p_resource->p_aout );
406
407         assert( p_resource->i_vout == 0 );
408         if( p_resource->p_vout_free )
409             vlc_object_detach( p_resource->p_vout_free );
410
411         if( p_resource->p_sout )
412             vlc_object_detach( p_resource->p_sout );
413     }
414
415     /* */
416     p_resource->p_input = p_input;
417
418     vlc_mutex_unlock( &p_resource->lock );
419 }
420
421 input_resource_t *input_resource_Detach( input_resource_t *p_resource )
422 {
423     input_resource_t *p_ret = input_resource_New();
424     if( !p_ret )
425         return NULL;
426
427     vlc_mutex_lock( &p_resource->lock );
428     assert( !p_resource->p_input );
429     p_ret->p_sout = DetachSout( p_resource );
430     p_ret->p_vout_free = DetachVout( p_resource );
431     p_ret->p_aout = DetachAout( p_resource );
432     vlc_mutex_unlock( &p_resource->lock );
433
434     return p_ret;
435 }
436
437 vout_thread_t *input_resource_RequestVout( input_resource_t *p_resource,
438                                             vout_thread_t *p_vout, video_format_t *p_fmt, bool b_recycle )
439 {
440     vlc_mutex_lock( &p_resource->lock );
441     vout_thread_t *p_ret = RequestVout( p_resource, p_vout, p_fmt, b_recycle );
442     vlc_mutex_unlock( &p_resource->lock );
443
444     return p_ret;
445 }
446 vout_thread_t *input_resource_HoldVout( input_resource_t *p_resource )
447 {
448     return HoldVout( p_resource );
449 }
450 void input_resource_HoldVouts( input_resource_t *p_resource, vout_thread_t ***ppp_vout, int *pi_vout )
451 {
452     HoldVouts( p_resource, ppp_vout, pi_vout );
453 }
454 void input_resource_TerminateVout( input_resource_t *p_resource )
455 {
456     input_resource_RequestVout( p_resource, NULL, NULL, false );
457 }
458 bool input_resource_HasVout( input_resource_t *p_resource )
459 {
460     vlc_mutex_lock( &p_resource->lock );
461     assert( !p_resource->p_input );
462     const bool b_vout = p_resource->p_vout_free != NULL;
463     vlc_mutex_unlock( &p_resource->lock );
464
465     return b_vout;
466 }
467
468 /* */
469 aout_instance_t *input_resource_RequestAout( input_resource_t *p_resource, aout_instance_t *p_aout )
470 {
471     vlc_mutex_lock( &p_resource->lock );
472     aout_instance_t *p_ret = RequestAout( p_resource, p_aout );
473     vlc_mutex_unlock( &p_resource->lock );
474
475     return p_ret;
476 }
477 aout_instance_t *input_resource_HoldAout( input_resource_t *p_resource )
478 {
479     vlc_mutex_lock( &p_resource->lock );
480     aout_instance_t *p_ret = HoldAout( p_resource );
481     vlc_mutex_unlock( &p_resource->lock );
482
483     return p_ret;
484 }
485 /* */
486 sout_instance_t *input_resource_RequestSout( input_resource_t *p_resource, sout_instance_t *p_sout, const char *psz_sout )
487 {
488     vlc_mutex_lock( &p_resource->lock );
489     sout_instance_t *p_ret = RequestSout( p_resource, p_sout, psz_sout );
490     vlc_mutex_unlock( &p_resource->lock );
491
492     return p_ret;
493 }
494 void input_resource_TerminateSout( input_resource_t *p_resource )
495 {
496     input_resource_RequestSout( p_resource, NULL, NULL );
497 }
498