]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
This is the first part of the new configuration architecture for vlc.
[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.9 2002/02/24 20:51:10 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 <errno.h>                                                 /* ENOMEM */
29 #include <stdlib.h>                                                /* free() */
30 #include <stdio.h>                                              /* sprintf() */
31 #include <string.h>                                            /* strerror() */
32
33 #include <videolan/vlc.h>
34
35 #include "video.h"
36 #include "video_output.h"
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static void vout_RenderSPU( const vout_thread_t *p_vout, picture_t *p_pic,
42                             const subpicture_t *p_spu );
43
44 /*****************************************************************************
45  * vout_DisplaySubPicture: display a subpicture unit
46  *****************************************************************************
47  * Remove the reservation flag of a subpicture, which will cause it to be
48  * ready for display.
49  *****************************************************************************/
50 void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
51 {
52     int         i_margin;
53
54     /* Check if status is valid */
55     if( p_subpic->i_status != RESERVED_SUBPICTURE )
56     {
57         intf_ErrMsg( "vout error: subpicture %p has invalid status #%d",
58                      p_subpic, p_subpic->i_status );
59     }
60
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 );
64
65     if( i_margin >= 0 )
66     {
67         if( p_subpic->i_height + i_margin <= p_vout->output.i_height )
68         {
69             p_subpic->i_y = p_vout->output.i_height
70                              - i_margin - p_subpic->i_height;
71         }
72     }
73
74     /* Remove reservation flag */
75     p_subpic->i_status = READY_SUBPICTURE;
76 }
77
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,
87                                      int i_size )
88 {
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 */
92
93     /* Get lock */
94     vlc_mutex_lock( &p_vout->subpicture_lock );
95
96     /*
97      * Look for an empty place
98      */
99     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
100     {
101         if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
102         {
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) )
106             {
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
110                  * to be done */
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] );
114             }
115             else if( p_destroyed_subpic == NULL )
116             {
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];
120             }
121         }
122         else if( (p_free_subpic == NULL) &&
123                  (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
124         {
125             /* Subpicture is empty and ready for allocation */
126             p_free_subpic = &p_vout->p_subpicture[i_subpic];
127         }
128     }
129
130     /* If no free subpictures are available, use a destroyed subpicture */
131     if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
132     {
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;
137     }
138
139     /* If no free or destroyed subpicture could be found */
140     if( p_free_subpic == NULL )
141     {
142         intf_ErrMsg( "vout error: subpicture heap is full" );
143         vlc_mutex_unlock( &p_vout->subpicture_lock );
144         return( NULL );
145     }
146
147     /* Prepare subpicture */
148     switch( i_type )
149     {
150     case TEXT_SUBPICTURE:                             /* text subpicture */
151         p_free_subpic->p_data = memalign( 16, i_size + 1 );
152         break;
153     case DVD_SUBPICTURE:                          /* DVD subpicture unit */
154         p_free_subpic->p_data = memalign( 16, i_size );
155         break;
156     default:
157         intf_ErrMsg( "vout error: unknown subpicture type %d", i_type );
158         p_free_subpic->p_data   =  NULL;
159         break;
160     }
161
162     if( p_free_subpic->p_data != NULL )
163     {
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;
172     }
173     else
174     {
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 ) );
181     }
182
183     vlc_mutex_unlock( &p_vout->subpicture_lock );
184
185     return( p_free_subpic );
186 }
187
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 )
197 {
198    /* Check if status is valid */
199    if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
200           && ( p_subpic->i_status != READY_SUBPICTURE ) )
201    {
202        intf_ErrMsg( "vout error: subpicture %p has invalid status %d",
203                    p_subpic, p_subpic->i_status );
204    }
205
206     p_subpic->i_status = DESTROYED_SUBPICTURE;
207 }
208
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 )
216 {
217 #if 0
218     p_vout_font_t       p_font;                                 /* text font */
219     int                 i_width, i_height;          /* subpicture dimensions */
220 #endif
221
222     while( p_subpic != NULL )
223     {
224         switch( p_subpic->i_type )
225         {
226         case DVD_SUBPICTURE:                          /* DVD subpicture unit */
227             vout_RenderSPU( p_vout, p_pic, p_subpic );
228             break;
229
230 #if 0
231         case TEXT_SUBPICTURE:                            /* single line text */
232             /* Select default font if not specified */
233             p_font = p_subpic->type.text.p_font;
234             if( p_font == NULL )
235             {
236                 p_font = p_vout->p_default_font;
237             }
238
239             /* Compute text size (width and height fields are ignored)
240              * and print it */
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 ) )
246             {
247                 vout_Print( p_font,
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,
256                                i_width, i_height );
257             }
258             break;
259 #endif
260
261         default:
262             intf_ErrMsg( "vout error: unknown subpicture %p type %d",
263                          p_subpic, p_subpic->i_type );
264             break;
265         }
266
267         p_subpic = p_subpic->p_next;
268     }
269 }
270
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
277  * the subpicture.
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 )
284 {
285     int i_index;
286     subpicture_t *p_subpic     = NULL;
287     subpicture_t *p_ephemer    = NULL;
288     mtime_t       ephemer_date = 0;
289
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++ )
293     {
294         if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
295         {
296             /* If it is a DVD subpicture, check its date */
297             if( p_vout->p_subpicture[i_index].i_type == DVD_SUBPICTURE )
298             {
299                 if( display_date > p_vout->p_subpicture[i_index].i_stop )
300                 {
301                     /* Too late, destroy the subpic */
302                     vout_DestroySubPicture( p_vout,
303                                     &p_vout->p_subpicture[i_index] );
304                     continue;
305                 }
306
307                 if( display_date < p_vout->p_subpicture[i_index].i_start )
308                 {
309                     /* Too early, come back next monday */
310                     continue;
311                 }
312
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 )
316                 {
317                     if( p_ephemer == NULL )
318                     {
319                         p_ephemer = &p_vout->p_subpicture[i_index];
320                         continue;
321                     }
322
323                     if( p_vout->p_subpicture[i_index].i_start
324                                                      < p_ephemer->i_start )
325                     {
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];
331
332                         /* If it's the 2nd youngest subpicture,
333                          * register its date */
334                         if( !ephemer_date
335                               || ephemer_date > p_subpic->i_start )
336                         {
337                             ephemer_date = p_subpic->i_start;
338                         }
339
340                         continue;
341                     }
342                 }
343
344                 p_vout->p_subpicture[i_index].p_next = p_subpic;
345                 p_subpic = &p_vout->p_subpicture[i_index];
346
347                 /* If it's the 2nd youngest subpicture, register its date */                    if( !ephemer_date || ephemer_date > p_subpic->i_start )
348                 {
349                     ephemer_date = p_subpic->i_start;
350                 }
351             }
352             /* If it's not a DVD subpicture, just register it */
353             else
354             {
355                 p_vout->p_subpicture[i_index].p_next = p_subpic;
356                 p_subpic = &p_vout->p_subpicture[i_index];
357             }
358         }
359     }
360
361     /* If we found an ephemer subpicture, check if it has to be
362      * displayed */
363     if( p_ephemer != NULL )
364     {
365         if( p_ephemer->i_start < ephemer_date )
366         {
367             /* Ephemer subpicture has lived too long */
368             vout_DestroySubPicture( p_vout, p_ephemer );
369         }
370         else
371         {
372             /* Ephemer subpicture can still live a bit */
373             p_ephemer->p_next = p_subpic;
374             return p_ephemer;
375         }
376     }
377
378     return p_subpic;
379 }
380
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 )
391 {
392     /* Common variables */
393     u8   p_clut8[4], p_trsp[4];
394     u16  p_clut16[4];
395     u8  *p_dest;
396     u16 *p_source = (u16 *)p_spu->p_data;
397
398     int i_x, i_y;
399     int i_len, i_color;
400
401     /* RGB-specific */
402     int i_xscale, i_yscale, i_width, i_height, i_ytmp, i_yreal, i_ynext;
403
404     /* FIXME: get this from the DVD */
405     p_trsp[0] = 0x00; p_trsp[1] = 0xff; p_trsp[2] = 0xff; p_trsp[3] = 0xff;
406
407     switch( p_vout->output.i_chroma )
408     {
409     /* I420 target, no scaling */
410     case FOURCC_I420:
411     case FOURCC_IYUV:
412     case FOURCC_YV12:
413
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;
417
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 );
420
421     /* Draw until we reach the bottom of the subtitle */
422     for( i_y = p_spu->i_height * p_vout->output.i_width ;
423          i_y ;
424          i_y -= p_vout->output.i_width )
425     {
426         /* Draw until we reach the end of the line */
427         for( i_x = p_spu->i_width ; i_x ; )
428         {
429             /* Get the RLE part, then draw the line */
430             i_color = *p_source & 0x3;
431
432             switch( p_trsp[ i_color ] )
433             {
434                 case 0x00:
435                     i_x -= *p_source++ >> 2;
436                     break;
437
438                 case 0xff:
439                     i_len = *p_source++ >> 2;
440                     memset( p_dest - i_x - i_y, p_clut8[ i_color ], i_len );
441                     i_x -= i_len;
442                     break;
443
444                 default:
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 );
448                     i_x -= i_len;
449                     break;
450             }
451         }
452     }
453
454     break;
455
456     /* RV16 target, scaling */
457     case FOURCC_RV16:
458
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;
462
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;
465
466     i_width  = p_spu->i_width  * i_xscale;
467     i_height = p_spu->i_height * i_yscale;
468
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;
473
474     /* Draw until we reach the bottom of the subtitle */
475     for( i_y = 0 ; i_y < i_height ; )
476     {
477         i_ytmp = i_y >> 6;
478         i_y += i_yscale;
479
480         /* Check whether we need to draw one line or more than one */
481         if( i_ytmp + 1 >= ( i_y >> 6 ) )
482         {
483             /* Just one line : we precalculate i_y >> 6 */
484             i_yreal = p_vout->output.i_width * 2 * i_ytmp;
485
486             /* Draw until we reach the end of the line */
487             for( i_x = i_width ; i_x ; )
488             {
489                 /* Get the RLE part, then draw the line */
490                 i_color = *p_source & 0x3;
491
492                 switch( p_trsp[ i_color ] )
493                 {
494                 case 0x00:
495                     i_x -= i_xscale * ( *p_source++ >> 2 );
496                     break;
497
498                 case 0xff:
499                     i_len = i_xscale * ( *p_source++ >> 2 );
500                     memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
501                             p_clut16[ i_color ],
502                             2 * ( ( i_len >> 6 ) + 1 ) );
503                     i_x -= i_len;
504                     break;
505
506                 default:
507                     /* FIXME: we should do transparency */
508                     i_len = i_xscale * ( *p_source++ >> 2 );
509                     memset( p_dest - 2 * ( i_x >> 6 ) + i_yreal,
510                             p_clut16[ i_color ],
511                             2 * ( ( i_len >> 6 ) + 1 ) );
512                     i_x -= i_len;
513                     break;
514                 }
515
516             }
517         }
518         else
519         {
520             i_yreal = p_vout->output.i_width * 2 * i_ytmp;
521             i_ynext = p_vout->output.i_width * 2 * i_y >> 6;
522
523             /* Draw until we reach the end of the line */
524             for( i_x = i_width ; i_x ; )
525             {
526                 /* Get the RLE part, then draw as many lines as needed */
527                 i_color = *p_source & 0x3;
528
529                 switch( p_trsp[ i_color ] )
530                 {
531                 case 0x00:
532                     i_x -= i_xscale * ( *p_source++ >> 2 );
533                     break;
534
535                 case 0xff:
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 )
539                     {
540                         memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
541                                 p_clut16[ i_color ],
542                                 2 * ( ( i_len >> 6 ) + 1 ) );
543                     }
544                     i_x -= i_len;
545                     break;
546
547                 default:
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 )
552                     {
553                         memset( p_dest - 2 * ( i_x >> 6 ) + i_ytmp,
554                                 p_clut16[ i_color ],
555                                 2 * ( ( i_len >> 6 ) + 1 ) );
556                     }
557                     i_x -= i_len;
558                     break;
559                 }
560             }
561         }
562     }
563
564     break;
565
566     default:
567         intf_ErrMsg( "vout error: unknown chroma, can't render SPU" );
568         break;
569     }
570 }
571