1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000-2004 VideoLAN
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
28 #include <stdlib.h> /* free() */
29 #include <stdio.h> /* sprintf() */
30 #include <string.h> /* strerror() */
34 #include "vlc_video.h"
35 #include "video_output.h"
38 * Display a subpicture unit
40 * Remove the reservation flag of a subpicture, which will cause it to be
42 * \param p_vout the video output this subpicture should be displayed on
43 * \param p_subpic the subpicture to display
45 void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
49 /* Check if status is valid */
50 if( p_subpic->i_status != RESERVED_SUBPICTURE )
52 msg_Err( p_vout, "subpicture %p has invalid status #%d",
53 p_subpic, p_subpic->i_status );
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" );
62 if( p_subpic->i_height + (unsigned int)i_margin
63 <= p_vout->output.i_height )
65 p_subpic->i_y = p_vout->output.i_height
66 - i_margin - p_subpic->i_height;
70 /* Remove reservation flag */
71 p_subpic->i_status = READY_SUBPICTURE;
75 * Allocate a subpicture in the video output heap.
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
85 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_channel,
88 int i_subpic; /* subpicture index */
89 subpicture_t * p_subpic = NULL; /* first free subpicture */
91 /* Clear the default channel before writing into it */
92 if( i_channel == DEFAULT_CHAN )
94 vout_ClearOSDChannel( p_vout, DEFAULT_CHAN );
98 vlc_mutex_lock( &p_vout->subpicture_lock );
101 * Look for an empty place
104 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
106 if( p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
108 /* Subpicture is empty and ready for allocation */
109 p_subpic = &p_vout->p_subpicture[i_subpic];
110 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
115 /* If no free subpicture could be found */
116 if( p_subpic == NULL )
118 msg_Err( p_vout, "subpicture heap is full" );
119 vlc_mutex_unlock( &p_vout->subpicture_lock );
123 /* Copy subpicture information, set some default values */
124 p_subpic->i_channel = i_channel;
125 p_subpic->i_type = i_type;
126 p_subpic->i_status = RESERVED_SUBPICTURE;
128 p_subpic->i_start = 0;
129 p_subpic->i_stop = 0;
130 p_subpic->b_ephemer = VLC_FALSE;
134 p_subpic->i_width = 0;
135 p_subpic->i_height = 0;
137 /* Remain last subpicture displayed in DEFAULT_CHAN */
138 if( i_channel == DEFAULT_CHAN )
140 p_vout->p_default_channel = p_subpic;
143 vlc_mutex_unlock( &p_vout->subpicture_lock );
149 * Remove a subpicture from the heap
151 * This function frees a previously reserved subpicture.
152 * It is meant to be used when the construction of a picture aborted.
153 * This function does not need locking since reserved subpictures are ignored
154 * by the output thread.
156 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
159 vlc_mutex_lock( &p_vout->subpicture_lock );
161 /* There can be race conditions so we need to check the status */
162 if( p_subpic->i_status == FREE_SUBPICTURE )
164 vlc_mutex_unlock( &p_vout->subpicture_lock );
168 /* Check if status is valid */
169 if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
170 && ( p_subpic->i_status != READY_SUBPICTURE ) )
172 msg_Err( p_vout, "subpicture %p has invalid status %d",
173 p_subpic, p_subpic->i_status );
176 if( p_subpic->pf_destroy )
178 p_subpic->pf_destroy( p_subpic );
181 p_subpic->i_status = FREE_SUBPICTURE;
183 vlc_mutex_unlock( &p_vout->subpicture_lock );
186 /*****************************************************************************
187 * vout_RenderSubPictures: render a subpicture list
188 *****************************************************************************
189 * This function renders all sub picture units in the list.
190 *****************************************************************************/
191 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic,
192 subpicture_t *p_subpic )
195 vlc_mutex_lock( &p_vout->subpicture_lock );
197 /* Check i_status again to make sure spudec hasn't destroyed the subpic */
198 while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
200 p_subpic->pf_render( p_vout, p_pic, p_subpic );
201 p_subpic = p_subpic->p_next;
204 vlc_mutex_unlock( &p_vout->subpicture_lock );
207 /*****************************************************************************
208 * vout_SortSubPictures: find the subpictures to display
209 *****************************************************************************
210 * This function parses all subpictures and decides which ones need to be
211 * displayed. This operation does not need lock, since only READY_SUBPICTURE
212 * are handled. If no picture has been selected, display_date will depend on
214 * We also check for ephemer DVD subpictures (subpictures that have
215 * to be removed if a newer one is available), which makes it a lot
216 * more difficult to guess if a subpicture has to be rendered or not.
217 *****************************************************************************/
218 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
219 mtime_t display_date )
222 subpicture_t *p_subpic = NULL;
223 subpicture_t *p_ephemer = NULL;
224 mtime_t ephemer_date = 0;
226 /* We get an easily parsable chained list of subpictures which
227 * ends with NULL since p_subpic was initialized to NULL. */
228 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
230 if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
232 /* If it is a DVD subpicture, check its date */
233 if( p_vout->p_subpicture[i_index].i_type == MEMORY_SUBPICTURE )
235 if( !p_vout->p_subpicture[i_index].b_ephemer
236 && display_date > p_vout->p_subpicture[i_index].i_stop )
238 /* Too late, destroy the subpic */
239 vout_DestroySubPicture( p_vout,
240 &p_vout->p_subpicture[i_index] );
245 && display_date < p_vout->p_subpicture[i_index].i_start )
247 /* Too early, come back next monday */
251 /* If this is an ephemer subpic, see if it's the
252 * youngest we have */
253 if( p_vout->p_subpicture[i_index].b_ephemer )
255 if( p_ephemer == NULL )
257 p_ephemer = &p_vout->p_subpicture[i_index];
261 if( p_vout->p_subpicture[i_index].i_start
262 < p_ephemer->i_start )
264 /* Link the previous ephemer subpicture and
265 * replace it with the current one */
266 p_ephemer->p_next = p_subpic;
267 p_subpic = p_ephemer;
268 p_ephemer = &p_vout->p_subpicture[i_index];
270 /* If it's the 2nd youngest subpicture,
271 * register its date */
273 || ephemer_date > p_subpic->i_start )
275 ephemer_date = p_subpic->i_start;
282 p_vout->p_subpicture[i_index].p_next = p_subpic;
283 p_subpic = &p_vout->p_subpicture[i_index];
285 /* If it's the 2nd youngest subpicture, register its date */
286 if( !ephemer_date || ephemer_date > p_subpic->i_start )
288 ephemer_date = p_subpic->i_start;
291 /* If it's not a DVD subpicture, just register it */
294 p_vout->p_subpicture[i_index].p_next = p_subpic;
295 p_subpic = &p_vout->p_subpicture[i_index];
300 /* If we found an ephemer subpicture, check if it has to be
302 if( p_ephemer != NULL )
304 if( p_ephemer->i_start < ephemer_date )
306 /* Ephemer subpicture has lived too long */
307 vout_DestroySubPicture( p_vout, p_ephemer );
311 /* Ephemer subpicture can still live a bit */
312 p_ephemer->p_next = p_subpic;
320 /*****************************************************************************
321 * vout_RegisterOSDChannel: register an OSD channel
322 *****************************************************************************
323 * This function affects an ID to an OSD channel
324 *****************************************************************************/
325 int vout_RegisterOSDChannel( vout_thread_t *p_vout )
327 msg_Dbg( p_vout, "Registering OSD channel, ID: %i", p_vout->i_channel_count + 1 );
328 return ++p_vout->i_channel_count;
331 /*****************************************************************************
332 * vout_ClearOSDChannel: clear an OSD channel
333 *****************************************************************************
334 * This function destroys the subpictures which belong to the OSD channel
335 * corresponding to i_channel_id.
336 *****************************************************************************/
337 void vout_ClearOSDChannel( vout_thread_t *p_vout, int i_channel )
339 int i_subpic; /* subpicture index */
340 subpicture_t * p_subpic = NULL; /* first free subpicture */
342 if( i_channel == DEFAULT_CHAN )
344 if( p_vout->p_default_channel != NULL )
346 vout_DestroySubPicture( p_vout, p_vout->p_default_channel );
348 p_vout->p_default_channel = NULL;
352 vlc_mutex_lock( &p_vout->subpicture_lock );
354 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
356 p_subpic = &p_vout->p_subpicture[i_subpic];
357 if( p_subpic->i_status == FREE_SUBPICTURE
358 || ( p_subpic->i_status != RESERVED_SUBPICTURE
359 && p_subpic->i_status != READY_SUBPICTURE ) )
363 if( p_subpic->i_channel == i_channel )
365 if( p_subpic->pf_destroy )
367 p_subpic->pf_destroy( p_subpic );
369 p_subpic->i_status = FREE_SUBPICTURE;
373 vlc_mutex_unlock( &p_vout->subpicture_lock );