]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
* ./BUGS: added a list of known bugs. Please add your findings!
[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.5 2002/01/04 14:01:35 sam 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_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 );
45
46 /* FIXME: fake palette - the real one has to be sought in the .IFO */
47 static int p_palette[4] = { 0x0000, 0x0000, 0xffff, 0x8888 };
48
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 )
57 {
58 #ifdef TRACE_VOUT
59     char        psz_start[ MSTRTIME_MAX_SIZE ];    /* buffer for date string */
60     char        psz_stop[ MSTRTIME_MAX_SIZE ];     /* buffer for date string */
61 #endif
62
63 #ifdef DEBUG
64     /* Check if status is valid */
65     if( p_subpic->i_status != RESERVED_SUBPICTURE )
66     {
67         intf_ErrMsg("error: subpicture %p has invalid status #%d", p_subpic,
68                     p_subpic->i_status );
69     }
70 #endif
71
72     /* Remove reservation flag */
73     p_subpic->i_status = READY_SUBPICTURE;
74
75 #ifdef TRACE_VOUT
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 ) );
81 #endif
82 }
83
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,
93                                      int i_size )
94 {
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 */
98
99     /* Get lock */
100     vlc_mutex_lock( &p_vout->subpicture_lock );
101
102     /*
103      * Look for an empty place
104      */
105     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
106     {
107         if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
108         {
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) )
112             {
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
116                  * to be done */
117                 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
118 #ifdef TRACE_VOUT
119                 intf_DbgMsg("subpicture %p (in destroyed subpicture slot)",
120                             &p_vout->p_subpicture[i_subpic] );
121 #endif
122                 vlc_mutex_unlock( &p_vout->subpicture_lock );
123                 return( &p_vout->p_subpicture[i_subpic] );
124             }
125             else if( p_destroyed_subpic == NULL )
126             {
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];
130             }
131         }
132         else if( (p_free_subpic == NULL) &&
133                  (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
134         {
135             /* Subpicture is empty and ready for allocation */
136             p_free_subpic = &p_vout->p_subpicture[i_subpic];
137         }
138     }
139
140     /* If no free subpictures are available, use a destroyed subpicture */
141     if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
142     {
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;
147     }
148
149     /*
150      * Prepare subpicture
151      */
152     if( p_free_subpic != NULL )
153     {
154         /* Allocate memory */
155         switch( i_type )
156         {
157         case TEXT_SUBPICTURE:                             /* text subpicture */
158             p_free_subpic->p_data = memalign( 16, i_size + 1 );
159             break;
160         case DVD_SUBPICTURE:                          /* DVD subpicture unit */
161             p_free_subpic->p_data = memalign( 16, i_size );
162             break;
163 #ifdef DEBUG
164         default:
165             intf_ErrMsg("error: unknown subpicture type %d", i_type );
166             p_free_subpic->p_data   =  NULL;
167             break;
168 #endif
169         }
170
171         if( p_free_subpic->p_data != NULL )
172         {
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;
183         }
184         else
185         {
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 ) );
192         }
193
194 #ifdef TRACE_VOUT
195         intf_DbgMsg("subpicture %p (in free subpicture slot)", p_free_subpic );
196 #endif
197         vlc_mutex_unlock( &p_vout->subpicture_lock );
198         return( p_free_subpic );
199     }
200
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 );
204     return( NULL );
205 }
206
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 )
216 {
217 #ifdef DEBUG
218    /* Check if status is valid */
219    if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
220           && ( p_subpic->i_status != READY_SUBPICTURE ) )
221    {
222        intf_ErrMsg("error: subpicture %p has invalid status %d",
223                    p_subpic, p_subpic->i_status );
224    }
225 #endif
226
227     p_subpic->i_status = DESTROYED_SUBPICTURE;
228
229 #ifdef TRACE_VOUT
230     intf_DbgMsg("subpicture %p", p_subpic);
231 #endif
232 }
233
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 )
241 {
242 #if 0
243     p_vout_font_t       p_font;                                 /* text font */
244     int                 i_width, i_height;          /* subpicture dimensions */
245 #endif
246
247     while( p_subpic != NULL )
248     {
249         switch( p_subpic->i_type )
250         {
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 );
254             break;
255
256 #if 0
257         case TEXT_SUBPICTURE:                            /* single line text */
258             /* Select default font if not specified */
259             p_font = p_subpic->type.text.p_font;
260             if( p_font == NULL )
261             {
262                 p_font = p_vout->p_default_font;
263             }
264
265             /* Compute text size (width and height fields are ignored)
266              * and print it */
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 ) )
272             {
273                 vout_Print( p_font,
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,
282                                i_width, i_height );
283             }
284             break;
285 #endif
286
287         default:
288 #ifdef DEBUG
289             intf_ErrMsg( "error: unknown subpicture %p type %d",
290                          p_subpic, p_subpic->i_type );
291 #endif
292             break;
293         }
294
295         p_subpic = p_subpic->p_next;
296     }
297 }
298
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
305  * the subpicture.
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 )
312 {
313     int i_index;
314     subpicture_t *p_subpic     = NULL;
315     subpicture_t *p_ephemer    = NULL;
316     mtime_t       ephemer_date = 0;
317
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++ )
321     {
322         if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
323         {
324             /* If it is a DVD subpicture, check its date */
325             if( p_vout->p_subpicture[i_index].i_type == DVD_SUBPICTURE )
326             {
327                 if( display_date > p_vout->p_subpicture[i_index].i_stop )
328                 {
329                     /* Too late, destroy the subpic */
330                     vout_DestroySubPicture( p_vout,
331                                     &p_vout->p_subpicture[i_index] );
332                     continue;
333                 }
334
335                 if( display_date < p_vout->p_subpicture[i_index].i_start )
336                 {
337                     /* Too early, come back next monday */
338                     continue;
339                 }
340
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 )
344                 {
345                     if( p_ephemer == NULL )
346                     {
347                         p_ephemer = &p_vout->p_subpicture[i_index];
348                         continue;
349                     }
350
351                     if( p_vout->p_subpicture[i_index].i_start
352                                                      < p_ephemer->i_start )
353                     {
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];
359
360                         /* If it's the 2nd youngest subpicture,
361                          * register its date */
362                         if( !ephemer_date
363                               || ephemer_date > p_subpic->i_start )
364                         {
365                             ephemer_date = p_subpic->i_start;
366                         }
367
368                         continue;
369                     }
370                 }
371
372                 p_vout->p_subpicture[i_index].p_next = p_subpic;
373                 p_subpic = &p_vout->p_subpicture[i_index];
374
375                 /* If it's the 2nd youngest subpicture, register its date */                    if( !ephemer_date || ephemer_date > p_subpic->i_start )
376                 {
377                     ephemer_date = p_subpic->i_start;
378                 }
379             }
380             /* If it's not a DVD subpicture, just register it */
381             else
382             {
383                 p_vout->p_subpicture[i_index].p_next = p_subpic;
384                 p_subpic = &p_vout->p_subpicture[i_index];
385             }
386         }
387     }
388
389     /* If we found an ephemer subpicture, check if it has to be
390      * displayed */
391     if( p_ephemer != NULL )
392     {
393         if( p_ephemer->i_start < ephemer_date )
394         {
395             /* Ephemer subpicture has lived too long */
396             vout_DestroySubPicture( p_vout, p_ephemer );
397         }
398         else
399         {
400             /* Ephemer subpicture can still live a bit */
401             p_ephemer->p_next = p_subpic;
402             return p_ephemer;
403         }
404     }
405
406     return p_subpic;
407 }
408
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 )
419 {
420 #if 0
421     int  i_len, i_color;
422     u16 *p_source = (u16 *)p_spu->p_data;
423
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;
426
427     int i_width  = p_spu->i_width  * i_xscale;
428     int i_height = p_spu->i_height * i_yscale;
429
430     int i_x, i_y, i_ytmp, i_yreal, i_ynext;
431
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))
435                        * i_bytes_per_pixel
436                   + ( p_buffer->i_pic_y + ((p_spu->i_y * i_yscale) >> 6))
437                        * i_bytes_per_line;
438
439     /* Draw until we reach the bottom of the subtitle */
440     i_y = 0;
441
442     while( i_y < i_height )
443     {
444         i_ytmp = i_y >> 6;
445         i_y += i_yscale;
446
447         /* Check whether we need to draw one line or more than one */
448         if( i_ytmp + 1 >= ( i_y >> 6 ) )
449         {
450             /* Just one line : we precalculate i_y >> 6 */
451             i_yreal = i_bytes_per_line * i_ytmp;
452
453             /* Draw until we reach the end of the line */
454             i_x = i_width;
455
456             while( i_x )
457             {
458                 /* Get the RLE part */
459                 i_color = *p_source & 0x3;
460
461                 /* Draw the line */
462                 if( i_color )
463                 {
464                     i_len = i_xscale * ( *p_source++ >> 2 );
465
466                     memset( p_dest - i_bytes_per_pixel * ( i_x >> 6 )
467                                    + i_yreal,
468                             p_palette[ i_color ],
469                             i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
470
471                     i_x -= i_len;
472                     continue;
473                 }
474
475                 i_x -= i_xscale * ( *p_source++ >> 2 );
476             }
477         }
478         else
479         {
480             i_yreal = i_bytes_per_line * i_ytmp;
481             i_ynext = i_bytes_per_line * i_y >> 6;
482
483             /* Draw until we reach the end of the line */
484             i_x = i_width;
485
486             while( i_x )
487             {
488                 /* Get the RLE part */
489                 i_color = *p_source & 0x3;
490
491                 /* Draw as many lines as needed */
492                 if( i_color )
493                 {
494                     i_len = i_xscale * ( *p_source++ >> 2 );
495
496                     for( i_ytmp = i_yreal ;
497                          i_ytmp < i_ynext ;
498                          i_ytmp += i_bytes_per_line )
499                     {
500                         memset( p_dest - i_bytes_per_pixel * ( i_x >> 6 )
501                                        + i_ytmp,
502                                 p_palette[ i_color ],
503                                 i_bytes_per_pixel * ( ( i_len >> 6 ) + 1 ) );
504                     }
505
506                     i_x -= i_len;
507                     continue;
508                 }
509
510                 i_x -= i_xscale * ( *p_source++ >> 2 );
511             }
512         }
513     }
514 #endif
515 }
516
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 )
527 {
528     int  i_len, i_color;
529     u16 *p_source = (u16 *)p_spu->p_data;
530
531     int i_x, i_y;
532
533     u8 *p_dest = p_pic->p->p_pixels + p_spu->i_x + p_spu->i_width
534                    + p_vout->output.i_width * ( p_spu->i_y + p_spu->i_height );
535
536     /* Draw until we reach the bottom of the subtitle */
537     i_y = p_spu->i_height * p_vout->output.i_width;
538
539     while( i_y )
540     {
541         /* Draw until we reach the end of the line */
542         i_x = p_spu->i_width;
543
544         while( i_x )
545         {
546             /* Draw the line if needed */
547             i_color = *p_source & 0x3;
548
549             if( i_color )
550             {
551                 i_len = *p_source++ >> 2;
552                 memset( p_dest - i_x - i_y, p_palette[ i_color ], i_len );
553                 i_x -= i_len;
554                 continue;
555             }
556
557             i_x -= *p_source++ >> 2;
558         }
559
560         i_y -= p_vout->output.i_width;
561     }
562 }
563