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