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
00028
00029 #include <stdlib.h>
00030 #include <stdio.h>
00031 #include <string.h>
00032
00033 #include <vlc/vlc.h>
00034
00035 #include "vlc_block.h"
00036 #include "vlc_video.h"
00037 #include "video_output.h"
00038 #include "vlc_spu.h"
00039 #include "vlc_filter.h"
00040
00041
00042
00043
00044 static void UpdateSPU ( spu_t *, vlc_object_t * );
00045 static int CropCallback( vlc_object_t *, char const *,
00046 vlc_value_t, vlc_value_t, void * );
00047
00048 static int spu_vaControlDefault( spu_t *, int, va_list );
00049
00050 static subpicture_t *sub_new_buffer( filter_t * );
00051 static void sub_del_buffer( filter_t *, subpicture_t * );
00052 static subpicture_t *spu_new_buffer( filter_t * );
00053 static void spu_del_buffer( filter_t *, subpicture_t * );
00054 static picture_t *spu_new_video_buffer( filter_t * );
00055 static void spu_del_video_buffer( filter_t *, picture_t * );
00056
00057 struct filter_owner_sys_t
00058 {
00059 spu_t *p_spu;
00060 int i_channel;
00061 };
00062
00068 spu_t *__spu_Create( vlc_object_t *p_this )
00069 {
00070 int i_index;
00071 spu_t *p_spu = vlc_object_create( p_this, VLC_OBJECT_SPU );
00072
00073 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++)
00074 {
00075 p_spu->p_subpicture[i_index].i_status = FREE_SUBPICTURE;
00076 }
00077
00078 p_spu->p_blend = NULL;
00079 p_spu->p_text = NULL;
00080 p_spu->p_scale = NULL;
00081 p_spu->i_filter = 0;
00082 p_spu->pf_control = spu_vaControlDefault;
00083
00084
00085 p_spu->i_channel = 2;
00086
00087 vlc_mutex_init( p_this, &p_spu->subpicture_lock );
00088
00089 vlc_object_attach( p_spu, p_this );
00090
00091 return p_spu;
00092 }
00093
00099 int spu_Init( spu_t *p_spu )
00100 {
00101 char *psz_filter, *psz_filter_orig;
00102 vlc_value_t val;
00103
00104
00105 var_Create( p_spu, "sub-margin", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
00106 var_Get( p_spu, "sub-margin", &val );
00107 p_spu->i_margin = val.i_int;
00108
00109 var_Create( p_spu, "sub-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
00110 var_Get( p_spu, "sub-filter", &val );
00111 psz_filter = psz_filter_orig = val.psz_string;
00112 while( psz_filter && *psz_filter )
00113 {
00114 char *psz_parser = strchr( psz_filter, ':' );
00115
00116 if( psz_parser ) *psz_parser++ = 0;
00117
00118 p_spu->pp_filter[p_spu->i_filter] =
00119 vlc_object_create( p_spu, VLC_OBJECT_FILTER );
00120 vlc_object_attach( p_spu->pp_filter[p_spu->i_filter], p_spu );
00121 p_spu->pp_filter[p_spu->i_filter]->pf_sub_buffer_new = sub_new_buffer;
00122 p_spu->pp_filter[p_spu->i_filter]->pf_sub_buffer_del = sub_del_buffer;
00123 p_spu->pp_filter[p_spu->i_filter]->p_module =
00124 module_Need( p_spu->pp_filter[p_spu->i_filter],
00125 "sub filter", psz_filter, 0 );
00126 if( p_spu->pp_filter[p_spu->i_filter]->p_module )
00127 {
00128 filter_owner_sys_t *p_sys = malloc( sizeof(filter_owner_sys_t) );
00129 p_spu->pp_filter[p_spu->i_filter]->p_owner = p_sys;
00130 spu_Control( p_spu, SPU_CHANNEL_REGISTER, &p_sys->i_channel );
00131 p_sys->p_spu = p_spu;
00132 p_spu->i_filter++;
00133 }
00134 else
00135 {
00136 msg_Dbg( p_spu, "no sub filter found" );
00137 vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] );
00138 vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] );
00139 }
00140
00141 if( p_spu->i_filter >= 10 )
00142 {
00143 msg_Dbg( p_spu, "can't add anymore filters" );
00144 }
00145
00146 psz_filter = psz_parser;
00147 }
00148 if( psz_filter_orig ) free( psz_filter_orig );
00149
00150 return VLC_EGENERIC;
00151 }
00152
00158 void spu_Destroy( spu_t *p_spu )
00159 {
00160 int i_index;
00161
00162 vlc_object_detach( p_spu );
00163
00164
00165 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
00166 {
00167 if( p_spu->p_subpicture[i_index].i_status != FREE_SUBPICTURE )
00168 {
00169 spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
00170 }
00171 }
00172
00173 if( p_spu->p_blend )
00174 {
00175 if( p_spu->p_blend->p_module )
00176 module_Unneed( p_spu->p_blend, p_spu->p_blend->p_module );
00177
00178 vlc_object_detach( p_spu->p_blend );
00179 vlc_object_destroy( p_spu->p_blend );
00180 }
00181
00182 if( p_spu->p_text )
00183 {
00184 if( p_spu->p_text->p_module )
00185 module_Unneed( p_spu->p_text, p_spu->p_text->p_module );
00186
00187 vlc_object_detach( p_spu->p_text );
00188 vlc_object_destroy( p_spu->p_text );
00189 }
00190
00191 if( p_spu->p_scale )
00192 {
00193 if( p_spu->p_scale->p_module )
00194 module_Unneed( p_spu->p_scale, p_spu->p_scale->p_module );
00195
00196 vlc_object_detach( p_spu->p_scale );
00197 vlc_object_destroy( p_spu->p_scale );
00198 }
00199
00200 while( p_spu->i_filter-- )
00201 {
00202 module_Unneed( p_spu->pp_filter[p_spu->i_filter],
00203 p_spu->pp_filter[p_spu->i_filter]->p_module );
00204 free( p_spu->pp_filter[p_spu->i_filter]->p_owner );
00205 vlc_object_detach( p_spu->pp_filter[p_spu->i_filter] );
00206 vlc_object_destroy( p_spu->pp_filter[p_spu->i_filter] );
00207 }
00208
00209 vlc_mutex_destroy( &p_spu->subpicture_lock );
00210 vlc_object_destroy( p_spu );
00211 }
00212
00219 void spu_Attach( spu_t *p_spu, vlc_object_t *p_this, vlc_bool_t b_attach )
00220 {
00221 vlc_object_t *p_input;
00222
00223 p_input = vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT );
00224 if( !p_input ) return;
00225
00226 if( b_attach )
00227 {
00228 UpdateSPU( p_spu, VLC_OBJECT(p_input) );
00229 var_AddCallback( p_input, "highlight", CropCallback, p_spu );
00230 vlc_object_release( p_input );
00231 }
00232 else
00233 {
00234
00235 var_DelCallback( p_input, "highlight", CropCallback, p_spu );
00236 vlc_object_release( p_input );
00237 }
00238 }
00239
00246 static void RegionPictureRelease( picture_t *p_pic )
00247 {
00248 if( p_pic->p_data_orig ) free( p_pic->p_data_orig );
00249 }
00250 subpicture_region_t *__spu_CreateRegion( vlc_object_t *p_this,
00251 video_format_t *p_fmt )
00252 {
00253 subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
00254 memset( p_region, 0, sizeof(subpicture_region_t) );
00255 p_region->p_next = 0;
00256 p_region->p_cache = 0;
00257 p_region->fmt = *p_fmt;
00258 p_region->psz_text = 0;
00259 p_region->i_text_color = 0xFFFFFF;
00260
00261 if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
00262 p_fmt->p_palette = p_region->fmt.p_palette =
00263 malloc( sizeof(video_palette_t) );
00264 else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
00265
00266 p_region->picture.p_data_orig = NULL;
00267
00268 if( p_fmt->i_chroma == VLC_FOURCC('T','E','X','T') ) return p_region;
00269
00270 vout_AllocatePicture( p_this, &p_region->picture, p_fmt->i_chroma,
00271 p_fmt->i_width, p_fmt->i_height, p_fmt->i_aspect );
00272
00273 if( !p_region->picture.i_planes )
00274 {
00275 free( p_region );
00276 free( p_fmt->p_palette );
00277 return NULL;
00278 }
00279
00280 p_region->picture.pf_release = RegionPictureRelease;
00281
00282 return p_region;
00283 }
00284
00292 subpicture_region_t *__spu_MakeRegion( vlc_object_t *p_this,
00293 video_format_t *p_fmt,
00294 picture_t *p_pic )
00295 {
00296 subpicture_region_t *p_region = malloc( sizeof(subpicture_region_t) );
00297 memset( p_region, 0, sizeof(subpicture_region_t) );
00298 p_region->p_next = 0;
00299 p_region->p_cache = 0;
00300 p_region->fmt = *p_fmt;
00301 p_region->psz_text = 0;
00302 p_region->i_text_color = 0xFFFFFF;
00303
00304 if( p_fmt->i_chroma == VLC_FOURCC('Y','U','V','P') )
00305 p_fmt->p_palette = p_region->fmt.p_palette =
00306 malloc( sizeof(video_palette_t) );
00307 else p_fmt->p_palette = p_region->fmt.p_palette = NULL;
00308
00309 memcpy( &p_region->picture, p_pic, sizeof(picture_t) );
00310 p_region->picture.pf_release = RegionPictureRelease;
00311
00312 return p_region;
00313 }
00314
00321 void __spu_DestroyRegion( vlc_object_t *p_this, subpicture_region_t *p_region )
00322 {
00323 if( !p_region ) return;
00324 if( p_region->picture.pf_release )
00325 p_region->picture.pf_release( &p_region->picture );
00326 if( p_region->fmt.p_palette ) free( p_region->fmt.p_palette );
00327 if( p_region->psz_text ) free( p_region->psz_text );
00328 if( p_region->p_cache ) __spu_DestroyRegion( p_this, p_region->p_cache );
00329 free( p_region );
00330 }
00331
00340 void spu_DisplaySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
00341 {
00342
00343 if( p_subpic->i_status != RESERVED_SUBPICTURE )
00344 {
00345 msg_Err( p_spu, "subpicture %p has invalid status #%d",
00346 p_subpic, p_subpic->i_status );
00347 }
00348
00349
00350 p_subpic->i_status = READY_SUBPICTURE;
00351
00352 if( p_subpic->i_channel == DEFAULT_CHAN )
00353 {
00354 p_subpic->i_channel = 0xFFFF;
00355 spu_Control( p_spu, SPU_CHANNEL_CLEAR, DEFAULT_CHAN );
00356 p_subpic->i_channel = DEFAULT_CHAN;
00357 }
00358 }
00359
00370 subpicture_t *spu_CreateSubpicture( spu_t *p_spu )
00371 {
00372 int i_subpic;
00373 subpicture_t * p_subpic = NULL;
00374
00375
00376 vlc_mutex_lock( &p_spu->subpicture_lock );
00377
00378
00379
00380
00381 p_subpic = NULL;
00382 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
00383 {
00384 if( p_spu->p_subpicture[i_subpic].i_status == FREE_SUBPICTURE )
00385 {
00386
00387 p_subpic = &p_spu->p_subpicture[i_subpic];
00388 p_spu->p_subpicture[i_subpic].i_status = RESERVED_SUBPICTURE;
00389 break;
00390 }
00391 }
00392
00393
00394 if( p_subpic == NULL )
00395 {
00396 msg_Err( p_spu, "subpicture heap is full" );
00397 vlc_mutex_unlock( &p_spu->subpicture_lock );
00398 return NULL;
00399 }
00400
00401
00402 memset( p_subpic, 0, sizeof(subpicture_t) );
00403 p_subpic->i_status = RESERVED_SUBPICTURE;
00404 p_subpic->b_absolute = VLC_TRUE;
00405 p_subpic->b_fade = VLC_FALSE;
00406 p_subpic->i_alpha = 0xFF;
00407 p_subpic->p_region = 0;
00408 p_subpic->pf_render = 0;
00409 p_subpic->pf_destroy = 0;
00410 p_subpic->p_sys = 0;
00411 vlc_mutex_unlock( &p_spu->subpicture_lock );
00412
00413 p_subpic->pf_create_region = __spu_CreateRegion;
00414 p_subpic->pf_make_region = __spu_MakeRegion;
00415 p_subpic->pf_destroy_region = __spu_DestroyRegion;
00416
00417 return p_subpic;
00418 }
00419
00428 void spu_DestroySubpicture( spu_t *p_spu, subpicture_t *p_subpic )
00429 {
00430
00431 vlc_mutex_lock( &p_spu->subpicture_lock );
00432
00433
00434 if( p_subpic->i_status == FREE_SUBPICTURE )
00435 {
00436 vlc_mutex_unlock( &p_spu->subpicture_lock );
00437 return;
00438 }
00439
00440
00441 if( ( p_subpic->i_status != RESERVED_SUBPICTURE )
00442 && ( p_subpic->i_status != READY_SUBPICTURE ) )
00443 {
00444 msg_Err( p_spu, "subpicture %p has invalid status %d",
00445 p_subpic, p_subpic->i_status );
00446 }
00447
00448 while( p_subpic->p_region )
00449 {
00450 subpicture_region_t *p_region = p_subpic->p_region;
00451 p_subpic->p_region = p_region->p_next;
00452 spu_DestroyRegion( p_spu, p_region );
00453 }
00454
00455 if( p_subpic->pf_destroy )
00456 {
00457 p_subpic->pf_destroy( p_subpic );
00458 }
00459
00460 p_subpic->i_status = FREE_SUBPICTURE;
00461
00462 vlc_mutex_unlock( &p_spu->subpicture_lock );
00463 }
00464
00465
00466
00467
00468
00469
00470 void spu_RenderSubpictures( spu_t *p_spu, video_format_t *p_fmt,
00471 picture_t *p_pic_dst, picture_t *p_pic_src,
00472 subpicture_t *p_subpic,
00473 int i_scale_width_orig, int i_scale_height_orig )
00474 {
00475
00476 vlc_mutex_lock( &p_spu->subpicture_lock );
00477
00478
00479 while( p_subpic != NULL && p_subpic->i_status != FREE_SUBPICTURE )
00480 {
00481 subpicture_region_t *p_region = p_subpic->p_region;
00482 int i_scale_width, i_scale_height;
00483 int i_subpic_x = p_subpic->i_x;
00484
00485
00486 if( !p_spu->p_blend && p_region )
00487 {
00488 p_spu->p_blend = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
00489 vlc_object_attach( p_spu->p_blend, p_spu );
00490 p_spu->p_blend->fmt_out.video.i_x_offset =
00491 p_spu->p_blend->fmt_out.video.i_y_offset = 0;
00492 p_spu->p_blend->fmt_out.video.i_aspect = p_fmt->i_aspect;
00493 p_spu->p_blend->fmt_out.video.i_chroma = p_fmt->i_chroma;
00494 p_spu->p_blend->fmt_in.video.i_chroma = VLC_FOURCC('Y','U','V','P');
00495
00496 p_spu->p_blend->p_module =
00497 module_Need( p_spu->p_blend, "video blending", 0, 0 );
00498 }
00499
00500
00501 if( !p_spu->p_text && p_region )
00502 {
00503 p_spu->p_text = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
00504 vlc_object_attach( p_spu->p_text, p_spu );
00505
00506 p_spu->p_text->fmt_out.video.i_width =
00507 p_spu->p_text->fmt_out.video.i_visible_width =
00508 p_fmt->i_width;
00509 p_spu->p_text->fmt_out.video.i_height =
00510 p_spu->p_text->fmt_out.video.i_visible_height =
00511 p_fmt->i_height;
00512
00513 p_spu->p_text->pf_sub_buffer_new = spu_new_buffer;
00514 p_spu->p_text->pf_sub_buffer_del = spu_del_buffer;
00515 p_spu->p_text->p_module =
00516 module_Need( p_spu->p_text, "text renderer", 0, 0 );
00517 }
00518 else if( p_region )
00519 {
00520 p_spu->p_text->fmt_out.video.i_width =
00521 p_spu->p_text->fmt_out.video.i_visible_width =
00522 p_fmt->i_width;
00523 p_spu->p_text->fmt_out.video.i_height =
00524 p_spu->p_text->fmt_out.video.i_visible_height =
00525 p_fmt->i_height;
00526 }
00527
00528 i_scale_width = i_scale_width_orig;
00529 i_scale_height = i_scale_height_orig;
00530
00531 if( p_subpic->i_original_picture_width &&
00532 p_subpic->i_original_picture_height )
00533 {
00534 i_scale_width = i_scale_width * p_fmt->i_width /
00535 p_subpic->i_original_picture_width;
00536 i_scale_height = i_scale_height * p_fmt->i_height /
00537 p_subpic->i_original_picture_height;
00538 }
00539
00540
00541 if( p_region && p_region->fmt.i_aspect &&
00542 (!p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den) )
00543 {
00544 p_region->fmt.i_sar_den = p_region->fmt.i_aspect;
00545 p_region->fmt.i_sar_num = VOUT_ASPECT_FACTOR;
00546 }
00547 if( p_region &&
00548 (!p_region->fmt.i_sar_num || !p_region->fmt.i_sar_den) )
00549 {
00550 p_region->fmt.i_sar_den = p_fmt->i_sar_den;
00551 p_region->fmt.i_sar_num = p_fmt->i_sar_num;
00552 }
00553
00554
00555 if( p_region && p_region->fmt.i_sar_num * p_fmt->i_sar_den !=
00556 p_region->fmt.i_sar_den * p_fmt->i_sar_num )
00557 {
00558 i_scale_width = i_scale_width *
00559 (int64_t)p_region->fmt.i_sar_num * p_fmt->i_sar_den /
00560 p_region->fmt.i_sar_den / p_fmt->i_sar_num;
00561 i_subpic_x = p_subpic->i_x * i_scale_width / 1000;
00562 }
00563
00564
00565 if( !p_spu->p_scale && (i_scale_width != 1000 ||
00566 i_scale_height != 1000) )
00567 {
00568 p_spu->p_scale = vlc_object_create( p_spu, VLC_OBJECT_FILTER );
00569 vlc_object_attach( p_spu->p_scale, p_spu );
00570 p_spu->p_scale->fmt_out.video.i_chroma =
00571 p_spu->p_scale->fmt_in.video.i_chroma =
00572 VLC_FOURCC('Y','U','V','P');
00573 p_spu->p_scale->fmt_in.video.i_width =
00574 p_spu->p_scale->fmt_in.video.i_height = 32;
00575 p_spu->p_scale->fmt_out.video.i_width =
00576 p_spu->p_scale->fmt_out.video.i_height = 16;
00577
00578 p_spu->p_scale->pf_vout_buffer_new = spu_new_video_buffer;
00579 p_spu->p_scale->pf_vout_buffer_del = spu_del_video_buffer;
00580 p_spu->p_scale->p_module =
00581 module_Need( p_spu->p_scale, "video filter2", 0, 0 );
00582 }
00583
00584 while( p_region && p_spu->p_blend && p_spu->p_blend->pf_video_blend )
00585 {
00586 int i_fade_alpha = 255;
00587 int i_x_offset = p_region->i_x + i_subpic_x;
00588 int i_y_offset = p_region->i_y + p_subpic->i_y;
00589
00590 if( p_region->fmt.i_chroma == VLC_FOURCC('T','E','X','T') )
00591 {
00592 if( p_spu->p_text && p_spu->p_text->p_module &&
00593 p_spu->p_text->pf_render_text )
00594 {
00595 p_region->i_text_align = p_subpic->i_flags & 0x3;
00596 p_spu->p_text->pf_render_text( p_spu->p_text,
00597 p_region, p_region );
00598 }
00599 }
00600
00601
00602 if( p_spu->b_force_palette && VLC_FOURCC('Y','U','V','P') ==
00603 p_region->fmt.i_chroma )
00604 {
00605 memcpy( p_region->fmt.p_palette->palette,
00606 p_spu->palette, 16 );
00607 }
00608
00609
00610 if( p_region->p_cache )
00611 {
00612 if( i_scale_width * p_region->fmt.i_width / 1000 !=
00613 p_region->p_cache->fmt.i_width ||
00614 i_scale_height * p_region->fmt.i_height / 1000 !=
00615 p_region->p_cache->fmt.i_height )
00616 {
00617 p_subpic->pf_destroy_region( VLC_OBJECT(p_spu),
00618 p_region->p_cache );
00619 p_region->p_cache = 0;
00620 }
00621 }
00622
00623 if( (i_scale_width != 1000 || i_scale_height != 1000) &&
00624 p_spu->p_scale && !p_region->p_cache )
00625 {
00626 picture_t *p_pic;
00627
00628 p_spu->p_scale->fmt_in.video = p_region->fmt;
00629 p_spu->p_scale->fmt_out.video = p_region->fmt;
00630
00631 p_region->p_cache =
00632 p_subpic->pf_create_region( VLC_OBJECT(p_spu),
00633 &p_spu->p_scale->fmt_out.video );
00634 if( p_spu->p_scale->fmt_out.video.p_palette )
00635 *p_spu->p_scale->fmt_out.video.p_palette =
00636 *p_region->fmt.p_palette;
00637 p_region->p_cache->p_next = p_region->p_next;
00638
00639 vout_CopyPicture( p_spu, &p_region->p_cache->picture,
00640 &p_region->picture );
00641
00642 p_spu->p_scale->fmt_out.video.i_width =
00643 p_region->fmt.i_width * i_scale_width / 1000;
00644 p_spu->p_scale->fmt_out.video.i_visible_width =
00645 p_region->fmt.i_visible_width * i_scale_width / 1000;
00646 p_spu->p_scale->fmt_out.video.i_height =
00647 p_region->fmt.i_height * i_scale_height / 1000;
00648 p_spu->p_scale->fmt_out.video.i_visible_height =
00649 p_region->fmt.i_visible_height * i_scale_height / 1000;
00650 p_region->p_cache->fmt = p_spu->p_scale->fmt_out.video;
00651 p_region->p_cache->i_x = p_region->i_x * i_scale_width / 1000;
00652 p_region->p_cache->i_y = p_region->i_y * i_scale_height / 1000;
00653
00654 p_pic = p_spu->p_scale->pf_video_filter(
00655 p_spu->p_scale, &p_region->p_cache->picture );
00656 if( p_pic )
00657 {
00658 picture_t p_pic_tmp = p_region->p_cache->picture;
00659 p_region->p_cache->picture = *p_pic;
00660 *p_pic = p_pic_tmp;
00661 free( p_pic );
00662 }
00663 }
00664 if( (i_scale_width != 1000 || i_scale_height != 1000) &&
00665 p_spu->p_scale && p_region->p_cache )
00666 {
00667 p_region = p_region->p_cache;
00668 }
00669
00670 if( p_subpic->i_flags & SUBPICTURE_ALIGN_BOTTOM )
00671 {
00672 i_y_offset = p_fmt->i_height - p_region->fmt.i_height -
00673 p_subpic->i_y;
00674 }
00675 else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_TOP) )
00676 {
00677 i_y_offset = p_fmt->i_height / 2 - p_region->fmt.i_height / 2;
00678 }
00679
00680 if( p_subpic->i_flags & SUBPICTURE_ALIGN_RIGHT )
00681 {
00682 i_x_offset = p_fmt->i_width - p_region->fmt.i_width -
00683 i_subpic_x;
00684 }
00685 else if ( !(p_subpic->i_flags & SUBPICTURE_ALIGN_LEFT) )
00686 {
00687 i_x_offset = p_fmt->i_width / 2 - p_region->fmt.i_width / 2;
00688 }
00689
00690 if( p_subpic->b_absolute )
00691 {
00692 i_x_offset = p_region->i_x +
00693 i_subpic_x * i_scale_width / 1000;
00694 i_y_offset = p_region->i_y +
00695 p_subpic->i_y * i_scale_height / 1000;
00696
00697 if( p_spu->i_margin >= 0 )
00698 {
00699 if( p_subpic->i_height + (unsigned int)p_spu->i_margin <=
00700 p_fmt->i_height )
00701 {
00702 i_y_offset = p_fmt->i_height -
00703 p_spu->i_margin - p_subpic->i_height;
00704 }
00705 }
00706 }
00707
00708 p_spu->p_blend->fmt_in.video = p_region->fmt;
00709
00710
00711 if( p_spu->b_force_crop )
00712 {
00713 video_format_t *p_fmt = &p_spu->p_blend->fmt_in.video;
00714 int i_crop_x = p_spu->i_crop_x * i_scale_width / 1000;
00715 int i_crop_y = p_spu->i_crop_y * i_scale_height / 1000;
00716 int i_crop_width = p_spu->i_crop_width * i_scale_width / 1000;
00717 int i_crop_height = p_spu->i_crop_height * i_scale_height/1000;
00718
00719
00720 if( i_crop_x + i_crop_width <= i_x_offset ||
00721 i_x_offset + (int)p_fmt->i_visible_width < i_crop_x ||
00722 i_crop_y + i_crop_height <= i_y_offset ||
00723 i_y_offset + (int)p_fmt->i_visible_height < i_crop_y )
00724 {
00725
00726 p_fmt->i_visible_width = p_fmt->i_visible_height = 0;
00727 }
00728 else
00729 {
00730 int i_x, i_y, i_x_end, i_y_end;
00731 i_x = __MAX( i_crop_x, i_x_offset );
00732 i_y = __MAX( i_crop_y, i_y_offset );
00733 i_x_end = __MIN( i_crop_x + i_crop_width,
00734 i_x_offset + (int)p_fmt->i_visible_width );
00735 i_y_end = __MIN( i_crop_y + i_crop_height,
00736 i_y_offset + (int)p_fmt->i_visible_height );
00737
00738 p_fmt->i_x_offset = i_x - i_x_offset;
00739 p_fmt->i_y_offset = i_y - i_y_offset;
00740 p_fmt->i_visible_width = i_x_end - i_x;
00741 p_fmt->i_visible_height = i_y_end - i_y;
00742
00743 i_x_offset = i_x;
00744 i_y_offset = i_y;
00745 }
00746 }
00747
00748
00749 p_spu->p_blend->fmt_out.video.i_width =
00750 p_spu->p_blend->fmt_out.video.i_visible_width =
00751 p_fmt->i_width;
00752 p_spu->p_blend->fmt_out.video.i_height =
00753 p_spu->p_blend->fmt_out.video.i_visible_height =
00754 p_fmt->i_height;
00755
00756 if( p_subpic->b_fade )
00757 {
00758 mtime_t i_fade_start = ( p_subpic->i_stop +
00759 p_subpic->i_start ) / 2;
00760 mtime_t i_now = mdate();
00761 if( i_now >= i_fade_start && p_subpic->i_stop > i_fade_start )
00762 {
00763 i_fade_alpha = 255 * ( p_subpic->i_stop - i_now ) /
00764 ( p_subpic->i_stop - i_fade_start );
00765 }
00766 }
00767
00768 p_spu->p_blend->pf_video_blend( p_spu->p_blend, p_pic_dst,
00769 p_pic_src, &p_region->picture, i_x_offset, i_y_offset,
00770 i_fade_alpha * p_subpic->i_alpha / 255 );
00771
00772 p_region = p_region->p_next;
00773 }
00774
00775 p_subpic = p_subpic->p_next;
00776 }
00777
00778 vlc_mutex_unlock( &p_spu->subpicture_lock );
00779 }
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792 subpicture_t *spu_SortSubpictures( spu_t *p_spu, mtime_t display_date )
00793 {
00794 int i_index, i_channel;
00795 subpicture_t *p_subpic = NULL;
00796 subpicture_t *p_ephemer;
00797 mtime_t ephemer_date;
00798
00799
00800 for( i_index = 0; i_index < p_spu->i_filter; i_index++ )
00801 {
00802 subpicture_t *p_subpic_filter;
00803 p_subpic_filter = p_spu->pp_filter[i_index]->
00804 pf_sub_filter( p_spu->pp_filter[i_index], display_date );
00805 if( p_subpic_filter )
00806 {
00807 spu_DisplaySubpicture( p_spu, p_subpic_filter );
00808 }
00809 }
00810
00811
00812
00813 for( i_channel = 0; i_channel < p_spu->i_channel; i_channel++ )
00814 {
00815 p_ephemer = 0;
00816 ephemer_date = 0;
00817
00818 for( i_index = 0; i_index < VOUT_MAX_SUBPICTURES; i_index++ )
00819 {
00820 if( p_spu->p_subpicture[i_index].i_channel != i_channel ||
00821 p_spu->p_subpicture[i_index].i_status != READY_SUBPICTURE )
00822 {
00823 continue;
00824 }
00825 if( display_date &&
00826 display_date < p_spu->p_subpicture[i_index].i_start )
00827 {
00828
00829 continue;
00830 }
00831
00832 if( p_spu->p_subpicture[i_index].i_start > ephemer_date )
00833 ephemer_date = p_spu->p_subpicture[i_index].i_start;
00834
00835 if( display_date > p_spu->p_subpicture[i_index].i_stop &&
00836 ( !p_spu->p_subpicture[i_index].b_ephemer ||
00837 p_spu->p_subpicture[i_index].i_stop >
00838 p_spu->p_subpicture[i_index].i_start ) )
00839 {
00840
00841 spu_DestroySubpicture( p_spu, &p_spu->p_subpicture[i_index] );
00842 continue;
00843 }
00844
00845
00846 if( p_spu->p_subpicture[i_index].b_ephemer )
00847 {
00848 p_spu->p_subpicture[i_index].p_next = p_ephemer;
00849 p_ephemer = &p_spu->p_subpicture[i_index];
00850
00851 continue;
00852 }
00853
00854 p_spu->p_subpicture[i_index].p_next = p_subpic;
00855 p_subpic = &p_spu->p_subpicture[i_index];
00856 }
00857
00858
00859
00860 while( p_ephemer != NULL )
00861 {
00862 subpicture_t *p_tmp = p_ephemer;
00863 p_ephemer = p_ephemer->p_next;
00864
00865 if( p_tmp->i_start < ephemer_date )
00866 {
00867
00868 spu_DestroySubpicture( p_spu, p_tmp );
00869 }
00870 else
00871 {
00872
00873 p_tmp->p_next = p_subpic;
00874 p_subpic = p_tmp;
00875 }
00876 }
00877 }
00878
00879 return p_subpic;
00880 }
00881
00882
00883
00884
00885
00886
00887
00888 static void SpuClearChannel( spu_t *p_spu, int i_channel )
00889 {
00890 int i_subpic;
00891 subpicture_t *p_subpic = NULL;
00892
00893 vlc_mutex_lock( &p_spu->subpicture_lock );
00894
00895 for( i_subpic = 0; i_subpic < VOUT_MAX_SUBPICTURES; i_subpic++ )
00896 {
00897 p_subpic = &p_spu->p_subpicture[i_subpic];
00898 if( p_subpic->i_status == FREE_SUBPICTURE
00899 || ( p_subpic->i_status != RESERVED_SUBPICTURE
00900 && p_subpic->i_status != READY_SUBPICTURE ) )
00901 {
00902 continue;
00903 }
00904
00905 if( p_subpic->i_channel == i_channel )
00906 {
00907 while( p_subpic->p_region )
00908 {
00909 subpicture_region_t *p_region = p_subpic->p_region;
00910 p_subpic->p_region = p_region->p_next;
00911 spu_DestroyRegion( p_spu, p_region );
00912 }
00913
00914 if( p_subpic->pf_destroy ) p_subpic->pf_destroy( p_subpic );
00915 p_subpic->i_status = FREE_SUBPICTURE;
00916 }
00917 }
00918
00919 vlc_mutex_unlock( &p_spu->subpicture_lock );
00920 }
00921
00922
00923
00924
00925 static int spu_vaControlDefault( spu_t *p_spu, int i_query, va_list args )
00926 {
00927 int *pi, i;
00928
00929 switch( i_query )
00930 {
00931 case SPU_CHANNEL_REGISTER:
00932 pi = (int *)va_arg( args, int * );
00933 if( pi ) *pi = p_spu->i_channel++;
00934 msg_Dbg( p_spu, "Registering subpicture channel, ID: %i",
00935 p_spu->i_channel - 1 );
00936 break;
00937
00938 case SPU_CHANNEL_CLEAR:
00939 i = (int)va_arg( args, int );
00940 SpuClearChannel( p_spu, i );
00941 break;
00942
00943 default:
00944 msg_Dbg( p_spu, "control query not supported" );
00945 return VLC_EGENERIC;
00946 }
00947
00948 return VLC_SUCCESS;
00949 }
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961 static void UpdateSPU( spu_t *p_spu, vlc_object_t *p_object )
00962 {
00963 vlc_value_t val;
00964
00965 p_spu->b_force_palette = VLC_FALSE;
00966 p_spu->b_force_crop = VLC_FALSE;
00967
00968 if( var_Get( p_object, "highlight", &val ) || !val.b_bool ) return;
00969
00970 p_spu->b_force_crop = VLC_TRUE;
00971 var_Get( p_object, "x-start", &val );
00972 p_spu->i_crop_x = val.i_int;
00973 var_Get( p_object, "y-start", &val );
00974 p_spu->i_crop_y = val.i_int;
00975 var_Get( p_object, "x-end", &val );
00976 p_spu->i_crop_width = val.i_int - p_spu->i_crop_x;
00977 var_Get( p_object, "y-end", &val );
00978 p_spu->i_crop_height = val.i_int - p_spu->i_crop_y;
00979
00980 if( var_Get( p_object, "menu-palette", &val ) == VLC_SUCCESS )
00981 {
00982 memcpy( p_spu->palette, val.p_address, 16 );
00983 p_spu->b_force_palette = VLC_TRUE;
00984 }
00985
00986 msg_Dbg( p_object, "crop: %i,%i,%i,%i, palette forced: %i",
00987 p_spu->i_crop_x, p_spu->i_crop_y,
00988 p_spu->i_crop_width, p_spu->i_crop_height,
00989 p_spu->b_force_palette );
00990 }
00991
00992
00993
00994
00995
00996
00997 static int CropCallback( vlc_object_t *p_object, char const *psz_var,
00998 vlc_value_t oldval, vlc_value_t newval, void *p_data )
00999 {
01000 UpdateSPU( (spu_t *)p_data, p_object );
01001 return VLC_SUCCESS;
01002 }
01003
01004
01005
01006
01007 static subpicture_t *sub_new_buffer( filter_t *p_filter )
01008 {
01009 filter_owner_sys_t *p_sys = p_filter->p_owner;
01010 subpicture_t *p_subpicture = spu_CreateSubpicture( p_sys->p_spu );
01011 if( p_subpicture ) p_subpicture->i_channel = p_sys->i_channel;
01012 return p_subpicture;
01013 }
01014
01015 static void sub_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
01016 {
01017 filter_owner_sys_t *p_sys = p_filter->p_owner;
01018 spu_DestroySubpicture( p_sys->p_spu, p_subpic );
01019 }
01020
01021 static subpicture_t *spu_new_buffer( filter_t *p_filter )
01022 {
01023 subpicture_t *p_subpic = (subpicture_t *)malloc(sizeof(subpicture_t));
01024 memset( p_subpic, 0, sizeof(subpicture_t) );
01025 p_subpic->b_absolute = VLC_TRUE;
01026
01027 p_subpic->pf_create_region = __spu_CreateRegion;
01028 p_subpic->pf_make_region = __spu_MakeRegion;
01029 p_subpic->pf_destroy_region = __spu_DestroyRegion;
01030
01031 return p_subpic;
01032 }
01033
01034 static void spu_del_buffer( filter_t *p_filter, subpicture_t *p_subpic )
01035 {
01036 while( p_subpic->p_region )
01037 {
01038 subpicture_region_t *p_region = p_subpic->p_region;
01039 p_subpic->p_region = p_region->p_next;
01040 p_subpic->pf_destroy_region( VLC_OBJECT(p_filter), p_region );
01041 }
01042
01043 free( p_subpic );
01044 }
01045
01046 static picture_t *spu_new_video_buffer( filter_t *p_filter )
01047 {
01048 picture_t *p_picture = malloc( sizeof(picture_t) );
01049
01050 if( vout_AllocatePicture( p_filter, p_picture,
01051 p_filter->fmt_out.video.i_chroma,
01052 p_filter->fmt_out.video.i_width,
01053 p_filter->fmt_out.video.i_height,
01054 p_filter->fmt_out.video.i_aspect )
01055 != VLC_SUCCESS )
01056 {
01057 free( p_picture );
01058 return NULL;
01059 }
01060
01061 p_picture->pf_release = RegionPictureRelease;
01062
01063 return p_picture;
01064 }
01065
01066 static void spu_del_video_buffer( filter_t *p_filter, picture_t *p_pic )
01067 {
01068 if( p_pic && p_pic->p_data_orig ) free( p_pic->p_data_orig );
01069 if( p_pic ) free( p_pic );
01070 }