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