* src/video_output/vout_subpictures.c: fixed a few race conditions that
[vlc.git] / src / video_output / vout_subpictures.c
1 /*****************************************************************************
2  * vout_subpictures.c : subpicture management functions
3  *****************************************************************************
4  * Copyright (C) 2000 VideoLAN
5  * $Id: vout_subpictures.c,v 1.19 2003/01/30 12:38:13 gbazin Exp $
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                                /* free() */
29 #include <stdio.h>                                              /* sprintf() */
30 #include <string.h>                                            /* strerror() */
31
32 #include <vlc/vlc.h>
33
34 #include "video.h"
35 #include "video_output.h"
36
37 /*****************************************************************************
38  * vout_DisplaySubPicture: display a subpicture unit
39  *****************************************************************************
40  * Remove the reservation flag of a subpicture, which will cause it to be
41  * ready for display.
42  *****************************************************************************/
43 void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
44 {
45     int         i_margin;
46
47     /* Check if status is valid */
48     if( p_subpic->i_status != RESERVED_SUBPICTURE )
49     {
50         msg_Err( p_vout, "subpicture %p has invalid status #%d",
51                          p_subpic, p_subpic->i_status );
52     }
53
54     /* If the user requested an SPU margin, we force the position after
55      * having checked that it was a valid value. */
56     i_margin = config_GetInt( p_vout, "spumargin" );
57
58     if( i_margin >= 0 )
59     {
60         if( p_subpic->i_height + (unsigned int)i_margin
61                                                  <= p_vout->output.i_height )
62         {
63             p_subpic->i_y = p_vout->output.i_height
64                              - i_margin - p_subpic->i_height;
65         }
66     }
67
68     /* Remove reservation flag */
69     p_subpic->i_status = READY_SUBPICTURE;
70 }
71
72 /*****************************************************************************
73  * vout_CreateSubPicture: allocate a subpicture in the video output heap.
74  *****************************************************************************
75  * This function create a reserved subpicture in the video output heap.
76  * A null pointer is returned if the function fails. This method provides an
77  * already allocated zone of memory in the spu data fields. It needs locking
78  * since several pictures can be created by several producers threads.
79  *****************************************************************************/
80 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type )
81 {
82     int                 i_subpic;                        /* subpicture index */
83     subpicture_t *      p_subpic = NULL;            /* first free subpicture */
84
85     /* Get lock */
86     vlc_mutex_lock( &p_vout->subpicture_lock );
87
88     /*
89      * Look for an empty place
90      */
91     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
92     {
93         if( p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
94         {
95             /* Subpicture is empty and ready for allocation */
96             p_subpic = &p_vout->p_subpicture[i_subpic];
97             p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
98             break;
99         }
100     }
101
102     /* If no free subpicture could be found */
103     if( p_subpic == NULL )
104     {
105         msg_Err( p_vout, "subpicture heap is full" );
106         vlc_mutex_unlock( &p_vout->subpicture_lock );
107         return NULL;
108     }
109
110     /* Copy subpicture information, set some default values */
111     p_subpic->i_type    = i_type;
112     p_subpic->i_status  = RESERVED_SUBPICTURE;
113
114     p_subpic->i_start   = 0;
115     p_subpic->i_stop    = 0;
116     p_subpic->b_ephemer = VLC_FALSE;
117
118     p_subpic->i_x       = 0;
119     p_subpic->i_y       = 0;
120     p_subpic->i_width   = 0;
121     p_subpic->i_height  = 0;
122
123     vlc_mutex_unlock( &p_vout->subpicture_lock );
124
125     return p_subpic;
126 }
127
128 /*****************************************************************************
129  * vout_DestroySubPicture: remove a subpicture from the heap
130  *****************************************************************************
131  * This function frees a previously reserved subpicture.
132  * It is meant to be used when the construction of a picture aborted.
133  * This function does not need locking since reserved subpictures are ignored
134  * by the output thread.
135  *****************************************************************************/
136 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
137 {
138     /* Get lock */
139     vlc_mutex_lock( &p_vout->subpicture_lock );
140
141     /* There can be race conditions so we need to check the status */
142     if( p_subpic->i_status == FREE_SUBPICTURE )
143     {
144         vlc_mutex_unlock( &p_vout->subpicture_lock );
145         return;
146     }
147
148     /* Check if status is valid */
149     if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
150            && ( p_subpic->i_status != READY_SUBPICTURE ) )
151     {
152         msg_Err( p_vout, "subpicture %p has invalid status %d",
153                          p_subpic, p_subpic->i_status );
154     }
155
156     if( p_subpic->pf_destroy )
157     {
158         p_subpic->pf_destroy( p_subpic );
159     }
160
161     p_subpic->i_status = FREE_SUBPICTURE;
162
163     vlc_mutex_unlock( &p_vout->subpicture_lock );
164 }
165
166 /*****************************************************************************
167  * vout_RenderSubPictures: render a subpicture list
168  *****************************************************************************
169  * This function renders all sub picture units in the list.
170  *****************************************************************************/
171 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic,
172                              subpicture_t *p_subpic )
173 {
174     /* Get lock */
175     vlc_mutex_lock( &p_vout->subpicture_lock );
176
177     /* Check i_status again to make sure spudec hasn't destroyed the subpic */
178     while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
179     {
180         p_subpic->pf_render( p_vout, p_pic, p_subpic );
181         p_subpic = p_subpic->p_next;
182     }
183
184     vlc_mutex_unlock( &p_vout->subpicture_lock );
185 }
186
187 /*****************************************************************************
188  * vout_SortSubPictures: find the subpictures to display
189  *****************************************************************************
190  * This function parses all subpictures and decides which ones need to be
191  * displayed. This operation does not need lock, since only READY_SUBPICTURE
192  * are handled. If no picture has been selected, display_date will depend on
193  * the subpicture.
194  * We also check for ephemer DVD subpictures (subpictures that have
195  * to be removed if a newer one is available), which makes it a lot
196  * more difficult to guess if a subpicture has to be rendered or not.
197  *****************************************************************************/
198 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
199                                     mtime_t display_date )
200 {
201     int i_index;
202     subpicture_t *p_subpic     = NULL;
203     subpicture_t *p_ephemer    = NULL;
204     mtime_t       ephemer_date = 0;
205
206     /* We get an easily parsable chained list of subpictures which
207      * ends with NULL since p_subpic was initialized to NULL. */
208     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
209     {
210         if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
211         {
212             /* If it is a DVD subpicture, check its date */
213             if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
214             {
215                 if( !p_vout->p_subpicture[i_index].b_ephemer
216                      && display_date > p_vout->p_subpicture[i_index].i_stop )
217                 {
218                     /* Too late, destroy the subpic */
219                     vout_DestroySubPicture( p_vout,
220                                     &p_vout->p_subpicture[i_index] );
221                     continue;
222                 }
223
224                 if( display_date
225                      && display_date < p_vout->p_subpicture[i_index].i_start )
226                 {
227                     /* Too early, come back next monday */
228                     continue;
229                 }
230
231                 /* If this is an ephemer subpic, see if it's the
232                  * youngest we have */
233                 if( p_vout->p_subpicture[i_index].b_ephemer )
234                 {
235                     if( p_ephemer == NULL )
236                     {
237                         p_ephemer = &p_vout->p_subpicture[i_index];
238                         continue;
239                     }
240
241                     if( p_vout->p_subpicture[i_index].i_start
242                                                      < p_ephemer->i_start )
243                     {
244                         /* Link the previous ephemer subpicture and
245                          * replace it with the current one */
246                         p_ephemer->p_next = p_subpic;
247                         p_subpic = p_ephemer;
248                         p_ephemer = &p_vout->p_subpicture[i_index];
249
250                         /* If it's the 2nd youngest subpicture,
251                          * register its date */
252                         if( !ephemer_date
253                               || ephemer_date > p_subpic->i_start )
254                         {
255                             ephemer_date = p_subpic->i_start;
256                         }
257
258                         continue;
259                     }
260                 }
261
262                 p_vout->p_subpicture[i_index].p_next = p_subpic;
263                 p_subpic = &p_vout->p_subpicture[i_index];
264
265                 /* If it's the 2nd youngest subpicture, register its date */                    if( !ephemer_date || ephemer_date > p_subpic->i_start )
266                 {
267                     ephemer_date = p_subpic->i_start;
268                 }
269             }
270             /* If it's not a DVD subpicture, just register it */
271             else
272             {
273                 p_vout->p_subpicture[i_index].p_next = p_subpic;
274                 p_subpic = &p_vout->p_subpicture[i_index];
275             }
276         }
277     }
278
279     /* If we found an ephemer subpicture, check if it has to be
280      * displayed */
281     if( p_ephemer != NULL )
282     {
283         if( p_ephemer->i_start < ephemer_date )
284         {
285             /* Ephemer subpicture has lived too long */
286             vout_DestroySubPicture( p_vout, p_ephemer );
287         }
288         else
289         {
290             /* Ephemer subpicture can still live a bit */
291             p_ephemer->p_next = p_subpic;
292             return p_ephemer;
293         }
294     }
295
296     return p_subpic;
297 }
298