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 <vlc/vlc.h>
00031 #include <vlc/vout.h>
00032
00033 #include "vlc_filter.h"
00034 #include "vlc_block.h"
00035 #include "vlc_osd.h"
00036
00037 #include "vlc_block.h"
00038 #include "vlc_stream.h"
00039 #include "vlc_xml.h"
00040
00041
00042
00043
00044 static int CreateFilter ( vlc_object_t * );
00045 static void DestroyFilter( vlc_object_t * );
00046 static subpicture_t *Filter( filter_t *, mtime_t );
00047
00048 static int FetchRSS( filter_t * );
00049 static void FreeRSS( filter_t * );
00050
00051 static int pi_color_values[] = { 0xf0000000, 0x00000000, 0x00808080, 0x00C0C0C0,
00052 0x00FFFFFF, 0x00800000, 0x00FF0000, 0x00FF00FF, 0x00FFFF00,
00053 0x00808000, 0x00008000, 0x00008080, 0x0000FF00, 0x00800080,
00054 0x00000080, 0x000000FF, 0x0000FFFF};
00055 static char *ppsz_color_descriptions[] = { N_("Default"), N_("Black"),
00056 N_("Gray"), N_("Silver"), N_("White"), N_("Maroon"), N_("Red"),
00057 N_("Fuchsia"), N_("Yellow"), N_("Olive"), N_("Green"),
00058 N_("Teal"), N_("Lime"), N_("Purple"), N_("Navy"), N_("Blue"),
00059 N_("Aqua") };
00060
00061
00062
00063
00064
00065 struct rss_item_t
00066 {
00067 char *psz_title;
00068 char *psz_description;
00069 char *psz_link;
00070 };
00071
00072 struct rss_feed_t
00073 {
00074 char *psz_title;
00075 char *psz_description;
00076 char *psz_link;
00077
00078 int i_items;
00079 struct rss_item_t *p_items;
00080 };
00081
00082 struct filter_sys_t
00083 {
00084 vlc_mutex_t lock;
00085 vlc_mutex_t *p_lock;
00086
00087 int i_xoff, i_yoff;
00088 int i_pos;
00089 int i_speed;
00090 int i_length;
00091
00092 char *psz_marquee;
00093
00094 int i_font_color, i_font_opacity, i_font_size;
00095
00096 mtime_t last_date;
00097
00098 char *psz_urls;
00099 int i_feeds;
00100 struct rss_feed_t *p_feeds;
00101
00102 int i_ttl;
00103 time_t t_last_update;
00104
00105 int i_cur_feed;
00106 int i_cur_item;
00107 int i_cur_char;
00108 };
00109
00110 #define MSG_TEXT N_("RSS feed URLs")
00111 #define MSG_LONGTEXT N_("RSS feed '|' (pipe) seperated URLs")
00112 #define SPEED_TEXT N_("RSS feed speed")
00113 #define SPEED_LONGTEXT N_("RSS feed speed (bigger is slower)")
00114 #define LENGTH_TEXT N_("RSS feed max number of chars displayed")
00115 #define LENGTH_LONGTEXT N_("RSS feed max number of chars displayed")
00116 #define TTL_TEXT N_("Number of seconds between each forced refresh of the feeds")
00117 #define TTL_LONGTEXT N_("Number of seconds between each forced refresh of the feeds. If 0, the feeds will never be updated.")
00118
00119
00120 #define POSX_TEXT N_("X offset, from left")
00121 #define POSX_LONGTEXT N_("X offset, from the left screen edge" )
00122 #define POSY_TEXT N_("Y offset, from the top")
00123 #define POSY_LONGTEXT N_("Y offset, down from the top" )
00124 #define OPACITY_TEXT N_("Opacity")
00125 #define OPACITY_LONGTEXT N_("The opacity (inverse of transparency) of " \
00126 "overlay text. 0 = transparent, 255 = totally opaque. " )
00127 #define SIZE_TEXT N_("Font size, pixels")
00128 #define SIZE_LONGTEXT N_("Specify the font size, in pixels, " \
00129 "with -1 = use freetype-fontsize" )
00130
00131 #define COLOR_TEXT N_("Text Default Color")
00132 #define COLOR_LONGTEXT N_("The color of overlay text. 1 byte for each color, hexadecimal. " \
00133 "#000000 = all colors off, " \
00134 "0xFF0000 = just Red, 0xFFFFFF = all color on [White]" )
00135
00136 #define POS_TEXT N_("Marquee position")
00137 #define POS_LONGTEXT N_( \
00138 "You can enforce the marquee position on the video " \
00139 "(0=center, 1=left, 2=right, 4=top, 8=bottom, you can " \
00140 "also use combinations of these values by adding them).")
00141
00142 static int pi_pos_values[] = { 0, 1, 2, 4, 8, 5, 6, 9, 10 };
00143 static char *ppsz_pos_descriptions[] =
00144 { N_("Center"), N_("Left"), N_("Right"), N_("Top"), N_("Bottom"),
00145 N_("Top-Left"), N_("Top-Right"), N_("Bottom-Left"), N_("Bottom-Right") };
00146
00147
00148
00149
00150 vlc_module_begin();
00151 set_capability( "sub filter", 0 );
00152 set_shortname( N_("RSS" ));
00153 set_callbacks( CreateFilter, DestroyFilter );
00154 set_category( CAT_VIDEO );
00155 set_subcategory( SUBCAT_VIDEO_SUBPIC );
00156 add_string( "rss-urls", "rss", NULL, MSG_TEXT, MSG_LONGTEXT, VLC_FALSE );
00157
00158 set_section( N_("Position"), NULL );
00159 add_integer( "rss-x", -1, NULL, POSX_TEXT, POSX_LONGTEXT, VLC_TRUE );
00160 add_integer( "rss-y", 0, NULL, POSY_TEXT, POSY_LONGTEXT, VLC_TRUE );
00161 add_integer( "rss-position", 5, NULL, POS_TEXT, POS_LONGTEXT, VLC_FALSE );
00162 change_integer_list( pi_pos_values, ppsz_pos_descriptions, 0 );
00163
00164 set_section( N_("Font"), NULL );
00165
00166 add_integer_with_range( "rss-opacity", 255, 0, 255, NULL,
00167 OPACITY_TEXT, OPACITY_LONGTEXT, VLC_FALSE );
00168 add_integer( "rss-color", 0xFFFFFF, NULL, COLOR_TEXT, COLOR_LONGTEXT,
00169 VLC_FALSE );
00170 change_integer_list( pi_color_values, ppsz_color_descriptions, 0 );
00171 add_integer( "rss-size", -1, NULL, SIZE_TEXT, SIZE_LONGTEXT, VLC_FALSE );
00172
00173 set_section( N_("Misc"), NULL );
00174 add_integer( "rss-speed", 100000, NULL, SPEED_TEXT, SPEED_LONGTEXT,
00175 VLC_FALSE );
00176 add_integer( "rss-length", 60, NULL, LENGTH_TEXT, LENGTH_LONGTEXT,
00177 VLC_FALSE );
00178 add_integer( "rss-ttl", 1800, NULL, TTL_TEXT, TTL_LONGTEXT, VLC_FALSE );
00179
00180 set_description( _("RSS feed display") );
00181 add_shortcut( "rss" );
00182 vlc_module_end();
00183
00184
00185
00186
00187 static int CreateFilter( vlc_object_t *p_this )
00188 {
00189 filter_t *p_filter = (filter_t *)p_this;
00190 filter_sys_t *p_sys;
00191 int i_feed;
00192
00193
00194 p_sys = p_filter->p_sys = malloc( sizeof( filter_sys_t ) );
00195 if( p_sys == NULL )
00196 {
00197 msg_Err( p_filter, "out of memory" );
00198 return VLC_ENOMEM;
00199 }
00200
00201 vlc_mutex_init( p_filter, &p_sys->lock );
00202 vlc_mutex_lock( &p_sys->lock );
00203
00204 p_sys->psz_urls = var_CreateGetString( p_filter, "rss-urls" );
00205 p_sys->i_cur_feed = 0;
00206 p_sys->i_cur_item = 0;
00207 p_sys->i_cur_char = 0;
00208 p_sys->i_feeds = 0;
00209 p_sys->p_feeds = NULL;
00210 p_sys->i_speed = var_CreateGetInteger( p_filter, "rss-speed" );
00211 p_sys->i_length = var_CreateGetInteger( p_filter, "rss-length" );
00212 p_sys->i_ttl = __MAX( 0, var_CreateGetInteger( p_filter, "rss-ttl" ) );
00213 p_sys->psz_marquee = (char *)malloc( p_sys->i_length );
00214
00215 p_sys->i_xoff = var_CreateGetInteger( p_filter, "rss-x" );
00216 p_sys->i_yoff = var_CreateGetInteger( p_filter, "rss-y" );
00217 p_sys->i_pos = var_CreateGetInteger( p_filter, "rss-position" );
00218 var_Create( p_filter, "rss-opacity", VLC_VAR_INTEGER|VLC_VAR_DOINHERIT );
00219 p_sys->i_font_opacity = var_CreateGetInteger( p_filter, "rss-opacity" );
00220 p_sys->i_font_color = var_CreateGetInteger( p_filter, "rss-color" );
00221 p_sys->i_font_size = var_CreateGetInteger( p_filter, "rss-size" );
00222
00223 if( FetchRSS( p_filter ) )
00224 {
00225 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
00226 vlc_mutex_unlock( &p_sys->lock );
00227 return VLC_EGENERIC;
00228 }
00229 p_sys->t_last_update = time( NULL );
00230
00231 if( p_sys->i_feeds == 0 )
00232 {
00233 vlc_mutex_unlock( &p_sys->lock );
00234 return VLC_EGENERIC;
00235 }
00236 for( i_feed=0; i_feed < p_sys->i_feeds; i_feed ++ )
00237 if( p_sys->p_feeds[i_feed].i_items == 0 )
00238 {
00239 vlc_mutex_unlock( &p_sys->lock );
00240 return VLC_EGENERIC;
00241 }
00242
00243
00244 p_filter->pf_sub_filter = Filter;
00245 p_sys->last_date = (mtime_t)0;
00246
00247 vlc_mutex_unlock( &p_sys->lock );
00248
00249 return VLC_SUCCESS;
00250 }
00251
00252
00253
00254 static void DestroyFilter( vlc_object_t *p_this )
00255 {
00256 filter_t *p_filter = (filter_t *)p_this;
00257 filter_sys_t *p_sys = p_filter->p_sys;
00258
00259 vlc_mutex_lock( &p_sys->lock );
00260
00261 if( p_sys->psz_marquee ) free( p_sys->psz_marquee );
00262 free( p_sys->psz_urls );
00263 FreeRSS( p_filter );
00264 vlc_mutex_unlock( &p_sys->lock );
00265 vlc_mutex_destroy( &p_sys->lock );
00266 free( p_sys );
00267
00268
00269 var_Destroy( p_filter, "rss-urls" );
00270 var_Destroy( p_filter, "rss-speed" );
00271 var_Destroy( p_filter, "rss-length" );
00272 var_Destroy( p_filter, "rss-ttl" );
00273 var_Destroy( p_filter, "rss-x" );
00274 var_Destroy( p_filter, "rss-y" );
00275 var_Destroy( p_filter, "rss-position" );
00276 var_Destroy( p_filter, "rss-color");
00277 var_Destroy( p_filter, "rss-opacity");
00278 var_Destroy( p_filter, "rss-size");
00279 }
00280
00281
00282
00283
00284
00285
00286 static subpicture_t *Filter( filter_t *p_filter, mtime_t date )
00287 {
00288 filter_sys_t *p_sys = p_filter->p_sys;
00289 subpicture_t *p_spu;
00290 video_format_t fmt;
00291
00292 int i_feed, i_item;
00293
00294 vlc_mutex_lock( &p_sys->lock );
00295
00296 if( p_sys->last_date
00297 + ( p_sys->i_cur_char == 0 && p_sys->i_cur_item == 0 ? 5 : 1 )
00298
00299 * p_sys->i_speed > date )
00300 {
00301 vlc_mutex_unlock( &p_sys->lock );
00302 return NULL;
00303 }
00304
00305
00306 if( p_sys->i_ttl
00307 && time( NULL ) > p_sys->t_last_update + (time_t)p_sys->i_ttl )
00308 {
00309 msg_Dbg( p_filter, "Forcing update of all the RSS feeds" );
00310 if( FetchRSS( p_filter ) )
00311 {
00312 msg_Err( p_filter, "failed while fetching RSS ... too bad" );
00313 vlc_mutex_unlock( &p_sys->lock );
00314 return NULL;
00315
00316 }
00317 p_sys->t_last_update = time( NULL );
00318 }
00319
00320 p_sys->last_date = date;
00321 p_sys->i_cur_char++;
00322 if( p_sys->p_feeds[p_sys->i_cur_feed].p_items[p_sys->i_cur_item].psz_title[p_sys->i_cur_char] == 0 )
00323 {
00324 p_sys->i_cur_char = 0;
00325 p_sys->i_cur_item++;
00326 if( p_sys->i_cur_item >= p_sys->p_feeds[p_sys->i_cur_feed].i_items )
00327 {
00328 p_sys->i_cur_item = 0;
00329 p_sys->i_cur_feed = (p_sys->i_cur_feed + 1)%p_sys->i_feeds;
00330 }
00331 }
00332
00333 p_spu = p_filter->pf_sub_buffer_new( p_filter );
00334 if( !p_spu )
00335 {
00336 vlc_mutex_unlock( &p_sys->lock );
00337 return NULL;
00338 }
00339
00340 memset( &fmt, 0, sizeof(video_format_t) );
00341 fmt.i_chroma = VLC_FOURCC('T','E','X','T');
00342 fmt.i_aspect = 0;
00343 fmt.i_width = 0;
00344 fmt.i_height = 0;
00345 fmt.i_x_offset = 0;
00346 fmt.i_y_offset = 0;
00347 p_spu->p_region = p_spu->pf_create_region( VLC_OBJECT(p_filter), &fmt );
00348 if( !p_spu->p_region )
00349 {
00350 p_filter->pf_sub_buffer_del( p_filter, p_spu );
00351 vlc_mutex_unlock( &p_sys->lock );
00352 return NULL;
00353 }
00354
00355 i_item = p_sys->i_cur_item;
00356 i_feed = p_sys->i_cur_feed;
00357 snprintf( p_sys->psz_marquee, p_sys->i_length, "%s : %s", p_sys->p_feeds[i_feed].psz_title, p_sys->p_feeds[i_feed].p_items[i_item].psz_title+p_sys->i_cur_char );
00358 while( strlen( p_sys->psz_marquee ) < (unsigned int)p_sys->i_length )
00359 {
00360 i_item++;
00361 if( i_item == p_sys->p_feeds[i_feed].i_items ) break;
00362 snprintf( strchr( p_sys->psz_marquee, 0 ), p_sys->i_length - strlen( p_sys->psz_marquee ), " - %s", p_sys->p_feeds[i_feed].p_items[i_item].psz_title );
00363 }
00364
00365 p_spu->p_region->psz_text = strdup(p_sys->psz_marquee);
00366 p_spu->i_start = date;
00367 p_spu->i_stop = 0;
00368 p_spu->b_ephemer = VLC_TRUE;
00369
00370
00371 if( p_sys->i_xoff < 0 || p_sys->i_yoff < 0 )
00372 {
00373 p_spu->i_flags = p_sys->i_pos;
00374 p_spu->i_x = 0;
00375 p_spu->i_y = 0;
00376 p_spu->b_absolute = VLC_FALSE;
00377 }
00378 else
00379 {
00380 p_spu->i_flags = OSD_ALIGN_LEFT | OSD_ALIGN_TOP;
00381 p_spu->i_x = p_sys->i_xoff;
00382 p_spu->i_y = p_sys->i_yoff;
00383 p_spu->b_absolute = VLC_TRUE;
00384 }
00385 p_spu->i_height = 1;
00386 p_spu->p_region->i_text_color = p_sys->i_font_color;
00387 p_spu->p_region->i_text_alpha = 255 - p_sys->i_font_opacity;
00388 p_spu->p_region->i_text_size = p_sys->i_font_size;
00389
00390 vlc_mutex_unlock( &p_sys->lock );
00391 return p_spu;
00392 }
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404 static int FetchRSS( filter_t *p_filter)
00405 {
00406 filter_sys_t *p_sys = p_filter->p_sys;
00407
00408 stream_t *p_stream = NULL;
00409 xml_t *p_xml = NULL;
00410 xml_reader_t *p_xml_reader = NULL;
00411
00412 char *psz_eltname = NULL;
00413 char *psz_eltvalue = NULL;
00414 char *psz_feed = NULL;
00415 char *psz_buffer = NULL;
00416 char *psz_buffer_2 = NULL;
00417
00418 int i_feed;
00419 int i_item;
00420 int i_is_item;
00421 int i_int;
00422
00423 FreeRSS( p_filter );
00424 p_sys->i_feeds = 1;
00425 i_int = 0;
00426 while( p_sys->psz_urls[i_int] != 0 )
00427 if( p_sys->psz_urls[i_int++] == '|' )
00428 p_sys->i_feeds++;
00429 p_sys->p_feeds = (struct rss_feed_t *)malloc( p_sys->i_feeds
00430 * sizeof( struct rss_feed_t ) );
00431
00432 p_xml = xml_Create( p_filter );
00433 if( !p_xml )
00434 {
00435 msg_Err( p_filter, "Failed to open XML parser" );
00436 return 1;
00437 }
00438
00439 psz_buffer = strdup( p_sys->psz_urls );
00440 psz_buffer_2 = psz_buffer;
00441 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
00442 {
00443 struct rss_feed_t *p_feed = p_sys->p_feeds+i_feed;
00444
00445 if( psz_buffer == NULL ) break;
00446 if( psz_buffer[0] == 0 ) psz_buffer++;
00447 psz_feed = psz_buffer;
00448 psz_buffer = strchr( psz_buffer, '|' );
00449 if( psz_buffer != NULL ) psz_buffer[0] = 0;
00450
00451 p_feed->psz_title = NULL;
00452 p_feed->psz_description = NULL;
00453 p_feed->psz_link = NULL;
00454 p_feed->i_items = 0;
00455 p_feed->p_items = NULL;
00456
00457 msg_Dbg( p_filter, "Opening %s RSS feed ...", psz_feed );
00458
00459 p_stream = stream_UrlNew( p_filter, psz_feed );
00460 if( !p_stream )
00461 {
00462 msg_Err( p_filter, "Failed to open %s for reading", psz_feed );
00463 return 1;
00464 }
00465
00466 p_xml_reader = xml_ReaderCreate( p_xml, p_stream );
00467 if( !p_xml_reader )
00468 {
00469 msg_Err( p_filter, "Failed to open %s for parsing", psz_feed );
00470 return 1;
00471 }
00472
00473 i_item = 0;
00474 i_is_item = VLC_FALSE;
00475
00476 while( xml_ReaderRead( p_xml_reader ) == 1 )
00477 {
00478 switch( xml_ReaderNodeType( p_xml_reader ) )
00479 {
00480
00481 case -1:
00482 return 1;
00483
00484 case XML_READER_STARTELEM:
00485 if( psz_eltname )
00486 {
00487 free( psz_eltname );
00488 psz_eltname = NULL;
00489 }
00490 psz_eltname = xml_ReaderName( p_xml_reader );
00491 if( !psz_eltname )
00492 {
00493 return 1;
00494 }
00495 # ifdef RSS_DEBUG
00496 msg_Dbg( p_filter, "element name : %s", psz_eltname );
00497 # endif
00498 if( !strcmp( psz_eltname, "item" ) )
00499 {
00500 i_is_item = VLC_TRUE;
00501 p_feed->i_items++;
00502 p_feed->p_items = (struct rss_item_t *)realloc( p_feed->p_items, p_feed->i_items * sizeof( struct rss_item_t ) );
00503 p_feed->p_items[p_feed->i_items-1].psz_title = NULL;
00504 p_feed->p_items[p_feed->i_items-1].psz_description
00505 = NULL;
00506 p_feed->p_items[p_feed->i_items-1].psz_link = NULL;
00507 }
00508 break;
00509
00510 case XML_READER_ENDELEM:
00511 if( psz_eltname )
00512 {
00513 free( psz_eltname );
00514 psz_eltname = NULL;
00515 }
00516 psz_eltname = xml_ReaderName( p_xml_reader );
00517 if( !psz_eltname )
00518 {
00519 return 1;
00520 }
00521 # ifdef RSS_DEBUG
00522 msg_Dbg( p_filter, "element end : %s", psz_eltname );
00523 # endif
00524 if( !strcmp( psz_eltname, "item" ) )
00525 {
00526 i_is_item = VLC_FALSE;
00527 i_item++;
00528 }
00529 free( psz_eltname );
00530 psz_eltname = NULL;
00531 break;
00532
00533 case XML_READER_TEXT:
00534 psz_eltvalue = xml_ReaderValue( p_xml_reader );
00535 if( !psz_eltvalue )
00536 {
00537 return 1;
00538 }
00539 # ifdef RSS_DEBUG
00540 msg_Dbg( p_filter, " text : %s", psz_eltvalue );
00541 # endif
00542 if( i_is_item == VLC_FALSE )
00543 {
00544 if( !strcmp( psz_eltname, "title" ) )
00545 {
00546 p_feed->psz_title = psz_eltvalue;
00547 }
00548 else if( !strcmp( psz_eltname, "link" ) )
00549 {
00550 p_feed->psz_link = psz_eltvalue;
00551 }
00552 else if( !strcmp( psz_eltname, "description" ) )
00553 {
00554 p_feed->psz_description = psz_eltvalue;
00555 }
00556 else
00557 {
00558 free( psz_eltvalue );
00559 psz_eltvalue = NULL;
00560 }
00561 }
00562 else
00563 {
00564 struct rss_item_t *p_item;
00565 p_item = p_feed->p_items+i_item;
00566 if( !strcmp( psz_eltname, "title" ) )
00567 {
00568 p_item->psz_title = psz_eltvalue;
00569 }
00570 else if( !strcmp( psz_eltname, "link" ) )
00571 {
00572 p_item->psz_link = psz_eltvalue;
00573 }
00574 else if( !strcmp( psz_eltname, "description" ) )
00575 {
00576 p_item->psz_description = psz_eltvalue;
00577 }
00578 else
00579 {
00580 free( psz_eltvalue );
00581 psz_eltvalue = NULL;
00582 }
00583 }
00584 break;
00585 }
00586 }
00587
00588 if( p_xml_reader && p_xml ) xml_ReaderDelete( p_xml, p_xml_reader );
00589 if( p_stream ) stream_Delete( p_stream );
00590 msg_Dbg( p_filter, "Done with %s RSS feed.", psz_feed );
00591 }
00592 free( psz_buffer_2 );
00593 if( p_xml ) xml_Delete( p_xml );
00594
00595 return 0;
00596 }
00597
00598
00599
00600
00601 static void FreeRSS( filter_t *p_filter)
00602 {
00603 filter_sys_t *p_sys = p_filter->p_sys;
00604
00605 struct rss_item_t *p_item;
00606 struct rss_feed_t *p_feed;
00607
00608 int i_feed;
00609 int i_item;
00610
00611 for( i_feed = 0; i_feed < p_sys->i_feeds; i_feed++ )
00612 {
00613 p_feed = p_sys->p_feeds+i_feed;
00614 for( i_item = 0; i_item < p_feed->i_items; i_item++ )
00615 {
00616 p_item = p_feed->p_items+i_item;
00617 free( p_item->psz_title );
00618 free( p_item->psz_link );
00619 free( p_item->psz_description );
00620 }
00621 free( p_feed->p_items );
00622 free( p_feed->psz_title);
00623 free( p_feed->psz_link );
00624 free( p_feed->psz_description );
00625 }
00626 free( p_sys->p_feeds );
00627 p_sys->i_feeds = 0;
00628 }