1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000 VideoLAN
5 * $Id: vout_subpictures.c,v 1.9 2002/02/24 20:51:10 gbazin Exp $
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 <errno.h> /* ENOMEM */
29 #include <stdlib.h> /* free() */
30 #include <stdio.h> /* sprintf() */
31 #include <string.h> /* strerror() */
33 #include <videolan/vlc.h>
36 #include "video_output.h"
38 /*****************************************************************************
40 *****************************************************************************/
41 static void vout_RenderSPU( const vout_thread_t *p_vout, picture_t *p_pic,
42 const subpicture_t *p_spu );
44 /*****************************************************************************
45 * vout_DisplaySubPicture: display a subpicture unit
46 *****************************************************************************
47 * Remove the reservation flag of a subpicture, which will cause it to be
49 *****************************************************************************/
50 void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
54 /* Check if status is valid */
55 if( p_subpic->i_status != RESERVED_SUBPICTURE )
57 intf_ErrMsg( "vout error: subpicture %p has invalid status #%d",
58 p_subpic, p_subpic->i_status );
61 /* If the user requested an SPU margin, we force the position after
62 * having checked that it was a valid value. */
63 i_margin = config_GetIntVariable( VOUT_SPUMARGIN_VAR );
67 if( p_subpic->i_height + i_margin <= p_vout->output.i_height )
69 p_subpic->i_y = p_vout->output.i_height
70 - i_margin - p_subpic->i_height;
74 /* Remove reservation flag */
75 p_subpic->i_status = READY_SUBPICTURE;
78 /*****************************************************************************
79 * vout_CreateSubPicture: allocate a subpicture in the video output heap.
80 *****************************************************************************
81 * This function create a reserved subpicture in the video output heap.
82 * A null pointer is returned if the function fails. This method provides an
83 * already allocated zone of memory in the spu data fields. It needs locking
84 * since several pictures can be created by several producers threads.
85 *****************************************************************************/
86 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type,
89 int i_subpic; /* subpicture index */
90 subpicture_t * p_free_subpic = NULL; /* first free subpicture */
91 subpicture_t * p_destroyed_subpic = NULL; /* first destroyed subpic */
94 vlc_mutex_lock( &p_vout->subpicture_lock );
97 * Look for an empty place
99 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
101 if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
103 /* Subpicture is marked for destruction, but is still allocated */
104 if( (p_vout->p_subpicture[i_subpic].i_type == i_type) &&
105 (p_vout->p_subpicture[i_subpic].i_size >= i_size) )
107 /* Memory size do match or is smaller : memory will not be
108 * reallocated, and function can end immediately - this is
109 * the best possible case, since no memory allocation needs
111 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
112 vlc_mutex_unlock( &p_vout->subpicture_lock );
113 return( &p_vout->p_subpicture[i_subpic] );
115 else if( p_destroyed_subpic == NULL )
117 /* Memory size do not match, but subpicture index will be kept
118 * in case we find no other place */
119 p_destroyed_subpic = &p_vout->p_subpicture[i_subpic];
122 else if( (p_free_subpic == NULL) &&
123 (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
125 /* Subpicture is empty and ready for allocation */
126 p_free_subpic = &p_vout->p_subpicture[i_subpic];
130 /* If no free subpictures are available, use a destroyed subpicture */
131 if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
133 /* No free subpicture or matching destroyed subpictures have been
134 * found, but a destroyed subpicture is still avalaible */
135 free( p_destroyed_subpic->p_data );
136 p_free_subpic = p_destroyed_subpic;
139 /* If no free or destroyed subpicture could be found */
140 if( p_free_subpic == NULL )
142 intf_ErrMsg( "vout error: subpicture heap is full" );
143 vlc_mutex_unlock( &p_vout->subpicture_lock );
147 /* Prepare subpicture */
150 case TEXT_SUBPICTURE: /* text subpicture */
151 p_free_subpic->p_data = memalign( 16, i_size + 1 );
153 case DVD_SUBPICTURE: /* DVD subpicture unit */
154 p_free_subpic->p_data = memalign( 16, i_size );
157 intf_ErrMsg( "vout error: unknown subpicture type %d", i_type );
158 p_free_subpic->p_data = NULL;
162 if( p_free_subpic->p_data != NULL )
164 /* Copy subpicture information, set some default values */
165 p_free_subpic->i_type = i_type;
166 p_free_subpic->i_status = RESERVED_SUBPICTURE;
167 p_free_subpic->i_size = i_size;
168 p_free_subpic->i_x = 0;
169 p_free_subpic->i_y = 0;
170 p_free_subpic->i_width = 0;
171 p_free_subpic->i_height = 0;
175 /* Memory allocation failed : set subpicture as empty */
176 p_free_subpic->i_type = EMPTY_SUBPICTURE;
177 p_free_subpic->i_status = FREE_SUBPICTURE;
178 p_free_subpic = NULL;
179 intf_ErrMsg( "vout error: spu allocation returned %s",
180 strerror( ENOMEM ) );
183 vlc_mutex_unlock( &p_vout->subpicture_lock );
185 return( p_free_subpic );
188 /*****************************************************************************
189 * vout_DestroySubPicture: remove a subpicture from the heap
190 *****************************************************************************
191 * This function frees a previously reserved subpicture.
192 * It is meant to be used when the construction of a picture aborted.
193 * This function does not need locking since reserved subpictures are ignored
194 * by the output thread.
195 *****************************************************************************/
196 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
198 /* Check if status is valid */
199 if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
200 && ( p_subpic->i_status != READY_SUBPICTURE ) )
202 intf_ErrMsg( "vout error: subpicture %p has invalid status %d",
203 p_subpic, p_subpic->i_status );
206 p_subpic->i_status = DESTROYED_SUBPICTURE;
209 /*****************************************************************************
210 * vout_RenderSubPictures: render a subpicture list
211 *****************************************************************************
212 * This function renders a sub picture unit.
213 *****************************************************************************/
214 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic,
215 subpicture_t *p_subpic )
218 p_vout_font_t p_font; /* text font */
219 int i_width, i_height; /* subpicture dimensions */
222 while( p_subpic != NULL )
224 switch( p_subpic->i_type )
226 case DVD_SUBPICTURE: /* DVD subpicture unit */
227 vout_RenderSPU( p_vout, p_pic, p_subpic );
231 case TEXT_SUBPICTURE: /* single line text */
232 /* Select default font if not specified */
233 p_font = p_subpic->type.text.p_font;
236 p_font = p_vout->p_default_font;
239 /* Compute text size (width and height fields are ignored)
241 vout_TextSize( p_font, p_subpic->type.text.i_style,
242 p_subpic->p_data, &i_width, &i_height );
243 if( !Align( p_vout, &p_subpic->i_x, &p_subpic->i_y,
244 i_width, i_height, p_subpic->i_horizontal_align,
245 p_subpic->i_vertical_align ) )
248 p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
249 p_subpic->i_x * p_vout->i_bytes_per_pixel +
250 p_subpic->i_y * p_vout->i_bytes_per_line,
251 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line, p_subpic->type.text.i_char_color,
252 p_subpic->type.text.i_border_color,
253 p_subpic->type.text.i_bg_color,
254 p_subpic->type.text.i_style, p_subpic->p_data, 100 );
255 SetBufferArea( p_vout, p_subpic->i_x, p_subpic->i_y,
262 intf_ErrMsg( "vout error: unknown subpicture %p type %d",
263 p_subpic, p_subpic->i_type );
267 p_subpic = p_subpic->p_next;
271 /*****************************************************************************
272 * vout_SortSubPictures: find the subpictures to display
273 *****************************************************************************
274 * This function parses all subpictures and decides which ones need to be
275 * displayed. This operation does not need lock, since only READY_SUBPICTURE
276 * are handled. If no picture has been selected, display_date will depend on
278 * We also check for ephemer DVD subpictures (subpictures that have
279 * to be removed if a newer one is available), which makes it a lot
280 * more difficult to guess if a subpicture has to be rendered or not.
281 *****************************************************************************/
282 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
283 mtime_t display_date )
286 subpicture_t *p_subpic = NULL;
287 subpicture_t *p_ephemer = NULL;
288 mtime_t ephemer_date = 0;
290 /* We get an easily parsable chained list of subpictures which
291 * ends with NULL since p_subpic was initialized to NULL. */
292 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
294 if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
296 /* If it is a DVD subpicture, check its date */
297 if( p_vout->p_subpicture[i_index].i_type == DVD_SUBPICTURE )
299 if( display_date > p_vout->p_subpicture[i_index].i_stop )
301 /* Too late, destroy the subpic */
302 vout_DestroySubPicture( p_vout,
303 &p_vout->p_subpicture[i_index] );
307 if( display_date < p_vout->p_subpicture[i_index].i_start )
309 /* Too early, come back next monday */
313 /* If this is an ephemer subpic, see if it's the
314 * youngest we have */
315 if( p_vout->p_subpicture[i_index].b_ephemer )
317 if( p_ephemer == NULL )
319 p_ephemer = &p_vout->p_subpicture[i_index];
323 if( p_vout->p_subpicture[i_index].i_start
324 < p_ephemer->i_start )
326 /* Link the previous ephemer subpicture and
327 * replace it with the current one */
328 p_ephemer->p_next = p_subpic;
329 p_subpic = p_ephemer;
330 p_ephemer = &p_vout->p_subpicture[i_index];
332 /* If it's the 2nd youngest subpicture,
333 * register its date */
335 || ephemer_date > p_subpic->i_start )
337 ephemer_date = p_subpic->i_start;
344 p_vout->p_subpicture[i_index].p_next = p_subpic;
345 p_subpic = &p_vout->p_subpicture[i_index];
347 /* If it's the 2nd youngest subpicture, register its date */ if( !ephemer_date || ephemer_date > p_subpic->i_start )
349 ephemer_date = p_subpic->i_start;
352 /* If it's not a DVD subpicture, just register it */
355 p_vout->p_subpicture[i_index].p_next = p_subpic;
356 p_subpic = &p_vout->p_subpicture[i_index];
361 /* If we found an ephemer subpicture, check if it has to be
363 if( p_ephemer != NULL )
365 if( p_ephemer->i_start < ephemer_date )
367 /* Ephemer subpicture has lived too long */
368 vout_DestroySubPicture( p_vout, p_ephemer );
372 /* Ephemer subpicture can still live a bit */
373 p_ephemer->p_next = p_subpic;
381 /*****************************************************************************
382 * vout_RenderSPU: draw an SPU on a picture
383 *****************************************************************************
384 * This is a fast implementation of the subpicture drawing code. The data
385 * has been preprocessed once in spu_decoder.c, so we don't need to parse the
386 * RLE buffer again and again. Most sanity checks are done in spu_decoder.c
387 * so that this routine can be as fast as possible.
388 *****************************************************************************/
389 static void vout_RenderSPU( const vout_thread_t *p_vout, picture_t *p_pic,
390 const subpicture_t *p_spu )
392 /* Common variables */
393 u8 p_clut8[4], p_trsp[4];
396 u16 *p_source = (u16 *)p_spu->p_data;
402 int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
404 /* FIXME: get this from the DVD */
405 p_trsp[0] = 0x00; p_trsp[1] = 0xff; p_trsp[2] = 0xff; p_trsp[3] = 0xff;
407 switch( p_vout->output.i_chroma )
409 /* I420 target, no scaling */
414 /* FIXME: get this from the DVD */
415 p_clut8[0] = 0xaa; p_clut8[1] = 0x44;
416 p_clut8[2] = 0xff; p_clut8[3] = 0x88;
418 p_dest = p_pic->p->p_pixels + p_spu->i_x + p_spu->i_width
419 + p_vout->output.i_width * ( p_spu->i_y + p_spu->i_height );
421 /* Draw until we reach the bottom of the subtitle */
422 for( i_y = p_spu->i_height * p_vout->output.i_width ;
424 i_y -= p_vout->output.i_width )
426 /* Draw until we reach the end of the line */
427 for( i_x = p_spu->i_width ; i_x ; )
429 /* Get the RLE part, then draw the line */
430 i_color = *p_source & 0x3;
432 switch( p_trsp[ i_color ] )
435 i_x -= *p_source++ >> 2;
439 i_len = *p_source++ >> 2;
440 memset( p_dest - i_x - i_y, p_clut8[ i_color ], i_len );
445 /* FIXME: we should do transparency */
446 i_len = *p_source++ >> 2;
447 memset( p_dest - i_x - i_y, p_clut8[ i_color ], i_len );
456 /* RV16 target, scaling */
459 /* FIXME: get this from the DVD */
460 p_clut16[0] = 0xaaaa; p_clut16[1] = 0x4444;
461 p_clut16[2] = 0xffff; p_clut16[3] = 0x8888;
463 i_xscale = ( p_vout->output.i_width << 6 ) / p_vout->render.i_width;
464 i_yscale = ( p_vout->output.i_height << 6 ) / p_vout->render.i_height;
466 i_width = p_spu->i_width * i_xscale;
467 i_height = p_spu->i_height * i_yscale;
469 p_dest = p_pic->p->p_pixels + ( i_width >> 6 ) * 2
470 /* Add the picture coordinates and the SPU coordinates */
471 + ( (p_spu->i_x * i_xscale) >> 6 ) * 2
472 + ( (p_spu->i_y * i_yscale) >> 6 ) * p_vout->output.i_width * 2;
474 /* Draw until we reach the bottom of the subtitle */
475 for( i_y = 0 ; i_y < i_height ; )
480 /* Check whether we need to draw one line or more than one */
481 if( i_ytmp + 1 >= ( i_y >> 6 ) )
483 /* Just one line : we precalculate i_y >> 6 */
484 i_yreal = p_vout->output.i_width * 2 * i_ytmp;
486 /* Draw until we reach the end of the line */
487 for( i_x = i_width ; i_x ; )
489 /* Get the RLE part, then draw the line */
490 i_color = *p_source & 0x3;
492 switch( p_trsp[ i_color ] )
495 i_x -= i_xscale * ( *p_source++ >> 2 );
499 i_len = i_xscale * ( *p_source++ >> 2 );
500 memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
502 2 * ( ( i_len >> 6 ) + 1 ) );
507 /* FIXME: we should do transparency */
508 i_len = i_xscale * ( *p_source++ >> 2 );
509 memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
511 2 * ( ( i_len >> 6 ) + 1 ) );
520 i_yreal = p_vout->output.i_width * 2 * i_ytmp;
521 i_ynext = p_vout->output.i_width * 2 * i_y >> 6;
523 /* Draw until we reach the end of the line */
524 for( i_x = i_width ; i_x ; )
526 /* Get the RLE part, then draw as many lines as needed */
527 i_color = *p_source & 0x3;
529 switch( p_trsp[ i_color ] )
532 i_x -= i_xscale * ( *p_source++ >> 2 );
536 i_len = i_xscale * ( *p_source++ >> 2 );
537 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
538 i_ytmp += p_vout->output.i_width * 2 )
540 memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
542 2 * ( ( i_len >> 6 ) + 1 ) );
548 /* FIXME: we should do transparency */
549 i_len = i_xscale * ( *p_source++ >> 2 );
550 for( i_ytmp = i_yreal ; i_ytmp < i_ynext ;
551 i_ytmp += p_vout->output.i_width * 2 )
553 memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
555 2 * ( ( i_len >> 6 ) + 1 ) );
567 intf_ErrMsg( "vout error: unknown chroma, can't render SPU" );