1 /*****************************************************************************
2 * vout_subpictures.c : subpicture management functions
3 *****************************************************************************
4 * Copyright (C) 2000 VideoLAN
5 * $Id: vout_subpictures.c,v 1.3 2001/12/30 07:09:56 sam 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_RenderRGBSPU( const vout_thread_t *p_vout, picture_t *p_pic,
42 const subpicture_t *p_spu );
43 static void vout_RenderYUVSPU( const vout_thread_t *p_vout, picture_t *p_pic,
44 const subpicture_t *p_spu );
46 /* FIXME: fake palette - the real one has to be sought in the .IFO */
47 static int p_palette[4] = { 0x0000, 0x0000, 0xffff, 0x8888 };
49 /*****************************************************************************
50 * vout_DisplaySubPicture: display a subpicture unit
51 *****************************************************************************
52 * Remove the reservation flag of a subpicture, which will cause it to be ready
53 * for display. The picture does not need to be locked, since it is ignored by
54 * the output thread if is reserved.
55 *****************************************************************************/
56 void vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
59 char psz_start[ MSTRTIME_MAX_SIZE ]; /* buffer for date string */
60 char psz_stop[ MSTRTIME_MAX_SIZE ]; /* buffer for date string */
64 /* Check if status is valid */
65 if( p_subpic->i_status != RESERVED_SUBPICTURE )
67 intf_ErrMsg("error: subpicture %p has invalid status #%d", p_subpic,
72 /* Remove reservation flag */
73 p_subpic->i_status = READY_SUBPICTURE;
76 /* Send subpicture information */
77 intf_DbgMsg("subpicture %p: type=%d, begin date=%s, end date=%s",
78 p_subpic, p_subpic->i_type,
79 mstrtime( psz_start, p_subpic->i_start ),
80 mstrtime( psz_stop, p_subpic->i_stop ) );
84 /*****************************************************************************
85 * vout_CreateSubPicture: allocate a subpicture in the video output heap.
86 *****************************************************************************
87 * This function create a reserved subpicture in the video output heap.
88 * A null pointer is returned if the function fails. This method provides an
89 * already allocated zone of memory in the spu data fields. It needs locking
90 * since several pictures can be created by several producers threads.
91 *****************************************************************************/
92 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type,
95 int i_subpic; /* subpicture index */
96 subpicture_t * p_free_subpic = NULL; /* first free subpicture */
97 subpicture_t * p_destroyed_subpic = NULL; /* first destroyed subpic */
100 vlc_mutex_lock( &p_vout->subpicture_lock );
103 * Look for an empty place
105 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
107 if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
109 /* Subpicture is marked for destruction, but is still allocated */
110 if( (p_vout->p_subpicture[i_subpic].i_type == i_type) &&
111 (p_vout->p_subpicture[i_subpic].i_size >= i_size) )
113 /* Memory size do match or is smaller : memory will not be
114 * reallocated, and function can end immediately - this is
115 * the best possible case, since no memory allocation needs
117 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
119 intf_DbgMsg("subpicture %p (in destroyed subpicture slot)",
120 &p_vout->p_subpicture[i_subpic] );
122 vlc_mutex_unlock( &p_vout->subpicture_lock );
123 return( &p_vout->p_subpicture[i_subpic] );
125 else if( p_destroyed_subpic == NULL )
127 /* Memory size do not match, but subpicture index will be kept
128 * in case we find no other place */
129 p_destroyed_subpic = &p_vout->p_subpicture[i_subpic];
132 else if( (p_free_subpic == NULL) &&
133 (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
135 /* Subpicture is empty and ready for allocation */
136 p_free_subpic = &p_vout->p_subpicture[i_subpic];
140 /* If no free subpictures are available, use a destroyed subpicture */
141 if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
143 /* No free subpicture or matching destroyed subpictures have been
144 * found, but a destroyed subpicture is still avalaible */
145 free( p_destroyed_subpic->p_data );
146 p_free_subpic = p_destroyed_subpic;
152 if( p_free_subpic != NULL )
154 /* Allocate memory */
157 case TEXT_SUBPICTURE: /* text subpicture */
158 p_free_subpic->p_data = memalign( 16, i_size + 1 );
160 case DVD_SUBPICTURE: /* DVD subpicture unit */
161 p_free_subpic->p_data = memalign( 16, i_size );
165 intf_ErrMsg("error: unknown subpicture type %d", i_type );
166 p_free_subpic->p_data = NULL;
171 if( p_free_subpic->p_data != NULL )
173 /* Copy subpicture information, set some default values */
174 p_free_subpic->i_type = i_type;
175 p_free_subpic->i_status = RESERVED_SUBPICTURE;
176 p_free_subpic->i_size = i_size;
177 p_free_subpic->i_x = 0;
178 p_free_subpic->i_y = 0;
179 p_free_subpic->i_width = 0;
180 p_free_subpic->i_height = 0;
181 p_free_subpic->i_horizontal_align = CENTER_RALIGN;
182 p_free_subpic->i_vertical_align = CENTER_RALIGN;
186 /* Memory allocation failed : set subpicture as empty */
187 p_free_subpic->i_type = EMPTY_SUBPICTURE;
188 p_free_subpic->i_status = FREE_SUBPICTURE;
189 p_free_subpic = NULL;
190 intf_ErrMsg( "vout error: spu allocation returned %s",
191 strerror( ENOMEM ) );
195 intf_DbgMsg("subpicture %p (in free subpicture slot)", p_free_subpic );
197 vlc_mutex_unlock( &p_vout->subpicture_lock );
198 return( p_free_subpic );
201 /* No free or destroyed subpicture could be found */
202 intf_DbgMsg( "warning: subpicture heap is full" );
203 vlc_mutex_unlock( &p_vout->subpicture_lock );
207 /*****************************************************************************
208 * vout_DestroySubPicture: remove a subpicture from the heap
209 *****************************************************************************
210 * This function frees a previously reserved subpicture.
211 * It is meant to be used when the construction of a picture aborted.
212 * This function does not need locking since reserved subpictures are ignored
213 * by the output thread.
214 *****************************************************************************/
215 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
218 /* Check if status is valid */
219 if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
220 && ( p_subpic->i_status != READY_SUBPICTURE ) )
222 intf_ErrMsg("error: subpicture %p has invalid status %d",
223 p_subpic, p_subpic->i_status );
227 p_subpic->i_status = DESTROYED_SUBPICTURE;
230 intf_DbgMsg("subpicture %p", p_subpic);
234 /*****************************************************************************
235 * vout_RenderSubPictures: render a subpicture list
236 *****************************************************************************
237 * This function renders a sub picture unit.
238 *****************************************************************************/
239 void vout_RenderSubPictures( vout_thread_t *p_vout, picture_t *p_pic,
240 subpicture_t *p_subpic )
243 p_vout_font_t p_font; /* text font */
244 int i_width, i_height; /* subpicture dimensions */
247 while( p_subpic != NULL )
249 switch( p_subpic->i_type )
251 case DVD_SUBPICTURE: /* DVD subpicture unit */
252 vout_RenderRGBSPU( p_vout, p_pic, p_subpic );
253 vout_RenderYUVSPU( p_vout, p_pic, p_subpic );
257 case TEXT_SUBPICTURE: /* single line text */
258 /* Select default font if not specified */
259 p_font = p_subpic->type.text.p_font;
262 p_font = p_vout->p_default_font;
265 /* Compute text size (width and height fields are ignored)
267 vout_TextSize( p_font, p_subpic->type.text.i_style,
268 p_subpic->p_data, &i_width, &i_height );
269 if( !Align( p_vout, &p_subpic->i_x, &p_subpic->i_y,
270 i_width, i_height, p_subpic->i_horizontal_align,
271 p_subpic->i_vertical_align ) )
274 p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
275 p_subpic->i_x * p_vout->i_bytes_per_pixel +
276 p_subpic->i_y * p_vout->i_bytes_per_line,
277 p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line, p_subpic->type.text.i_char_color,
278 p_subpic->type.text.i_border_color,
279 p_subpic->type.text.i_bg_color,
280 p_subpic->type.text.i_style, p_subpic->p_data, 100 );
281 SetBufferArea( p_vout, p_subpic->i_x, p_subpic->i_y,
289 intf_ErrMsg( "error: unknown subpicture %p type %d",
290 p_subpic, p_subpic->i_type );
295 p_subpic = p_subpic->p_next;
299 /*****************************************************************************
300 * vout_SortSubPictures: find the subpictures to display
301 *****************************************************************************
302 * This function parses all subpictures and decides which ones need to be
303 * displayed. This operation does not need lock, since only READY_SUBPICTURE
304 * are handled. If no picture has been selected, display_date will depend on
306 * We also check for ephemer DVD subpictures (subpictures that have
307 * to be removed if a newer one is available), which makes it a lot
308 * more difficult to guess if a subpicture has to be rendered or not.
309 *****************************************************************************/
310 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
311 mtime_t display_date )
314 subpicture_t *p_subpic = NULL;
315 subpicture_t *p_ephemer = NULL;
316 mtime_t ephemer_date = 0;
318 /* We get an easily parsable chained list of subpictures which
319 * ends with NULL since p_subpic was initialized to NULL. */
320 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
322 if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
324 /* If it is a DVD subpicture, check its date */
325 if( p_vout->p_subpicture[i_index].i_type == DVD_SUBPICTURE )
327 if( display_date > p_vout->p_subpicture[i_index].i_stop )
329 /* Too late, destroy the subpic */
330 vout_DestroySubPicture( p_vout,
331 &p_vout->p_subpicture[i_index] );
335 if( display_date < p_vout->p_subpicture[i_index].i_start )
337 /* Too early, come back next monday */
341 /* If this is an ephemer subpic, see if it's the
342 * youngest we have */
343 if( p_vout->p_subpicture[i_index].b_ephemer )
345 if( p_ephemer == NULL )
347 p_ephemer = &p_vout->p_subpicture[i_index];
351 if( p_vout->p_subpicture[i_index].i_start
352 < p_ephemer->i_start )
354 /* Link the previous ephemer subpicture and
355 * replace it with the current one */
356 p_ephemer->p_next = p_subpic;
357 p_subpic = p_ephemer;
358 p_ephemer = &p_vout->p_subpicture[i_index];
360 /* If it's the 2nd youngest subpicture,
361 * register its date */
363 || ephemer_date > p_subpic->i_start )
365 ephemer_date = p_subpic->i_start;
372 p_vout->p_subpicture[i_index].p_next = p_subpic;
373 p_subpic = &p_vout->p_subpicture[i_index];
375 /* If it's the 2nd youngest subpicture, register its date */ if( !ephemer_date || ephemer_date > p_subpic->i_start )
377 ephemer_date = p_subpic->i_start;
380 /* If it's not a DVD subpicture, just register it */
383 p_vout->p_subpicture[i_index].p_next = p_subpic;
384 p_subpic = &p_vout->p_subpicture[i_index];
389 /* If we found an ephemer subpicture, check if it has to be
391 if( p_ephemer != NULL )
393 if( p_ephemer->i_start < ephemer_date )
395 /* Ephemer subpicture has lived too long */
396 vout_DestroySubPicture( p_vout, p_ephemer );
400 /* Ephemer subpicture can still live a bit */
401 p_ephemer->p_next = p_subpic;
409 /*****************************************************************************
410 * vout_RenderRGBSPU: draw an SPU on a picture
411 *****************************************************************************
412 * This is a fast implementation of the subpicture drawing code. The data
413 * has been preprocessed once in spu_decoder.c, so we don't need to parse the
414 * RLE buffer again and again. Most sanity checks are done in spu_decoder.c
415 * so that this routine can be as fast as possible.
416 *****************************************************************************/
417 static void vout_RenderRGBSPU( const vout_thread_t *p_vout, picture_t *p_pic,
418 const subpicture_t *p_spu )
422 u16 *p_source = (u16 *)p_spu->p_data;
424 int i_xscale = ( p_buffer->i_pic_width << 6 ) / p_pic->i_width;
425 int i_yscale = ( p_buffer->i_pic_height << 6 ) / p_pic->i_height;
427 int i_width = p_spu->i_width * i_xscale;
428 int i_height = p_spu->i_height * i_yscale;
430 int i_x, i_y, i_ytmp, i_yreal, i_ynext;
432 u8 *p_dest = p_buffer->p_data + ( i_width >> 6 ) * i_bytes_per_pixel
433 /* Add the picture coordinates and the SPU coordinates */
434 + ( p_buffer->i_pic_x + ((p_spu->i_x * i_xscale) >> 6))
436 + ( p_buffer->i_pic_y + ((p_spu->i_y * i_yscale) >> 6))
439 /* Draw until we reach the bottom of the subtitle */
442 while( i_y < i_height )
447 /* Check whether we need to draw one line or more than one */
448 if( i_ytmp + 1 >= ( i_y >> 6 ) )
450 /* Just one line : we precalculate i_y >> 6 */
451 i_yreal = i_bytes_per_line * i_ytmp;
453 /* Draw until we reach the end of the line */
458 /* Get the RLE part */
459 i_color = *p_source & 0x3;
464 i_len = i_xscale * ( *p_source++ >> 2 );
466 memset( p_dest - i_bytes_per_pixel * ( i_x >> 6 )
468 p_palette[ i_color ],
469 i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
475 i_x -= i_xscale * ( *p_source++ >> 2 );
480 i_yreal = i_bytes_per_line * i_ytmp;
481 i_ynext = i_bytes_per_line * i_y >> 6;
483 /* Draw until we reach the end of the line */
488 /* Get the RLE part */
489 i_color = *p_source & 0x3;
491 /* Draw as many lines as needed */
494 i_len = i_xscale * ( *p_source++ >> 2 );
496 for( i_ytmp = i_yreal ;
498 i_ytmp += i_bytes_per_line )
500 memset( p_dest - i_bytes_per_pixel * ( i_x >> 6 )
502 p_palette[ i_color ],
503 i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
510 i_x -= i_xscale * ( *p_source++ >> 2 );
517 /*****************************************************************************
518 * vout_RenderYUVSPU: draw an SPU on an YUV overlay
519 *****************************************************************************
520 * This is a fast implementation of the subpicture drawing code. The data
521 * has been preprocessed once in spu_decoder.c, so we don't need to parse the
522 * RLE buffer again and again. Most sanity checks are done in spu_decoder.c
523 * so that this routine can be as fast as possible.
524 *****************************************************************************/
525 static void vout_RenderYUVSPU( const vout_thread_t *p_vout, picture_t *p_pic,
526 const subpicture_t *p_spu )
529 u16 *p_source = (u16 *)p_spu->p_data;
533 u8 *p_dest = p_pic->planes[ Y_PLANE ].p_data + p_spu->i_x + p_spu->i_width
534 + p_vout->output.i_width * ( p_spu->i_y + p_spu->i_height );
536 /* Draw until we reach the bottom of the subtitle */
537 i_y = p_spu->i_height * p_vout->output.i_width;
541 /* Draw until we reach the end of the line */
542 i_x = p_spu->i_width;
546 /* Draw the line if needed */
547 i_color = *p_source & 0x3;
551 i_len = *p_source++ >> 2;
552 memset( p_dest - i_x - i_y, p_palette[ i_color ], i_len );
557 i_x -= *p_source++ >> 2;
560 i_y -= p_vout->output.i_width;