00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027 #include <stdlib.h>
00028 #include <string.h>
00029
00030 #include <sys/types.h>
00031 #include <sys/stat.h>
00032 #include <unistd.h>
00033
00034 #include <vlc/vlc.h>
00035 #include <vlc/vout.h>
00036 #include "vlc_osd.h"
00037 #include "vlc_block.h"
00038 #include "vlc_filter.h"
00039
00040 #include <librsvg-2/librsvg/rsvg.h>
00041
00042 typedef struct svg_rendition_t svg_rendition_t;
00043
00044
00045
00046
00047 static int Create ( vlc_object_t * );
00048 static void Destroy ( vlc_object_t * );
00049 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
00050 subpicture_region_t *p_region_in );
00051
00052
00053
00054
00055
00056
00057 #define TEMPLATE_TEXT N_( "SVG template file" )
00058 #define TEMPLATE_LONGTEXT N_( "Location of a file holding a SVG template for automatic string conversion" )
00059
00060 vlc_module_begin();
00061 set_category( CAT_INPUT);
00062 set_category( SUBCAT_INPUT_SCODEC );
00063 set_capability( "text renderer", 101 );
00064 add_shortcut( "svg" );
00065 add_string( "svg-template-file", "", NULL, TEMPLATE_TEXT, TEMPLATE_LONGTEXT, VLC_TRUE );
00066 set_callbacks( Create, Destroy );
00067 vlc_module_end();
00068
00072 struct svg_rendition_t
00073 {
00074 int i_width;
00075 int i_height;
00076 int i_chroma;
00078 byte_t *psz_text;
00079
00080 GdkPixbuf *p_rendition;
00081 };
00082
00083 static int Render( filter_t *, subpicture_region_t *, svg_rendition_t *, int, int);
00084 static byte_t *svg_GetTemplate ();
00085 static void svg_set_size( filter_t *p_filter, int width, int height );
00086 static void svg_SizeCallback ( int *width, int *height, gpointer data );
00087 static void svg_RenderPicture ( filter_t *p_filter,
00088 svg_rendition_t *p_svg );
00089 static void FreeString( svg_rendition_t * );
00090
00091
00092
00093
00094
00095
00096
00097 struct filter_sys_t
00098 {
00099
00100 byte_t *psz_template;
00101
00102 int i_width;
00103 int i_height;
00104 vlc_mutex_t *lock;
00105 };
00106
00107
00108
00109
00110
00111
00112 static int Create( vlc_object_t *p_this )
00113 {
00114 filter_t *p_filter = (filter_t *)p_this;
00115 filter_sys_t *p_sys;
00116
00117
00118 p_sys = malloc( sizeof( filter_sys_t ) );
00119 if( !p_sys )
00120 {
00121 msg_Err( p_filter, "Out of memory" );
00122 return VLC_ENOMEM;
00123 }
00124
00125
00126 p_sys->psz_template = svg_GetTemplate( p_this );
00127 if( !p_sys->psz_template )
00128 {
00129 msg_Err( p_filter, "Out of memory" );
00130 return VLC_ENOMEM;
00131 }
00132
00133 p_sys->i_width = p_filter->fmt_out.video.i_width;
00134 p_sys->i_height = p_filter->fmt_out.video.i_height;
00135
00136 p_filter->pf_render_text = RenderText;
00137 p_filter->p_sys = p_sys;
00138
00139
00140 g_type_init ();
00141
00142 return VLC_SUCCESS;
00143 }
00144
00145 static byte_t *svg_GetTemplate( vlc_object_t *p_this )
00146 {
00147 filter_t *p_filter = (filter_t *)p_this;
00148 char *psz_filename;
00149 char *psz_template;
00150 FILE *file;
00151
00152 psz_filename = config_GetPsz( p_filter, "svg-template-file" );
00153 if( !psz_filename || psz_filename[0] == 0 )
00154 {
00155
00156 psz_template = NULL;
00157 }
00158 else
00159 {
00160
00161 file = fopen( psz_filename, "rt" );
00162 if( !file )
00163 {
00164 msg_Warn( p_this, "SVG template file %s does not exist.", psz_filename );
00165 psz_template = NULL;
00166 }
00167 else
00168 {
00169 struct stat s;
00170 int i_ret;
00171
00172 i_ret = lstat( psz_filename, &s );
00173 if( i_ret )
00174 {
00175
00176
00177 psz_template = NULL;
00178 }
00179 else
00180 {
00181 msg_Dbg( p_this, "Reading %ld bytes from template %s\n", (long)s.st_size, psz_filename );
00182
00183 psz_template = malloc( s.st_size + 42 );
00184 if( !psz_template )
00185 {
00186 msg_Err( p_filter, "Out of memory" );
00187 return NULL;
00188 }
00189 memset( psz_template, 0, s.st_size + 1 );
00190 fread( psz_template, s.st_size, 1, file );
00191 fclose( file );
00192 }
00193 }
00194 }
00195 if( !psz_template )
00196 {
00197
00198
00199 psz_template = strdup( "<?xml version='1.0' encoding='UTF-8' standalone='no'?> \
00200 <svg version='1' preserveAspectRatio='xMinYMin meet' viewBox='0 0 800 600'> \
00201 <text x='10' y='560' fill='white' font-size='32' \
00202 font-family='sans-serif'>%s</text></svg>" );
00203 }
00204
00205 return psz_template;
00206 }
00207
00208
00209
00210
00211
00212
00213 static void Destroy( vlc_object_t *p_this )
00214 {
00215 filter_t *p_filter = (filter_t *)p_this;
00216 filter_sys_t *p_sys = p_filter->p_sys;
00217
00218 free( p_sys->psz_template );
00219 free( p_sys );
00220 }
00221
00222
00223
00224
00225 static int Render( filter_t *p_filter, subpicture_region_t *p_region,
00226 svg_rendition_t *p_svg, int i_width, int i_height )
00227 {
00228 video_format_t fmt;
00229 uint8_t *p_y, *p_u, *p_v, *p_a;
00230 int x, y, i_pitch, i_u_pitch;
00231 guchar *pixels_in = NULL;
00232 int rowstride_in;
00233 int channels_in;
00234 int alpha;
00235 picture_t *p_pic;
00236 subpicture_region_t *p_region_tmp;
00237
00238 if ( p_filter->p_sys->i_width != i_width ||
00239 p_filter->p_sys->i_height != i_height )
00240 {
00241 svg_set_size( p_filter, i_width, i_height );
00242 p_svg->p_rendition = NULL;
00243 }
00244
00245 if( p_svg->p_rendition == NULL ) {
00246 svg_RenderPicture( p_filter, p_svg );
00247
00248
00249 }
00250 i_width = gdk_pixbuf_get_width( p_svg->p_rendition );
00251 i_height = gdk_pixbuf_get_height( p_svg->p_rendition );
00252
00253
00254 memset( &fmt, 0, sizeof(video_format_t) );
00255 fmt.i_chroma = VLC_FOURCC('Y','U','V','A');
00256 fmt.i_aspect = VOUT_ASPECT_FACTOR;
00257 fmt.i_width = fmt.i_visible_width = i_width;
00258 fmt.i_height = fmt.i_visible_height = i_height;
00259 fmt.i_x_offset = fmt.i_y_offset = 0;
00260 p_region_tmp = spu_CreateRegion( p_filter, &fmt );
00261 if( !p_region_tmp )
00262 {
00263 msg_Err( p_filter, "cannot allocate SPU region" );
00264 return VLC_EGENERIC;
00265 }
00266 p_region->fmt = p_region_tmp->fmt;
00267 p_region->picture = p_region_tmp->picture;
00268 free( p_region_tmp );
00269
00270 p_region->i_x = p_region->i_y = 0;
00271 p_y = p_region->picture.Y_PIXELS;
00272 p_u = p_region->picture.U_PIXELS;
00273 p_v = p_region->picture.V_PIXELS;
00274 p_a = p_region->picture.A_PIXELS;
00275
00276 i_pitch = p_region->picture.Y_PITCH;
00277 i_u_pitch = p_region->picture.U_PITCH;
00278
00279
00280 memset( p_y, 0x00, i_pitch * p_region->fmt.i_height );
00281 memset( p_u, 0x80, i_u_pitch * p_region->fmt.i_height );
00282 memset( p_v, 0x80, i_u_pitch * p_region->fmt.i_height );
00283
00284 p_pic = &(p_region->picture);
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306 pixels_in = gdk_pixbuf_get_pixels( p_svg->p_rendition );
00307 rowstride_in = gdk_pixbuf_get_rowstride( p_svg->p_rendition );
00308 channels_in = gdk_pixbuf_get_n_channels( p_svg->p_rendition );
00309 alpha = gdk_pixbuf_get_has_alpha( p_svg->p_rendition );
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319 #define INDEX_IN( x, y ) ( y * rowstride_in + x * channels_in )
00320 #define INDEX_OUT( x, y ) ( y * i_pitch + x * p_pic->p[Y_PLANE].i_pixel_pitch )
00321
00322 for( y = 0; y < i_height; y++ )
00323 {
00324 for( x = 0; x < i_width; x++ )
00325 {
00326 guchar *p_in;
00327 int i_out;
00328
00329 p_in = &pixels_in[INDEX_IN( x, y )];
00330
00331 #define R( pixel ) *pixel
00332 #define G( pixel ) *( pixel+1 )
00333 #define B( pixel ) *( pixel+2 )
00334 #define ALPHA( pixel ) *( pixel+3 )
00335
00336
00337
00338
00339
00340
00341 if ( alpha ) {
00342 i_out = INDEX_OUT( x, y );
00343
00344 p_pic->Y_PIXELS[i_out] = .299 * R( p_in ) + .587 * G( p_in ) + .114 * B( p_in );
00345
00346 p_pic->U_PIXELS[i_out] = -.1687 * R( p_in ) - .3313 * G( p_in ) + .5 * B( p_in ) + 128;
00347 p_pic->V_PIXELS[i_out] = .5 * R( p_in ) - .4187 * G( p_in ) - .0813 * B( p_in ) + 128;
00348
00349 p_pic->A_PIXELS[i_out] = ALPHA( p_in );
00350 }
00351 }
00352 }
00353
00354 return VLC_SUCCESS;
00355 }
00356
00357 static void svg_set_size( filter_t *p_filter, int width, int height )
00358 {
00359 p_filter->p_sys->i_width = width;
00360 p_filter->p_sys->i_height = height;
00361 }
00362
00363 static void svg_SizeCallback( int *width, int *height, gpointer data )
00364 {
00365 filter_t *p_filter = data;
00366
00367 *width = p_filter->p_sys->i_width;
00368 *height = p_filter->p_sys->i_height;
00369 return;
00370 }
00371
00372 static void svg_RenderPicture( filter_t *p_filter,
00373 svg_rendition_t *p_svg )
00374 {
00375
00376
00377 RsvgHandle *p_handle;
00378 GError *error = NULL;
00379
00380 p_handle = rsvg_handle_new();
00381
00382 rsvg_handle_set_size_callback( p_handle, svg_SizeCallback, p_filter, NULL );
00383
00384 rsvg_handle_write( p_handle,
00385 p_svg->psz_text, strlen( p_svg->psz_text ) + 1,
00386 &error );
00387 if( error != NULL )
00388 {
00389 msg_Err( p_filter, "Error in handle_write: %s\n", error->message );
00390 return;
00391 }
00392 rsvg_handle_close( p_handle, &error );
00393
00394 p_svg->p_rendition = rsvg_handle_get_pixbuf( p_handle );
00395 rsvg_handle_free( p_handle );
00396 }
00397
00398
00399 static int RenderText( filter_t *p_filter, subpicture_region_t *p_region_out,
00400 subpicture_region_t *p_region_in )
00401 {
00402 filter_sys_t *p_sys = p_filter->p_sys;
00403 svg_rendition_t *p_svg = NULL;
00404 char *psz_string;
00405
00406
00407 if( !p_region_in || !p_region_out ) return VLC_EGENERIC;
00408 psz_string = p_region_in->psz_text;
00409 if( !psz_string || !*psz_string ) return VLC_EGENERIC;
00410
00411 p_svg = ( svg_rendition_t * )malloc( sizeof( svg_rendition_t ) );
00412 if( !p_svg )
00413 {
00414 msg_Err( p_filter, "Out of memory" );
00415 return VLC_ENOMEM;
00416 }
00417
00418 p_region_out->i_x = p_region_in->i_x;
00419 p_region_out->i_y = p_region_in->i_y;
00420
00421
00422
00423 if( strstr( psz_string, "<svg" ))
00424 {
00425
00426 p_svg->psz_text = strdup( psz_string );
00427 if( !p_svg->psz_text )
00428 {
00429 msg_Err( p_filter, "Out of memory" );
00430 free( p_svg );
00431 return VLC_ENOMEM;
00432 }
00433 }
00434 else
00435 {
00436
00437 int length;
00438 byte_t* psz_template = p_sys->psz_template;
00439 length = strlen( psz_string ) + strlen( psz_template ) + 42;
00440 p_svg->psz_text = malloc( length + 1 );
00441 if( !p_svg->psz_text )
00442 {
00443 msg_Err( p_filter, "Out of memory" );
00444 free( p_svg );
00445 return VLC_ENOMEM;
00446 }
00447 memset( p_svg->psz_text, 0, length + 1 );
00448 snprintf( p_svg->psz_text, length, psz_template, psz_string );
00449 }
00450 p_svg->i_width = p_sys->i_width;
00451 p_svg->i_height = p_sys->i_height;
00452 p_svg->i_chroma = VLC_FOURCC('Y','U','V','A');
00453
00454
00455
00456
00457 svg_RenderPicture( p_filter, p_svg );
00458
00459 Render( p_filter, p_region_out, p_svg, p_svg->i_width, p_svg->i_height );
00460 FreeString( p_svg );
00461
00462 return VLC_SUCCESS;
00463 }
00464
00465 static void FreeString( svg_rendition_t *p_svg )
00466 {
00467 free( p_svg->psz_text );
00468 free( p_svg->p_rendition );
00469 free( p_svg );
00470 }