1 /*****************************************************************************
2 * Common SVCD and CVD subtitle routines.
3 *****************************************************************************
4 * Copyright (C) 2003, 2004 VideoLAN
7 * Author: Rocky Bernstein <rocky@panix.com>
9 * Julio Sanchez Fernandez (http://subhandler.sourceforge.net)
10 * Samuel Hocevar <sam@zoy.org>
11 * Laurent Aimar <fenrir@via.ecp.fr>
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.
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.
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 *****************************************************************************/
28 /*****************************************************************************
30 *****************************************************************************/
33 #include <vlc/decoder.h>
39 #include "write_png.h"
42 /*****************************************************************************
43 Free Resources associated with subtitle packet.
44 *****************************************************************************/
45 void VCDSubClose( vlc_object_t *p_this )
47 decoder_t *p_dec = (decoder_t*)p_this;
48 decoder_sys_t *p_sys = p_dec->p_sys;
50 dbg_print( (DECODE_DBG_CALL|DECODE_DBG_EXT) , "");
52 if( !p_sys->b_packetizer && p_sys->p_vout )
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 );
61 block_ChainRelease( p_sys->p_block );
64 free(p_sys->subtitle_data);
68 /*****************************************************************************
70 Initialize so the next packet will start off a new one.
72 *****************************************************************************/
74 VCDSubInitSubtitleBlock( decoder_sys_t * p_sys )
76 p_sys->i_spu_size = 0;
77 p_sys->state = SUBTITLE_BLOCK_EMPTY;
79 p_sys->p_block = NULL;
80 p_sys->subtitle_data_pos = 0;
85 VCDSubInitSubtitleData(decoder_sys_t *p_sys)
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,
91 p_sys->subtitle_data_size = p_sys->i_spu_size;
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 */
99 p_sys->subtitle_data_pos = 0;
103 VCDSubAppendData ( decoder_t *p_dec, uint8_t *buffer, uint32_t buf_len )
105 decoder_sys_t *p_sys = p_dec->p_sys;
106 int chunk_length = buf_len;
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 );
112 chunk_length = p_sys->i_spu_size - p_sys->subtitle_data_pos;
115 if ( chunk_length > 0 ) {
119 for (i=0; i<chunk_length; i++)
120 printf ("%02x", b[i]);
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);
133 /*****************************************************************************
134 * FindVout: Find a vout or wait for one to be created.
135 *****************************************************************************/
136 vout_thread_t *VCDSubFindVout( decoder_t *p_dec )
138 vout_thread_t *p_vout = NULL;
140 /* Find an available video output */
143 if( p_dec->b_die || p_dec->b_error )
148 p_vout = vlc_object_find( p_dec, VLC_OBJECT_VOUT, FIND_ANYWHERE );
154 msleep( VOUT_OUTMEM_SLEEP );
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.
169 InlinePalette ( /*inout*/ uint8_t *p_dest, decoder_sys_t *p_sys )
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;
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]];*/
184 Check to see if user has overridden subtitle aspect ratio.
185 0 is returned for no override which means just counteract any
189 VCDSubGetAROverride(vlc_object_t * p_input, vout_thread_t *p_vout)
191 char *psz_string = config_GetPsz( p_input, MODULE_STRING "-aspect-ratio" );
193 /* Check whether the user tried to override aspect ratio */
194 if( !psz_string ) return 0;
197 unsigned int i_new_aspect = 0;
198 char *psz_parser = strchr( psz_string, ':' );
202 *psz_parser++ = '\0';
203 i_new_aspect = atoi( psz_string ) * VOUT_ASPECT_FACTOR
204 / atoi( psz_parser );
208 i_new_aspect = p_vout->output.i_width * VOUT_ASPECT_FACTOR
210 / p_vout->output.i_height;
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
223 The aspect ratio is assumed to be between 1/2 and 1.
225 Note: the scaling truncates the new width rather than rounds it.
226 Perhaps something one might want to address.
229 VCDSubScaleX( decoder_t *p_dec, subpicture_t *p_spu,
230 unsigned int i_scale_x, unsigned int i_scale_y )
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. */
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);
245 if (! (i_scale_x < i_scale_y && i_scale_y < i_scale_x+i_scale_x) )
247 msg_Warn( p_dec, "Need x < y < 2x. x: %i, y: %i", i_scale_x, i_scale_y );
251 for ( i_row=0; i_row <= p_spu->i_height - 1; i_row++ ) {
254 /* Discard the remaining piece of the column of the previous line*/
257 p_src2 += PIXEL_SIZE;
260 for ( i_col=0; i_col <= p_spu->i_width - 2; i_col++ ) {
262 unsigned int w1= i_scale_x - i_used;
265 if ( i_scale_y - w1 <= i_scale_x ) {
266 /* Average spans 2 pixels. */
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++;
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;
280 p_src2 += PIXEL_SIZE;
282 for (i = 0; i < PIXEL_SIZE; i++ ) {
283 *p_dst = ( (*p_src0 * w0) + (*p_src1 * w1) + (*p_src2 * w2) )
285 p_src0++; p_src1++; p_src2++; p_dst++;
292 if (i_scale_x == i_used) {
293 /* End of last pixel was end of p_src2. */
295 p_src2 += PIXEL_SIZE;
301 p_spu->i_width = i_new_width;
303 if ( p_sys && p_sys->i_debug & DECODE_DBG_TRANSFORM )
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);
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
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.
327 We also expand palette entries here, unless we are dealing with a
328 palettized chroma (e.g. RGB2).
332 VCDSubHandleScaling( subpicture_t *p_spu, decoder_t *p_dec )
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,
337 int i_aspect_x, i_aspect_y;
338 uint8_t *p_dest = (uint8_t *)p_spu->p_sys->p_data;
341 /* Check for user-configuration override. */
342 unsigned int i_new_aspect;
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.
352 InlinePalette( p_dest, p_dec->p_sys );
353 i_new_aspect = VCDSubGetAROverride( p_input, p_vout );
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.
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.
366 switch( p_vout->output.i_chroma )
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'):
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'):
387 msg_Err( p_vout, "unknown chroma %x",
388 p_vout->output.i_chroma );
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 );
396 /* User knows best? */
397 vlc_reduce( &i_aspect_x, &i_aspect_y, p_vout->render.i_aspect,
398 VOUT_ASPECT_FACTOR, 0 );
400 VCDSubScaleX( p_dec, p_spu, i_aspect_x, i_aspect_y );
407 * DestroySPU: subpicture destructor
409 void VCDSubDestroySPU( subpicture_t *p_spu )
411 if( p_spu->p_sys->p_input )
413 /* Detach from our input thread */
414 vlc_object_release( p_spu->p_sys->p_input );
417 vlc_mutex_destroy( &p_spu->p_sys->lock );
418 free( p_spu->p_sys );
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 )
427 VCDSubUpdateSPU( (subpicture_t *)p_data, p_object );
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 )
443 p_spu->p_sys->b_crop = val.b_bool;
444 if( !p_spu->p_sys->b_crop )
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;
461 Dump an a subtitle image to standard output - for debugging.
463 void VCDSubDumpImage( uint8_t *p_image, uint32_t i_height, uint32_t i_width )
465 uint8_t *p = p_image;
466 unsigned int i_row; /* scanline row number */
467 unsigned int i_column; /* scanline column number */
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);
476 printf("\n-------------------------------------\n");
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)
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.
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 )
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];
506 dbg_print( (DECODE_DBG_CALL), "%s", filename);
508 if (NULL == image_data) return;
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 );
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 ];
528 write_png( filename, i_height, i_width, image_data, text_ptr, i_text_count );
531 #endif /*HAVE_LIBPNG*/
536 * c-file-style: "gnu"
538 * indent-tabs-mode: nil