]> git.sesse.net Git - vlc/blob - src/video_output/vout_subpictures.c
Fixed major bugs in the PSI decoder.
[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.1 2001/12/09 17:01:37 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 "defs.h"
29
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                                /* free() */
32 #include <stdio.h>                                              /* sprintf() */
33 #include <string.h>                                            /* strerror() */
34
35 #include "common.h"
36 #include "intf_msg.h"
37 #include "threads.h"
38 #include "mtime.h"
39
40 #include "video.h"
41 #include "video_output.h"
42
43 /*****************************************************************************
44  * Local prototypes
45  *****************************************************************************/
46 static void vout_RenderRGBSPU( picture_t *p_pic, const subpicture_t *p_spu );
47 static void vout_RenderYUVSPU( picture_t *p_pic, const subpicture_t *p_spu );
48
49 /* FIXME: fake palette - the real one has to be sought in the .IFO */
50 static int p_palette[4] = { 0x0000, 0x0000, 0xffff, 0x8888 };
51
52 /*****************************************************************************
53  * vout_DisplaySubPicture: display a subpicture unit
54  *****************************************************************************
55  * Remove the reservation flag of a subpicture, which will cause it to be ready
56  * for display. The picture does not need to be locked, since it is ignored by
57  * the output thread if is reserved.
58  *****************************************************************************/
59 void  vout_DisplaySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
60 {
61 #ifdef TRACE_VOUT
62     char        psz_start[ MSTRTIME_MAX_SIZE ];    /* buffer for date string */
63     char        psz_stop[ MSTRTIME_MAX_SIZE ];     /* buffer for date string */
64 #endif
65
66 #ifdef DEBUG
67     /* Check if status is valid */
68     if( p_subpic->i_status != RESERVED_SUBPICTURE )
69     {
70         intf_ErrMsg("error: subpicture %p has invalid status %d", p_subpic,
71                     p_subpic->i_status );
72     }
73 #endif
74
75     /* Remove reservation flag */
76     p_subpic->i_status = READY_SUBPICTURE;
77
78 #ifdef TRACE_VOUT
79     /* Send subpicture information */
80     intf_DbgMsg("subpicture %p: type=%d, begin date=%s, end date=%s",
81                 p_subpic, p_subpic->i_type,
82                 mstrtime( psz_start, p_subpic->i_start ),
83                 mstrtime( psz_stop, p_subpic->i_stop ) );
84 #endif
85 }
86
87 /*****************************************************************************
88  * vout_CreateSubPicture: allocate a subpicture in the video output heap.
89  *****************************************************************************
90  * This function create a reserved subpicture in the video output heap.
91  * A null pointer is returned if the function fails. This method provides an
92  * already allocated zone of memory in the spu data fields. It needs locking
93  * since several pictures can be created by several producers threads.
94  *****************************************************************************/
95 subpicture_t *vout_CreateSubPicture( vout_thread_t *p_vout, int i_type,
96                                      int i_size )
97 {
98     int                 i_subpic;                        /* subpicture index */
99     subpicture_t *      p_free_subpic = NULL;       /* first free subpicture */
100     subpicture_t *      p_destroyed_subpic = NULL; /* first destroyed subpic */
101
102     /* Get lock */
103     vlc_mutex_lock( &p_vout->subpicture_lock );
104
105     /*
106      * Look for an empty place
107      */
108     for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
109     {
110         if( p_vout->p_subpicture[i_subpic].i_status == DESTROYED_SUBPICTURE )
111         {
112             /* Subpicture is marked for destruction, but is still allocated */
113             if( (p_vout->p_subpicture[i_subpic].i_type  == i_type)   &&
114                 (p_vout->p_subpicture[i_subpic].i_size  >= i_size) )
115             {
116                 /* Memory size do match or is smaller : memory will not be
117                  * reallocated, and function can end immediately - this is
118                  * the best possible case, since no memory allocation needs
119                  * to be done */
120                 p_vout->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
121 #ifdef TRACE_VOUT
122                 intf_DbgMsg("subpicture %p (in destroyed subpicture slot)",
123                             &p_vout->p_subpicture[i_subpic] );
124 #endif
125                 vlc_mutex_unlock( &p_vout->subpicture_lock );
126                 return( &p_vout->p_subpicture[i_subpic] );
127             }
128             else if( p_destroyed_subpic == NULL )
129             {
130                 /* Memory size do not match, but subpicture index will be kept
131                  * in case we find no other place */
132                 p_destroyed_subpic = &p_vout->p_subpicture[i_subpic];
133             }
134         }
135         else if( (p_free_subpic == NULL) &&
136                  (p_vout->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE ))
137         {
138             /* Subpicture is empty and ready for allocation */
139             p_free_subpic = &p_vout->p_subpicture[i_subpic];
140         }
141     }
142
143     /* If no free subpictures are available, use a destroyed subpicture */
144     if( (p_free_subpic == NULL) && (p_destroyed_subpic != NULL ) )
145     {
146         /* No free subpicture or matching destroyed subpictures have been
147          * found, but a destroyed subpicture is still avalaible */
148         free( p_destroyed_subpic->p_data );
149         p_free_subpic = p_destroyed_subpic;
150     }
151
152     /*
153      * Prepare subpicture
154      */
155     if( p_free_subpic != NULL )
156     {
157         /* Allocate memory */
158         switch( i_type )
159         {
160         case TEXT_SUBPICTURE:                             /* text subpicture */
161             p_free_subpic->p_data = memalign( 16, i_size + 1 );
162             break;
163         case DVD_SUBPICTURE:                          /* DVD subpicture unit */
164             p_free_subpic->p_data = memalign( 16, i_size );
165             break;
166 #ifdef DEBUG
167         default:
168             intf_ErrMsg("error: unknown subpicture type %d", i_type );
169             p_free_subpic->p_data   =  NULL;
170             break;
171 #endif
172         }
173
174         if( p_free_subpic->p_data != NULL )
175         {
176             /* Copy subpicture information, set some default values */
177             p_free_subpic->i_type                      = i_type;
178             p_free_subpic->i_status                    = RESERVED_SUBPICTURE;
179             p_free_subpic->i_size                      = i_size;
180             p_free_subpic->i_x                         = 0;
181             p_free_subpic->i_y                         = 0;
182             p_free_subpic->i_width                     = 0;
183             p_free_subpic->i_height                    = 0;
184             p_free_subpic->i_horizontal_align          = CENTER_RALIGN;
185             p_free_subpic->i_vertical_align            = CENTER_RALIGN;
186         }
187         else
188         {
189             /* Memory allocation failed : set subpicture as empty */
190             p_free_subpic->i_type   =  EMPTY_SUBPICTURE;
191             p_free_subpic->i_status =  FREE_SUBPICTURE;
192             p_free_subpic =            NULL;
193             intf_ErrMsg( "vout error: spu allocation returned %s",
194                          strerror( ENOMEM ) );
195         }
196
197 #ifdef TRACE_VOUT
198         intf_DbgMsg("subpicture %p (in free subpicture slot)", p_free_subpic );
199 #endif
200         vlc_mutex_unlock( &p_vout->subpicture_lock );
201         return( p_free_subpic );
202     }
203
204     /* No free or destroyed subpicture could be found */
205     intf_DbgMsg( "warning: subpicture heap is full" );
206     vlc_mutex_unlock( &p_vout->subpicture_lock );
207     return( NULL );
208 }
209
210 /*****************************************************************************
211  * vout_DestroySubPicture: remove a subpicture from the heap
212  *****************************************************************************
213  * This function frees a previously reserved subpicture.
214  * It is meant to be used when the construction of a picture aborted.
215  * This function does not need locking since reserved subpictures are ignored
216  * by the output thread.
217  *****************************************************************************/
218 void vout_DestroySubPicture( vout_thread_t *p_vout, subpicture_t *p_subpic )
219 {
220 #ifdef DEBUG
221    /* Check if status is valid */
222    if( p_subpic->i_status != RESERVED_SUBPICTURE )
223    {
224        intf_ErrMsg("error: subpicture %p has invalid status %d",
225                    p_subpic, p_subpic->i_status );
226    }
227 #endif
228
229     p_subpic->i_status = DESTROYED_SUBPICTURE;
230
231 #ifdef TRACE_VOUT
232     intf_DbgMsg("subpicture %p", p_subpic);
233 #endif
234 }
235
236 /*****************************************************************************
237  * vout_RenderSubPictures: render a subpicture list
238  *****************************************************************************
239  * This function renders a sub picture unit.
240  *****************************************************************************/
241 void vout_RenderSubPictures( picture_t *p_pic, subpicture_t *p_subpic )
242 {
243 #if 0
244     p_vout_font_t       p_font;                                 /* text font */
245     int                 i_width, i_height;          /* subpicture dimensions */
246 #endif
247
248     while( p_subpic != NULL )
249     {
250         switch( p_subpic->i_type )
251         {
252         case DVD_SUBPICTURE:                          /* DVD subpicture unit */
253             vout_RenderRGBSPU( p_pic, p_subpic );
254             vout_RenderYUVSPU( p_pic, p_subpic );
255             break;
256
257 #if 0
258         case TEXT_SUBPICTURE:                            /* single line text */
259             /* Select default font if not specified */
260             p_font = p_subpic->type.text.p_font;
261             if( p_font == NULL )
262             {
263                 p_font = p_vout->p_default_font;
264             }
265
266             /* Compute text size (width and height fields are ignored)
267              * and print it */
268             vout_TextSize( p_font, p_subpic->type.text.i_style,
269                            p_subpic->p_data, &i_width, &i_height );
270             if( !Align( p_vout, &p_subpic->i_x, &p_subpic->i_y,
271                         i_width, i_height, p_subpic->i_horizontal_align,
272                         p_subpic->i_vertical_align ) )
273             {
274                 vout_Print( p_font,
275                             p_vout->p_buffer[ p_vout->i_buffer_index ].p_data +
276                             p_subpic->i_x * p_vout->i_bytes_per_pixel +
277                             p_subpic->i_y * p_vout->i_bytes_per_line,
278                             p_vout->i_bytes_per_pixel, p_vout->i_bytes_per_line,                            p_subpic->type.text.i_char_color,
279                             p_subpic->type.text.i_border_color,
280                             p_subpic->type.text.i_bg_color,
281                             p_subpic->type.text.i_style, p_subpic->p_data, 100 );
282                 SetBufferArea( p_vout, p_subpic->i_x, p_subpic->i_y,
283                                i_width, i_height );
284             }
285             break;
286 #endif
287
288         default:
289 #ifdef DEBUG
290             intf_ErrMsg( "error: unknown subpicture %p type %d",
291                          p_subpic, p_subpic->i_type );
292 #endif
293             break;
294         }
295
296         p_subpic = p_subpic->p_next;
297     }
298 }
299
300 /*****************************************************************************
301  * vout_SortSubPictures: find the subpictures to display
302  *****************************************************************************
303  * This function parses all subpictures and decides which ones need to be
304  * displayed. This operation does not need lock, since only READY_SUBPICTURE
305  * are handled. If no picture has been selected, display_date will depend on
306  * the subpicture.
307  * We also check for ephemer DVD subpictures (subpictures that have
308  * to be removed if a newer one is available), which makes it a lot
309  * more difficult to guess if a subpicture has to be rendered or not.
310  *****************************************************************************/
311 subpicture_t *vout_SortSubPictures( vout_thread_t *p_vout,
312                                     mtime_t display_date )
313 {
314     int i_index;
315     subpicture_t *p_subpic     = NULL;
316     subpicture_t *p_ephemer    = NULL;
317     mtime_t       ephemer_date = 0;
318
319     /* We get an easily parsable chained list of subpictures which
320      * ends with NULL since p_subpic was initialized to NULL. */
321     for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
322     {
323         if( p_vout->p_subpicture[i_index].i_status == READY_SUBPICTURE )
324         {
325             /* If it is a DVD subpicture, check its date */
326             if( p_vout->p_subpicture[i_index].i_type == DVD_SUBPICTURE )
327             {
328                 if( display_date > p_vout->p_subpicture[i_index].i_stop )
329                 {
330                     /* Too late, destroy the subpic */
331                     vout_DestroySubPicture( p_vout,
332                                     &p_vout->p_subpicture[i_index] );
333                     continue;
334                 }
335
336                 if( display_date < p_vout->p_subpicture[i_index].i_start )
337                 {
338                     /* Too early, come back next monday */
339                     continue;
340                 }
341
342                 /* If this is an ephemer subpic, see if it's the
343                  * youngest we have */
344                 if( p_vout->p_subpicture[i_index].b_ephemer )
345                 {
346                     if( p_ephemer == NULL )
347                     {
348                         p_ephemer = &p_vout->p_subpicture[i_index];
349                         continue;
350                     }
351
352                     if( p_vout->p_subpicture[i_index].i_start
353                                                      < p_ephemer->i_start )
354                     {
355                         /* Link the previous ephemer subpicture and
356                          * replace it with the current one */
357                         p_ephemer->p_next = p_subpic;
358                         p_subpic = p_ephemer;
359                         p_ephemer = &p_vout->p_subpicture[i_index];
360
361                         /* If it's the 2nd youngest subpicture,
362                          * register its date */
363                         if( !ephemer_date
364                               || ephemer_date > p_subpic->i_start )
365                         {
366                             ephemer_date = p_subpic->i_start;
367                         }
368
369                         continue;
370                     }
371                 }
372
373                 p_vout->p_subpicture[i_index].p_next = p_subpic;
374                 p_subpic = &p_vout->p_subpicture[i_index];
375
376                 /* If it's the 2nd youngest subpicture, register its date */                    if( !ephemer_date || ephemer_date > p_subpic->i_start )
377                 {
378                     ephemer_date = p_subpic->i_start;
379                 }
380             }
381             /* If it's not a DVD subpicture, just register it */
382             else
383             {
384                 p_vout->p_subpicture[i_index].p_next = p_subpic;
385                 p_subpic = &p_vout->p_subpicture[i_index];
386             }
387         }
388     }
389
390     /* If we found an ephemer subpicture, check if it has to be
391      * displayed */
392     if( p_ephemer != NULL )
393     {
394         if( p_ephemer->i_start < ephemer_date )
395         {
396             /* Ephemer subpicture has lived too long */
397             vout_DestroySubPicture( p_vout, p_ephemer );
398         }
399         else
400         {
401             /* Ephemer subpicture can still live a bit */
402             p_ephemer->p_next = p_subpic;
403             return p_ephemer;
404         }
405     }
406
407     return p_subpic;
408 }
409
410 /*****************************************************************************
411  * vout_RenderRGBSPU: draw an SPU on a picture
412  *****************************************************************************
413  * This is a fast implementation of the subpicture drawing code. The data
414  * has been preprocessed once in spu_decoder.c, so we don't need to parse the
415  * RLE buffer again and again. Most sanity checks are done in spu_decoder.c
416  * so that this routine can be as fast as possible.
417  *****************************************************************************/
418 static void vout_RenderRGBSPU( picture_t *p_pic, 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( picture_t *p_pic, const subpicture_t *p_spu )
526 {
527     int  i_len, i_color;
528     u16 *p_source = (u16 *)p_spu->p_data;
529
530     int i_x, i_y;
531
532     u8 *p_dest = p_pic->planes[ Y_PLANE ].p_data + p_spu->i_x + p_spu->i_width
533                         + p_pic->i_width * ( p_spu->i_y + p_spu->i_height );
534
535     /* Draw until we reach the bottom of the subtitle */
536     i_y = p_spu->i_height * p_pic->i_width;
537
538     while( i_y )
539     {
540         /* Draw until we reach the end of the line */
541         i_x = p_spu->i_width;
542
543         while( i_x )
544         {
545             /* Draw the line if needed */
546             i_color = *p_source & 0x3;
547
548             if( i_color )
549             {
550                 i_len = *p_source++ >> 2;
551                 memset( p_dest - i_x - i_y, p_palette[ i_color ], i_len );
552                 i_x -= i_len;
553                 continue;
554             }
555
556             i_x -= *p_source++ >> 2;
557         }
558
559         i_y -= p_pic->i_width;
560     }
561 }
562