* ALL: separation of the SPU engine from the VOUT.
[vlc.git] / modules / codec / ogt / common.c
1 /*****************************************************************************
2  * Common SVCD and CVD subtitle routines.
3  *****************************************************************************
4  * Copyright (C) 2003, 2004 VideoLAN
5  * $Id$
6  *
7  * Author: Rocky Bernstein <rocky@panix.com>
8  *   based on code from:
9  *       Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
10  *       Samuel Hocevar <sam@zoy.org>
11  *       Laurent Aimar <fenrir@via.ecp.fr>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <vlc/vlc.h>
32 #include <vlc/vout.h>
33 #include <vlc/decoder.h>
34
35 #include "subtitle.h"
36 #include "pixmap.h"
37 #include "common.h"
38 #ifdef HAVE_LIBPNG
39 #include "write_png.h"
40 #endif
41
42 /*****************************************************************************
43  Free Resources associated with subtitle packet.
44  *****************************************************************************/
45 void VCDSubClose( vlc_object_t *p_this )
46 {
47     decoder_t     *p_dec = (decoder_t*)p_this;
48     decoder_sys_t *p_sys = p_dec->p_sys;
49
50     dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
51
52     if( !p_sys->b_packetizer && p_sys->p_vout )
53     {
54         /* FIXME check if it's ok to not lock vout */
55         spu_Control( p_sys->p_vout->p_spu, SPU_CHANNEL_CLEAR,
56                      p_sys->i_subpic_channel );
57     }
58
59     if( p_sys->p_block )
60     {
61         block_ChainRelease( p_sys->p_block );
62     }
63
64     free(p_sys->subtitle_data);
65     free( p_sys );
66 }
67
68 /*****************************************************************************
69
70 Initialize so the next packet will start off a new one.
71
72  *****************************************************************************/
73 void 
74 VCDSubInitSubtitleBlock( decoder_sys_t * p_sys ) 
75 {
76   p_sys->i_spu_size = 0;
77   p_sys->state      = SUBTITLE_BLOCK_EMPTY;
78   p_sys->i_spu      = 0;
79   p_sys->p_block    = NULL;
80   p_sys->subtitle_data_pos = 0;
81
82 }
83
84 void 
85 VCDSubInitSubtitleData(decoder_sys_t *p_sys)
86 {
87   if ( p_sys->subtitle_data ) {
88     if ( p_sys->subtitle_data_size < p_sys->i_spu_size ) {
89       p_sys->subtitle_data = realloc(p_sys->subtitle_data,
90                                     p_sys->i_spu_size);
91       p_sys->subtitle_data_size = p_sys->i_spu_size;
92     }
93   } else {
94     p_sys->subtitle_data = malloc(p_sys->i_spu_size);
95     p_sys->subtitle_data_size = p_sys->i_spu_size;
96     /* FIXME: wrong place to get p_sys */
97     p_sys->i_image = 0;
98   }
99   p_sys->subtitle_data_pos = 0;
100 }
101
102 void 
103 VCDSubAppendData ( decoder_t *p_dec, uint8_t *buffer, uint32_t buf_len )
104 {
105   decoder_sys_t *p_sys = p_dec->p_sys;
106   int chunk_length = buf_len;
107
108   if ( chunk_length > p_sys->i_spu_size - p_sys->subtitle_data_pos ) {
109     msg_Warn( p_dec, "too much data (%d) expecting at most %u",
110               chunk_length, p_sys->i_spu_size - p_sys->subtitle_data_pos );
111
112     chunk_length = p_sys->i_spu_size - p_sys->subtitle_data_pos;
113   }
114
115   if ( chunk_length > 0 ) {
116 #if 0
117     int i;
118     int8_t *b=buffer;
119     for (i=0; i<chunk_length; i++)
120       printf ("%02x", b[i]);
121     printf("\n");
122 #endif
123     
124     memcpy(p_sys->subtitle_data + p_sys->subtitle_data_pos,
125            buffer, chunk_length);
126     p_sys->subtitle_data_pos += chunk_length;
127     dbg_print(DECODE_DBG_PACKET, "%d bytes appended, pointer now %d",
128               chunk_length, p_sys->subtitle_data_pos);
129   }
130 }
131
132
133 /*****************************************************************************
134  * FindVout: Find a vout or wait for one to be created.
135  *****************************************************************************/
136 vout_thread_t *VCDSubFindVout( decoder_t *p_dec )
137 {
138     vout_thread_t *p_vout = NULL;
139
140     /* Find an available video output */
141     do
142     {
143         if( p_dec->b_die || p_dec->b_error )
144         {
145             break;
146         }
147
148         p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
149         if( p_vout )
150         {
151             break;
152         }
153
154         msleep( VOUT_OUTMEM_SLEEP );
155     }
156     while( 1 );
157
158     return p_vout;
159 }
160
161
162
163 /**
164    Remove color palette by expanding pixel entries to contain the
165    palette values. We work from the free space at the end to the
166    beginning so we can expand inline.
167 */
168 static void
169 InlinePalette ( /*inout*/ uint8_t *p_dest, decoder_sys_t *p_sys )
170 {
171   const unsigned int i_width  = p_sys->i_width;
172   const unsigned int i_height = p_sys->i_height;
173   int n = (i_height * i_width) - 1;
174   uint8_t    *p_from = p_dest;
175   ogt_yuvt_t *p_to   = (ogt_yuvt_t *) p_dest;
176   
177   for ( ; n >= 0 ; n-- ) {
178     p_to[n] = p_sys->p_palette[p_from[n]];
179     /*p_to[n] = p_sys->p_palette[p_from[3]];*/
180   }
181 }
182
183 /**
184    Check to see if user has overridden subtitle aspect ratio. 
185    0 is returned for no override which means just counteract any
186    scaling effects.
187 */
188 unsigned int 
189 VCDSubGetAROverride(vlc_object_t * p_input, vout_thread_t *p_vout)
190 {
191   char *psz_string = config_GetPsz( p_input, MODULE_STRING "-aspect-ratio" );
192
193   /* Check whether the user tried to override aspect ratio */
194   if( !psz_string ) return 0;
195
196   {
197     unsigned int i_new_aspect = 0;
198     char *psz_parser = strchr( psz_string, ':' );
199     
200     if( psz_parser )
201       {
202         *psz_parser++ = '\0';
203         i_new_aspect = atoi( psz_string ) * VOUT_ASPECT_FACTOR
204           / atoi( psz_parser );
205       }
206     else
207       {
208         i_new_aspect = p_vout->output.i_width * VOUT_ASPECT_FACTOR
209           * atof( psz_string )
210           / p_vout->output.i_height;
211       }
212     
213     return i_new_aspect;
214   }
215 }
216
217
218 /**
219    Scales down (reduces size) of p_dest in the x direction as 
220    determined through aspect ratio x_scale by y_scale. Scaling
221    is done in place. p_spu->i_width, is updated to new width
222
223    The aspect ratio is assumed to be between 1/2 and 1.
224
225    Note: the scaling truncates the new width rather than rounds it.
226    Perhaps something one might want to address.
227 */
228 void
229 VCDSubScaleX( decoder_t *p_dec, subpicture_t *p_spu, 
230               unsigned int i_scale_x, unsigned int i_scale_y )
231 {
232   int i_row, i_col;
233
234   decoder_sys_t *p_sys = p_dec->p_sys;
235   uint8_t *p_src1 = p_spu->p_sys->p_data;
236   uint8_t *p_src2 = p_src1 + PIXEL_SIZE;
237   uint8_t *p_dst  = p_src1;
238   unsigned int i_new_width = (p_spu->i_width * i_scale_x) / i_scale_y ;
239   unsigned int i_used=0;  /* Number of bytes used up in p_src1. */
240
241   dbg_print( (DECODE_DBG_CALL|DECODE_DBG_TRANSFORM) , 
242              "aspect ratio %i:%i, Old width: %d, new width: %d", 
243              i_scale_x, i_scale_y, p_spu->i_width, i_new_width);
244
245   if (! (i_scale_x < i_scale_y && i_scale_y < i_scale_x+i_scale_x) )
246     {
247       msg_Warn( p_dec, "Need x < y < 2x. x: %i, y: %i", i_scale_x, i_scale_y );
248       return;
249     }
250   
251   for ( i_row=0; i_row <= p_spu->i_height - 1; i_row++ ) {
252
253     if (i_used != 0) {
254       /* Discard the remaining piece of the column of the previous line*/
255       i_used=0;
256       p_src1 = p_src2;
257       p_src2 += PIXEL_SIZE;
258     }
259     
260     for ( i_col=0; i_col <= p_spu->i_width - 2; i_col++ ) {
261       unsigned int i;
262       unsigned int w1= i_scale_x - i_used;
263       unsigned int w2;
264       
265       if ( i_scale_y - w1 <= i_scale_x ) {
266         /* Average spans 2 pixels. */
267         w2 = i_scale_y - w1;
268
269         for (i = 0; i < PIXEL_SIZE; i++ ) {
270           *p_dst = ( (*p_src1 * w1) + (*p_src2 * w2) ) / i_scale_y;
271           p_src1++; p_src2++; p_dst++;
272         }
273       } else {
274         /* Average spans 3 pixels. */
275         unsigned int w0 = w1;
276         unsigned int w1 = i_scale_x;
277         uint8_t *p_src0 = p_src1;
278         w2 = i_scale_y - w0 - w1;
279         p_src1 = p_src2;
280         p_src2 += PIXEL_SIZE;
281         
282         for (i = 0; i < PIXEL_SIZE; i++ ) {
283           *p_dst = ( (*p_src0 * w0) + (*p_src1 * w1) + (*p_src2 * w2) ) 
284                      / i_scale_y;
285           p_src0++; p_src1++; p_src2++; p_dst++;
286         }
287         i_col++;
288       }
289
290       i_used = w2;
291
292       if (i_scale_x == i_used) {
293         /* End of last pixel was end of p_src2. */
294         p_src1 = p_src2;
295         p_src2 += PIXEL_SIZE;
296         i_col++;
297         i_used = 0;
298       }
299     }
300   }
301   p_spu->i_width = i_new_width;
302
303   if ( p_sys && p_sys->i_debug & DECODE_DBG_TRANSFORM )
304   { 
305     ogt_yuvt_t *p_source = (ogt_yuvt_t *) p_spu->p_sys->p_data;
306     for ( i_row=0; i_row < p_spu->i_height; i_row++ ) {
307       for ( i_col=0; i_col < p_spu->i_width; i_col++ ) {
308         printf("%1x", p_source->s.t);
309         p_source++;
310       }
311       printf("\n");
312     }
313   }
314
315 }
316
317 /**
318    The video may be scaled. However subtitle bitmaps assume an 1:1
319    aspect ratio. So unless the user has specified otherwise, we
320    need to scale to compensate for or undo the effects of video
321    output scaling.
322    
323    Perhaps this should go in the Render routine? The advantage would
324    be that it will deal with a dynamically changing aspect ratio.
325    The downside is having to scale many times for each render call.
326
327    We also expand palette entries here, unless we are dealing with a 
328    palettized chroma (e.g. RGB2).
329 */
330
331 void 
332 VCDSubHandleScaling( subpicture_t *p_spu, decoder_t *p_dec )
333 {
334   vlc_object_t * p_input = p_spu->p_sys->p_input;
335   vout_thread_t *p_vout = vlc_object_find( p_input, VLC_OBJECT_VOUT, 
336                                            FIND_CHILD );
337   int i_aspect_x, i_aspect_y;
338   uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
339
340   if (p_vout) {
341     /* Check for user-configuration override. */
342     unsigned int i_new_aspect;
343     
344     if ( p_vout->output.i_chroma == VLC_FOURCC('R','G','B','2') ) {
345       /* This is an unscaled palettized format. We don't allow 
346          user scaling here. And to make the render process faster,
347          we don't expand the palette entries into a color value.
348        */
349       return;
350     }
351         
352     InlinePalette( p_dest, p_dec->p_sys );
353     i_new_aspect = VCDSubGetAROverride( p_input, p_vout );
354
355     if (i_new_aspect == VOUT_ASPECT_FACTOR) {
356       /* For scaling 1:1, nothing needs to be done. Note this means
357          subtitles will get scaled the same way the video does.
358       */
359       ;
360     } else {
361       if (0 == i_new_aspect) {
362         /* Counteract the effects of background video scaling when
363            there is scaling. That's why x and y are reversed from
364            the else branch in the call below.
365         */
366         switch( p_vout->output.i_chroma )
367           {
368             /* chromas in which scaling is done outside of our
369                blending routine, so we need to compensate for those
370                effects before blending gets called: */
371           case VLC_FOURCC('I','4','2','0'):
372           case VLC_FOURCC('I','Y','U','V'):
373           case VLC_FOURCC('Y','V','1','2'):
374           case VLC_FOURCC('Y','U','Y','2'):
375             break;
376             
377             /* chromas in which scaling is done in our blending 
378                routine and thus we don't do it here: */
379           case VLC_FOURCC('R','V','1','6'):
380           case VLC_FOURCC('R','V','2','4'):
381           case VLC_FOURCC('R','V','3','2'):
382           case VLC_FOURCC('R','G','B','2'):
383             return;
384             break;
385             
386           default:
387             msg_Err( p_vout, "unknown chroma %x", 
388                      p_vout->output.i_chroma );
389             return;
390             break;
391           }
392         /* We get here only for scaled chromas. */
393         vlc_reduce( &i_aspect_x, &i_aspect_y, p_vout->render.i_aspect,
394                     VOUT_ASPECT_FACTOR, 0 );
395       } else {
396         /* User knows best? */
397         vlc_reduce( &i_aspect_x, &i_aspect_y, p_vout->render.i_aspect,
398                     VOUT_ASPECT_FACTOR, 0 );
399       }
400       VCDSubScaleX( p_dec, p_spu, i_aspect_x, i_aspect_y );
401     }
402   }
403 }
404
405
406 /**
407  * DestroySPU: subpicture destructor
408  */
409 void VCDSubDestroySPU( subpicture_t *p_spu )
410 {
411     if( p_spu->p_sys->p_input )
412     {
413         /* Detach from our input thread */
414         vlc_object_release( p_spu->p_sys->p_input );
415     }
416
417     vlc_mutex_destroy( &p_spu->p_sys->lock );
418     free( p_spu->p_sys );
419 }
420
421 /*****************************************************************************
422   This callback is called from the input thread when we need cropping
423  *****************************************************************************/
424 int VCDSubCropCallback( vlc_object_t *p_object, char const *psz_var,
425                          vlc_value_t oldval, vlc_value_t newval, void *p_data )
426 {
427     VCDSubUpdateSPU( (subpicture_t *)p_data, p_object );
428
429     return VLC_SUCCESS;
430 }
431
432
433 /*****************************************************************************
434   update subpicture settings
435  *****************************************************************************
436   This function is called from CropCallback and at initialization time, to
437   retrieve crop information from the input.
438  *****************************************************************************/
439 void VCDSubUpdateSPU( subpicture_t *p_spu, vlc_object_t *p_object )
440 {
441     vlc_value_t val;
442
443     p_spu->p_sys->b_crop = val.b_bool;
444     if( !p_spu->p_sys->b_crop )
445     {
446         return;
447     }
448
449     if ( VLC_SUCCESS == var_Get( p_object, "x-start", &val ) )
450       p_spu->p_sys->i_x_start = val.i_int;
451     if ( VLC_SUCCESS == var_Get( p_object, "y-start", &val ) )
452       p_spu->p_sys->i_y_start = val.i_int;
453     if ( VLC_SUCCESS == var_Get( p_object, "x-end", &val ) )
454       p_spu->p_sys->i_x_end = val.i_int;
455     if ( VLC_SUCCESS == var_Get( p_object, "y-end", &val ) )
456       p_spu->p_sys->i_y_end = val.i_int;
457
458 }
459
460 /* 
461    Dump an a subtitle image to standard output - for debugging.
462  */
463 void VCDSubDumpImage( uint8_t *p_image, uint32_t i_height, uint32_t i_width )
464 {
465   uint8_t *p = p_image;
466   unsigned int i_row;    /* scanline row number */
467   unsigned int i_column; /* scanline column number */
468
469   printf("-------------------------------------\n++");
470   for ( i_row=0; i_row < i_height; i_row ++ ) {
471     for ( i_column=0; i_column<i_width; i_column++ ) {
472       printf("%1d", *p++ & 0x03);
473     }
474     printf("\n++");
475   }
476   printf("\n-------------------------------------\n");
477 }
478
479 #ifdef HAVE_LIBPNG
480
481 #define PALETTE_SIZE  4
482 /* Note the below assumes the above is a power of 2 */
483 #define PALETTE_SIZE_MASK (PALETTE_SIZE-1)
484
485 /* 
486    Dump an a subtitle image to a Portable Network Graphics (PNG) file.
487    All we do here is convert YUV palette entries to RGB, expand
488    the image into a linear RGB pixel array, and call the routine
489    that does the PNG writing.
490  */
491
492 void 
493 VCDSubDumpPNG( uint8_t *p_image, decoder_t *p_dec,
494                uint32_t i_height, uint32_t i_width, const char *filename,
495                png_text *text_ptr, int i_text_count )
496 {
497   decoder_sys_t *p_sys = p_dec->p_sys;
498   uint8_t *p = p_image;
499   uint8_t *image_data = malloc(RGB_SIZE * i_height * i_width );
500   uint8_t *q = image_data;
501   unsigned int i_row;    /* scanline row number */
502   unsigned int i_column; /* scanline column number */
503   uint8_t rgb_palette[PALETTE_SIZE * RGB_SIZE];
504   int i;
505
506   dbg_print( (DECODE_DBG_CALL), "%s", filename);
507   
508   if (NULL == image_data) return;
509
510   /* Convert palette YUV into RGB. */
511   for (i=0; i<PALETTE_SIZE; i++) {
512     ogt_yuvt_t *p_yuv     = &(p_sys->p_palette[i]);
513     uint8_t   *p_rgb_out  = &(rgb_palette[i*RGB_SIZE]);
514     yuv2rgb( p_yuv, p_rgb_out );
515   }
516   
517   /* Convert palette entries into linear RGB array. */
518   for ( i_row=0; i_row < i_height; i_row ++ ) {
519     for ( i_column=0; i_column<i_width; i_column++ ) {
520       uint8_t *p_rgb = &rgb_palette[ ((*p)&PALETTE_SIZE_MASK)*RGB_SIZE ];
521       *q++ = p_rgb[0];
522       *q++ = p_rgb[1];
523       *q++ = p_rgb[2];
524       p++;
525     }
526   }
527   
528   write_png( filename, i_height, i_width, image_data, text_ptr, i_text_count );
529   free(image_data);
530 }
531 #endif /*HAVE_LIBPNG*/
532
533 \f
534 /* 
535  * Local variables:
536  *  c-file-style: "gnu"
537  *  tab-width: 8
538  *  indent-tabs-mode: nil
539  * End:
540  */