]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
improves OSD sytem
[vlc] / 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.22 2003/12/08 17:48:13 yoann 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 "vlc_video.h"
35 #include "video_output.h"
36
37 /**
38  * Display a subpicture unit
39  *
40  * Remove the reservation flag of a subpicture, which will cause it to be
41  * ready for display.
42  * \param p_vout the video output this subpicture should be displayed on
43  * \param p_subpic the subpicture to display
44  */
45 void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
46 {
47     int         i_margin;
48
49     /* Check if status is valid */
50     if( p_subpic->i_status != RESERVED_SUBPICTURE )
51     {
52         msg_Err( p_vout, "subpicture %p has invalid status #%d",
53                          p_subpic, p_subpic->i_status );
54     }
55
56     /* If the user requested an SPU margin, we force the position after
57      * having checked that it was a valid value. */
58     i_margin = config_GetInt( p_vout, "spumargin" );
59
60     if( i_margin >= 0 )
61     {
62         if( p_subpic->i_height + (unsigned int)i_margin
63                                                  <= p_vout->output.i_height )
64         {
65             p_subpic->i_y = p_vout->output.i_height
66                              - i_margin - p_subpic->i_height;
67         }
68     }
69
70     /* Remove reservation flag */
71     p_subpic->i_status = READY_SUBPICTURE;
72 }
73
74 /**
75  * Allocate a subpicture in the video output heap.
76  *
77  * This function create a reserved subpicture in the video output heap.
78  * A null pointer is returned if the function fails. This method provides an
79  * already allocated zone of memory in the spu data fields. It needs locking
80  * since several pictures can be created by several producers threads.
81  * \param p_vout the vout in which to create the subpicture
82  * \param i_type the type of the subpicture
83  * \return NULL on error, a reserved subpicture otherwise
84  */
85 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type )
86 {
87     int                 i_subpic;                        /* subpicture index */
88     subpicture_t *      p_subpic = NULL;            /* first free subpicture */
89
90     /* Get lock */
91     vlc_mutex_lock( &p_vout->subpicture_lock );
92
93     /*
94      * Look for an empty place
95      */
96     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
97     {
98         if( p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
99         {
100             /* Subpicture is empty and ready for allocation */
101             p_subpic = &p_vout->p_subpicture[i_subpic];
102             p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
103             break;
104         }
105     }
106
107     /* If no free subpicture could be found */
108     if( p_subpic == NULL )
109     {
110         msg_Err( p_vout, "subpicture heap is full" );
111         vlc_mutex_unlock( &p_vout->subpicture_lock );
112         return NULL;
113     }
114
115     /* Copy subpicture information, set some default values */
116     p_subpic->i_type    = i_type;
117     p_subpic->i_status  = RESERVED_SUBPICTURE;
118
119     p_subpic->i_start   = 0;
120     p_subpic->i_stop    = 0;
121     p_subpic->b_ephemer = VLC_FALSE;
122
123     p_subpic->i_x       = 0;
124     p_subpic->i_y       = 0;
125     p_subpic->i_width   = 0;
126     p_subpic->i_height  = 0;
127
128     vlc_mutex_unlock( &p_vout->subpicture_lock );
129
130     return p_subpic;
131 }
132
133 /**
134  * Remove a subpicture from the heap
135  *
136  * This function frees a previously reserved subpicture.
137  * It is meant to be used when the construction of a picture aborted.
138  * This function does not need locking since reserved subpictures are ignored
139  * by the output thread.
140  */
141 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
142 {
143     /* Get lock */
144     vlc_mutex_lock( &p_vout->subpicture_lock );
145
146     /* There can be race conditions so we need to check the status */
147     if( p_subpic->i_status == FREE_SUBPICTURE )
148     {
149         vlc_mutex_unlock( &p_vout->subpicture_lock );
150         return;
151     }
152
153     /* Check if status is valid */
154     if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
155            && ( p_subpic->i_status != READY_SUBPICTURE ) )
156     {
157         msg_Err( p_vout, "subpicture %p has invalid status %d",
158                          p_subpic, p_subpic->i_status );
159     }
160
161     if( p_subpic->pf_destroy )
162     {
163         p_subpic->pf_destroy( p_subpic );
164     }
165
166     if( p_subpic == p_vout->last_osd_message )
167     {
168         p_vout->last_osd_message = NULL;
169     }
170
171     p_subpic->i_status = FREE_SUBPICTURE;
172
173     vlc_mutex_unlock( &p_vout->subpicture_lock );
174 }
175
176 /*****************************************************************************
177  * vout_RenderSubPictures: render a subpicture list
178  *****************************************************************************
179  * This function renders all sub picture units in the list.
180  *****************************************************************************/
181 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic,
182                              subpicture_t *p_subpic )
183 {
184     /* Get lock */
185     vlc_mutex_lock( &p_vout->subpicture_lock );
186
187     /* Check i_status again to make sure spudec hasn't destroyed the subpic */
188     while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
189     {
190         p_subpic->pf_render( p_vout, p_pic, p_subpic );
191         p_subpic = p_subpic->p_next;
192     }
193
194     vlc_mutex_unlock( &p_vout->subpicture_lock );
195 }
196
197 /*****************************************************************************
198  * vout_SortSubPictures: find the subpictures to display
199  *****************************************************************************
200  * This function parses all subpictures and decides which ones need to be
201  * displayed. This operation does not need lock, since only READY_SUBPICTURE
202  * are handled. If no picture has been selected, display_date will depend on
203  * the subpicture.
204  * We also check for ephemer DVD subpictures (subpictures that have
205  * to be removed if a newer one is available), which makes it a lot
206  * more difficult to guess if a subpicture has to be rendered or not.
207  *****************************************************************************/
208 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
209                                     mtime_t display_date )
210 {
211     int i_index;
212     subpicture_t *p_subpic     = NULL;
213     subpicture_t *p_ephemer    = NULL;
214     mtime_t       ephemer_date = 0;
215
216     /* We get an easily parsable chained list of subpictures which
217      * ends with NULL since p_subpic was initialized to NULL. */
218     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
219     {
220         if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
221         {
222             /* If it is a DVD subpicture, check its date */
223             if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
224             {
225                 if( !p_vout->p_subpicture[i_index].b_ephemer
226                      && display_date > p_vout->p_subpicture[i_index].i_stop )
227                 {
228                     /* Too late, destroy the subpic */
229                     vout_DestroySubPicture( p_vout,
230                                     &p_vout->p_subpicture[i_index] );
231                     continue;
232                 }
233
234                 if( display_date
235                      && display_date < p_vout->p_subpicture[i_index].i_start )
236                 {
237                     /* Too early, come back next monday */
238                     continue;
239                 }
240
241                 /* If this is an ephemer subpic, see if it's the
242                  * youngest we have */
243                 if( p_vout->p_subpicture[i_index].b_ephemer )
244                 {
245                     if( p_ephemer == NULL )
246                     {
247                         p_ephemer = &p_vout->p_subpicture[i_index];
248                         continue;
249                     }
250
251                     if( p_vout->p_subpicture[i_index].i_start
252                                                      < p_ephemer->i_start )
253                     {
254                         /* Link the previous ephemer subpicture and
255                          * replace it with the current one */
256                         p_ephemer->p_next = p_subpic;
257                         p_subpic = p_ephemer;
258                         p_ephemer = &p_vout->p_subpicture[i_index];
259
260                         /* If it's the 2nd youngest subpicture,
261                          * register its date */
262                         if( !ephemer_date
263                               || ephemer_date > p_subpic->i_start )
264                         {
265                             ephemer_date = p_subpic->i_start;
266                         }
267
268                         continue;
269                     }
270                 }
271
272                 p_vout->p_subpicture[i_index].p_next = p_subpic;
273                 p_subpic = &p_vout->p_subpicture[i_index];
274
275                 /* If it's the 2nd youngest subpicture, register its date */                    if( !ephemer_date || ephemer_date > p_subpic->i_start )
276                 {
277                     ephemer_date = p_subpic->i_start;
278                 }
279             }
280             /* If it's not a DVD subpicture, just register it */
281             else
282             {
283                 p_vout->p_subpicture[i_index].p_next = p_subpic;
284                 p_subpic = &p_vout->p_subpicture[i_index];
285             }
286         }
287     }
288
289     /* If we found an ephemer subpicture, check if it has to be
290      * displayed */
291     if( p_ephemer != NULL )
292     {
293         if( p_ephemer->i_start < ephemer_date )
294         {
295             /* Ephemer subpicture has lived too long */
296             vout_DestroySubPicture( p_vout, p_ephemer );
297         }
298         else
299         {
300             /* Ephemer subpicture can still live a bit */
301             p_ephemer->p_next = p_subpic;
302             return p_ephemer;
303         }
304     }
305
306     return p_subpic;
307 }
308