Modify input_ressource_RequestVout to allow avoiding recycling.
[vlc.git] / src / input / ressource.c
1 /*****************************************************************************
2  * ressource.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_aout.h>
34 #include <vlc_sout.h>
35 #include "../libvlc.h"
36 #include "../stream_output/stream_output.h"
37 #include "../audio_output/aout_internal.h"
38 #include "../video_output/vout_control.h"
39 #include "input_interface.h"
40 #include "ressource.h"
41
42 struct input_ressource_t
43 {
44     /* This lock is used to serialize request and protect
45      * our variables */
46     vlc_mutex_t    lock;
47
48     /* */
49     input_thread_t *p_input;
50
51     sout_instance_t *p_sout;
52     vout_thread_t   *p_vout_free;
53     aout_instance_t *p_aout;
54
55     /* This lock is used to protect vout ressources access (for hold)
56      * It is a special case because of embed video (possible deadlock
57      * between vout window request and vout holds in some(qt4) interface) */
58     vlc_mutex_t    lock_vout;
59     int             i_vout;
60     vout_thread_t   **pp_vout;
61 };
62
63 /* */
64 static void DestroySout( input_ressource_t *p_ressource )
65 {
66 #ifdef ENABLE_SOUT
67     if( p_ressource->p_sout )
68         sout_DeleteInstance( p_ressource->p_sout );
69 #endif
70     p_ressource->p_sout = NULL;
71 }
72 static sout_instance_t *RequestSout( input_ressource_t *p_ressource,
73                                      sout_instance_t *p_sout, const char *psz_sout )
74 {
75 #ifdef ENABLE_SOUT
76     if( !p_sout && !psz_sout )
77     {
78         if( p_ressource->p_sout )
79             msg_Dbg( p_ressource->p_sout, "destroying useless sout" );
80         DestroySout( p_ressource );
81         return NULL;
82     }
83
84     assert( p_ressource->p_input );
85     assert( !p_sout || ( !p_ressource->p_sout && !psz_sout ) );
86
87     /* Check the validity of the sout */
88     if( p_ressource->p_sout &&
89         strcmp( p_ressource->p_sout->psz_sout, psz_sout ) )
90     {
91         msg_Dbg( p_ressource->p_input, "destroying unusable sout" );
92         DestroySout( p_ressource );
93     }
94
95     if( psz_sout )
96     {
97         if( p_ressource->p_sout )
98         {
99             /* Reuse it */
100             msg_Dbg( p_ressource->p_input, "reusing sout" );
101             msg_Dbg( p_ressource->p_input, "you probably want to use gather stream_out" );
102             vlc_object_attach( p_ressource->p_sout, p_ressource->p_input );
103         }
104         else
105         {
106             /* Create a new one */
107             p_ressource->p_sout = sout_NewInstance( p_ressource->p_input, psz_sout );
108         }
109
110         p_sout = p_ressource->p_sout;
111         p_ressource->p_sout = NULL;
112
113         return p_sout;
114     }
115     else
116     {
117         vlc_object_detach( p_sout );
118         p_ressource->p_sout = p_sout;
119
120         return NULL;
121     }
122 #else
123     return NULL;
124 #endif
125 }
126
127 /* */
128 static void DestroyVout( input_ressource_t *p_ressource )
129 {
130     assert( p_ressource->i_vout == 0 );
131
132     if( p_ressource->p_vout_free )
133         vout_CloseAndRelease( p_ressource->p_vout_free );
134
135     p_ressource->p_vout_free = NULL;
136 }
137 static void DisplayVoutTitle( input_ressource_t *p_ressource,
138                               vout_thread_t *p_vout )
139 {
140     assert( p_ressource->p_input );
141
142     /* TODO display the title only one time for the same input ? */
143
144     input_item_t *p_item = input_GetItem( p_ressource->p_input );
145
146     char *psz_nowplaying = input_item_GetNowPlaying( p_item );
147     if( psz_nowplaying && *psz_nowplaying )
148     {
149         vout_DisplayTitle( p_vout, psz_nowplaying );
150     }
151     else
152     {
153         char *psz_artist = input_item_GetArtist( p_item );
154         char *psz_name = input_item_GetTitle( p_item );
155
156         if( !psz_name || *psz_name == '\0' )
157         {
158             free( psz_name );
159             psz_name = input_item_GetName( p_item );
160         }
161         if( psz_artist && *psz_artist )
162         {
163             char *psz_string;
164             if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 )
165             {
166                 vout_DisplayTitle( p_vout, psz_string );
167                 free( psz_string );
168             }
169         }
170         else if( psz_name )
171         {
172             vout_DisplayTitle( p_vout, psz_name );
173         }
174         free( psz_name );
175         free( psz_artist );
176     }
177     free( psz_nowplaying );
178 }
179 static vout_thread_t *RequestVout( input_ressource_t *p_ressource,
180                                    vout_thread_t *p_vout, video_format_t *p_fmt,
181                                    bool b_recycle )
182 {
183     if( !p_vout && !p_fmt )
184     {
185         if( p_ressource->p_vout_free )
186         {
187             msg_Dbg( p_ressource->p_vout_free, "destroying useless vout" );
188             vout_CloseAndRelease( p_ressource->p_vout_free );
189             p_ressource->p_vout_free = NULL;
190         }
191         return NULL;
192     }
193
194     assert( p_ressource->p_input );
195     if( p_fmt )
196     {
197         /* */
198         if( !p_vout && p_ressource->p_vout_free )
199         {
200             msg_Dbg( p_ressource->p_input, "trying to reuse free vout" );
201             p_vout = p_ressource->p_vout_free;
202
203             p_ressource->p_vout_free = NULL;
204         }
205         else if( p_vout )
206         {
207             assert( p_vout != p_ressource->p_vout_free );
208
209             vlc_mutex_lock( &p_ressource->lock_vout );
210             TAB_REMOVE( p_ressource->i_vout, p_ressource->pp_vout, p_vout );
211             vlc_mutex_unlock( &p_ressource->lock_vout );
212         }
213
214         /* */
215         p_vout = vout_Request( p_ressource->p_input, p_vout, p_fmt );
216         if( !p_vout )
217             return NULL;
218
219         DisplayVoutTitle( p_ressource, p_vout );
220
221         vlc_mutex_lock( &p_ressource->lock_vout );
222         TAB_APPEND( p_ressource->i_vout, p_ressource->pp_vout, p_vout );
223         vlc_mutex_unlock( &p_ressource->lock_vout );
224
225         return p_vout;
226     }
227     else
228     {
229         assert( p_vout );
230
231         vlc_mutex_lock( &p_ressource->lock_vout );
232         TAB_REMOVE( p_ressource->i_vout, p_ressource->pp_vout, p_vout );
233         const int i_vout_active = p_ressource->i_vout;
234         vlc_mutex_unlock( &p_ressource->lock_vout );
235
236         if( p_ressource->p_vout_free || i_vout_active > 0 || !b_recycle )
237         {
238             if( b_recycle )
239                 msg_Dbg( p_ressource->p_input, "detroying vout (already one saved or active)" );
240             vout_CloseAndRelease( p_vout );
241         }
242         else
243         {
244             msg_Dbg( p_ressource->p_input, "saving a free vout" );
245             vout_Flush( p_vout, 1 );
246             p_ressource->p_vout_free = p_vout;
247         }
248         return NULL;
249     }
250 }
251 static vout_thread_t *HoldVout( input_ressource_t *p_ressource )
252 {
253     if( p_ressource->i_vout <= 0 )
254         return NULL;
255
256     /* TODO FIXME: p_ressource->pp_vout order is NOT stable */
257     vlc_mutex_lock( &p_ressource->lock_vout );
258
259     vout_thread_t *p_vout = p_ressource->pp_vout[0];
260
261     vlc_object_hold( p_vout );
262
263     vlc_mutex_unlock( &p_ressource->lock_vout );
264
265     return p_vout;
266 }
267 static void HoldVouts( input_ressource_t *p_ressource, vout_thread_t ***ppp_vout, int *pi_vout )
268 {
269     vout_thread_t **pp_vout;
270
271     *pi_vout = 0;
272     *ppp_vout = NULL;
273
274     vlc_mutex_lock( &p_ressource->lock_vout );
275
276     if( p_ressource->i_vout <= 0 )
277         goto exit;
278
279     pp_vout = calloc( p_ressource->i_vout, sizeof(*pp_vout) );
280     if( !pp_vout )
281         goto exit;
282
283     *ppp_vout = pp_vout;
284     *pi_vout = p_ressource->i_vout;
285
286     for( int i = 0; i < p_ressource->i_vout; i++ )
287     {
288         pp_vout[i] = p_ressource->pp_vout[i];
289         vlc_object_hold( pp_vout[i] );
290     }
291
292 exit:
293     vlc_mutex_unlock( &p_ressource->lock_vout );
294 }
295
296 /* */
297 static void DestroyAout( input_ressource_t *p_ressource )
298 {
299     if( p_ressource->p_aout )
300         vlc_object_release( p_ressource->p_aout );
301     p_ressource->p_aout = NULL;
302 }
303 static aout_instance_t *RequestAout( input_ressource_t *p_ressource, aout_instance_t *p_aout )
304 {
305     assert( p_ressource->p_input );
306
307     if( p_aout )
308     {
309         msg_Dbg( p_ressource->p_input, "releasing aout" );
310         vlc_object_release( p_aout );
311         return NULL;
312     }
313     else
314     {
315         if( !p_ressource->p_aout )
316         {
317             msg_Dbg( p_ressource->p_input, "creating aout" );
318             p_ressource->p_aout = aout_New( p_ressource->p_input );
319         }
320         else
321         {
322             msg_Dbg( p_ressource->p_input, "reusing aout" );
323         }
324
325         if( !p_ressource->p_aout )
326             return NULL;
327
328         vlc_object_detach( p_ressource->p_aout );
329         vlc_object_attach( p_ressource->p_aout, p_ressource->p_input );
330         vlc_object_hold( p_ressource->p_aout );
331         return p_ressource->p_aout;
332     }
333 }
334 static aout_instance_t *HoldAout( input_ressource_t *p_ressource )
335 {
336     if( !p_ressource->p_aout )
337         return NULL;
338
339     aout_instance_t *p_aout = p_ressource->p_aout;
340
341     vlc_object_hold( p_aout );
342
343     return p_aout;
344 }
345
346 /* */
347 input_ressource_t *input_ressource_New( void )
348 {
349     input_ressource_t *p_ressource = calloc( 1, sizeof(*p_ressource) );
350     if( !p_ressource )
351         return NULL;
352
353     vlc_mutex_init( &p_ressource->lock );
354     vlc_mutex_init( &p_ressource->lock_vout );
355     return p_ressource;
356 }
357
358 void input_ressource_Delete( input_ressource_t *p_ressource )
359 {
360     DestroySout( p_ressource );
361     DestroyVout( p_ressource );
362     DestroyAout( p_ressource );
363
364     vlc_mutex_destroy( &p_ressource->lock_vout );
365     vlc_mutex_destroy( &p_ressource->lock );
366     free( p_ressource );
367 }
368
369 void input_ressource_SetInput( input_ressource_t *p_ressource, input_thread_t *p_input )
370 {
371     vlc_mutex_lock( &p_ressource->lock );
372
373     if( p_ressource->p_input && !p_input )
374     {
375         if( p_ressource->p_aout )
376             vlc_object_detach( p_ressource->p_aout );
377
378         assert( p_ressource->i_vout == 0 );
379         if( p_ressource->p_vout_free )
380             vlc_object_detach( p_ressource->p_vout_free );
381
382         if( p_ressource->p_sout )
383             vlc_object_detach( p_ressource->p_sout );
384     }
385
386     /* */
387     p_ressource->p_input = p_input;
388
389     vlc_mutex_unlock( &p_ressource->lock );
390 }
391
392 vout_thread_t *input_ressource_RequestVout( input_ressource_t *p_ressource,
393                                             vout_thread_t *p_vout, video_format_t *p_fmt, bool b_recycle )
394 {
395     vlc_mutex_lock( &p_ressource->lock );
396     vout_thread_t *p_ret = RequestVout( p_ressource, p_vout, p_fmt, b_recycle );
397     vlc_mutex_unlock( &p_ressource->lock );
398
399     return p_ret;
400 }
401 vout_thread_t *input_ressource_HoldVout( input_ressource_t *p_ressource )
402 {
403     return HoldVout( p_ressource );
404 }
405 void input_ressource_HoldVouts( input_ressource_t *p_ressource, vout_thread_t ***ppp_vout, int *pi_vout )
406 {
407     HoldVouts( p_ressource, ppp_vout, pi_vout );
408 }
409 void input_ressource_TerminateVout( input_ressource_t *p_ressource )
410 {
411     input_ressource_RequestVout( p_ressource, NULL, NULL, false );
412 }
413 bool input_ressource_HasVout( input_ressource_t *p_ressource )
414 {
415     vlc_mutex_lock( &p_ressource->lock );
416     assert( !p_ressource->p_input );
417     const bool b_vout = p_ressource->p_vout_free != NULL;
418     vlc_mutex_unlock( &p_ressource->lock );
419
420     return b_vout;
421 }
422
423 /* */
424 aout_instance_t *input_ressource_RequestAout( input_ressource_t *p_ressource, aout_instance_t *p_aout )
425 {
426     vlc_mutex_lock( &p_ressource->lock );
427     aout_instance_t *p_ret = RequestAout( p_ressource, p_aout );
428     vlc_mutex_unlock( &p_ressource->lock );
429
430     return p_ret;
431 }
432 aout_instance_t *input_ressource_HoldAout( input_ressource_t *p_ressource )
433 {
434     vlc_mutex_lock( &p_ressource->lock );
435     aout_instance_t *p_ret = HoldAout( p_ressource );
436     vlc_mutex_unlock( &p_ressource->lock );
437
438     return p_ret;
439 }
440 /* */
441 sout_instance_t *input_ressource_RequestSout( input_ressource_t *p_ressource, sout_instance_t *p_sout, const char *psz_sout )
442 {
443     vlc_mutex_lock( &p_ressource->lock );
444     sout_instance_t *p_ret = RequestSout( p_ressource, p_sout, psz_sout );
445     vlc_mutex_unlock( &p_ressource->lock );
446
447     return p_ret;
448 }
449 void input_ressource_TerminateSout( input_ressource_t *p_ressource )
450 {
451     input_ressource_RequestSout( p_ressource, NULL, NULL );
452 }
453