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 "filter_common.h"
00034
00035
00036
00037
00038 static int Create ( vlc_object_t * );
00039 static void Destroy ( vlc_object_t * );
00040
00041 static int Init ( vout_thread_t * );
00042 static void End ( vout_thread_t * );
00043 static void Render ( vout_thread_t *, picture_t * );
00044
00045 static void RemoveAllVout ( vout_thread_t *p_vout );
00046
00047 static int SendEvents( vlc_object_t *, char const *,
00048 vlc_value_t, vlc_value_t, void * );
00049
00050
00051
00052
00053 #define COLS_TEXT N_("Number of columns")
00054 #define COLS_LONGTEXT N_("Select the number of horizontal video windows in " \
00055 "which to split the video.")
00056
00057 #define ROWS_TEXT N_("Number of rows")
00058 #define ROWS_LONGTEXT N_("Select the number of vertical video windows in " \
00059 "which to split the video.")
00060
00061 #define ACTIVE_TEXT N_("Active windows")
00062 #define ACTIVE_LONGTEXT N_("Comma separated list of active windows, " \
00063 "defaults to all")
00064
00065 #define ASPECT_TEXT N_("Element aspect ratio")
00066 #define ASPECT_LONGTEXT N_("The aspect ratio of the individual displays building the display wall")
00067
00068 vlc_module_begin();
00069 set_description( _("Wall video filter") );
00070 set_shortname( N_("Image wall" ));
00071 set_capability( "video filter", 0 );
00072 set_category( CAT_VIDEO );
00073 set_subcategory( SUBCAT_VIDEO_VFILTER );
00074
00075 add_integer( "wall-cols", 3, NULL, COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
00076 add_integer( "wall-rows", 3, NULL, ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
00077 add_string( "wall-active", NULL, NULL, ACTIVE_TEXT, ACTIVE_LONGTEXT,
00078 VLC_TRUE );
00079 add_string( "wall-element-aspect", "4:3", NULL, ASPECT_TEXT, ASPECT_LONGTEXT, VLC_FALSE );
00080
00081 add_shortcut( "wall" );
00082 set_callbacks( Create, Destroy );
00083 vlc_module_end();
00084
00085
00086
00087
00088
00089
00090
00091 struct vout_sys_t
00092 {
00093 int i_col;
00094 int i_row;
00095 int i_vout;
00096 struct vout_list_t
00097 {
00098 vlc_bool_t b_active;
00099 int i_width;
00100 int i_height;
00101 int i_left;
00102 int i_top;
00103 vout_thread_t *p_vout;
00104 } *pp_vout;
00105 };
00106
00107
00108
00109
00110 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
00111 {
00112 int i_row, i_col, i_vout = 0;
00113
00114 for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
00115 {
00116 for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
00117 {
00118 vout_vaControl( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
00119 i_query, args );
00120 i_vout++;
00121 }
00122 }
00123 return VLC_SUCCESS;
00124 }
00125
00126
00127
00128
00129
00130
00131 static int Create( vlc_object_t *p_this )
00132 {
00133 vout_thread_t *p_vout = (vout_thread_t *)p_this;
00134 char *psz_method, *psz_tmp, *psz_method_tmp;
00135 int i_vout;
00136
00137
00138 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
00139 if( p_vout->p_sys == NULL )
00140 {
00141 msg_Err( p_vout, "out of memory" );
00142 return VLC_ENOMEM;
00143 }
00144
00145 p_vout->pf_init = Init;
00146 p_vout->pf_end = End;
00147 p_vout->pf_manage = NULL;
00148 p_vout->pf_render = Render;
00149 p_vout->pf_display = NULL;
00150 p_vout->pf_control = Control;
00151
00152
00153 p_vout->p_sys->i_col = config_GetInt( p_vout, "wall-cols" );
00154 p_vout->p_sys->i_row = config_GetInt( p_vout, "wall-rows" );
00155
00156 p_vout->p_sys->i_col = __MAX( 1, __MIN( 15, p_vout->p_sys->i_col ) );
00157 p_vout->p_sys->i_row = __MAX( 1, __MIN( 15, p_vout->p_sys->i_row ) );
00158
00159 msg_Dbg( p_vout, "opening a %i x %i wall",
00160 p_vout->p_sys->i_col, p_vout->p_sys->i_row );
00161
00162 p_vout->p_sys->pp_vout = malloc( p_vout->p_sys->i_row *
00163 p_vout->p_sys->i_col *
00164 sizeof(struct vout_list_t) );
00165 if( p_vout->p_sys->pp_vout == NULL )
00166 {
00167 msg_Err( p_vout, "out of memory" );
00168 free( p_vout->p_sys );
00169 return VLC_ENOMEM;
00170 }
00171
00172 psz_method_tmp = psz_method = config_GetPsz( p_vout, "wall-active" );
00173
00174
00175 if( psz_method == NULL )
00176 {
00177 for( i_vout = p_vout->p_sys->i_row * p_vout->p_sys->i_col;
00178 i_vout--; )
00179 {
00180 p_vout->p_sys->pp_vout[i_vout].b_active = 1;
00181 }
00182 }
00183
00184 else
00185 {
00186 for( i_vout = p_vout->p_sys->i_row * p_vout->p_sys->i_col;
00187 i_vout--; )
00188 {
00189 p_vout->p_sys->pp_vout[i_vout].b_active = 0;
00190 }
00191
00192 while( *psz_method )
00193 {
00194 psz_tmp = psz_method;
00195 while( *psz_tmp && *psz_tmp != ',' )
00196 {
00197 psz_tmp++;
00198 }
00199
00200 if( *psz_tmp )
00201 {
00202 *psz_tmp = '\0';
00203 i_vout = atoi( psz_method );
00204 psz_method = psz_tmp + 1;
00205 }
00206 else
00207 {
00208 i_vout = atoi( psz_method );
00209 psz_method = psz_tmp;
00210 }
00211
00212 if( i_vout >= 0 &&
00213 i_vout < p_vout->p_sys->i_row * p_vout->p_sys->i_col )
00214 {
00215 p_vout->p_sys->pp_vout[i_vout].b_active = 1;
00216 }
00217 }
00218 }
00219
00220 free( psz_method_tmp );
00221
00222 return VLC_SUCCESS;
00223 }
00224
00225
00226
00227
00228 static int Init( vout_thread_t *p_vout )
00229 {
00230 int i_index, i_row, i_col, i_width, i_height, i_left, i_top;
00231 unsigned int i_target_width,i_target_height;
00232 picture_t *p_pic;
00233 video_format_t fmt = {0};
00234 int i_aspect = 4*VOUT_ASPECT_FACTOR/3;
00235 int i_align = 0;
00236 unsigned int i_hstart, i_hend, i_vstart, i_vend;
00237 unsigned int w1,h1,w2,h2;
00238 int i_xpos, i_ypos;
00239 int i_vstart_rounded = 0, i_hstart_rounded = 0;
00240 char *psz_aspect;
00241
00242 psz_aspect = config_GetPsz( p_vout, "wall-element-aspect" );
00243 if( psz_aspect && *psz_aspect )
00244 {
00245 char *psz_parser = strchr( psz_aspect, ':' );
00246 if( psz_parser )
00247 {
00248 *psz_parser++ = '\0';
00249 i_aspect = atoi( psz_aspect ) * VOUT_ASPECT_FACTOR
00250 / atoi( psz_parser );
00251 }
00252 else
00253 {
00254 msg_Warn( p_vout, "invalid aspect ratio specification" );
00255 }
00256 free( psz_aspect );
00257 }
00258
00259
00260 i_xpos = var_CreateGetInteger( p_vout, "video-x" );
00261 i_ypos = var_CreateGetInteger( p_vout, "video-y" );
00262 if( i_xpos < 0 ) i_xpos = 0;
00263 if( i_ypos < 0 ) i_ypos = 0;
00264
00265 I_OUTPUTPICTURES = 0;
00266
00267
00268 p_vout->output.i_chroma = p_vout->render.i_chroma;
00269 p_vout->output.i_width = p_vout->render.i_width;
00270 p_vout->output.i_height = p_vout->render.i_height;
00271 p_vout->output.i_aspect = p_vout->render.i_aspect;
00272 var_Create( p_vout, "align", VLC_VAR_INTEGER );
00273
00274 fmt.i_width = fmt.i_visible_width = p_vout->render.i_width;
00275 fmt.i_height = fmt.i_visible_height = p_vout->render.i_height;
00276 fmt.i_x_offset = fmt.i_y_offset = 0;
00277 fmt.i_chroma = p_vout->render.i_chroma;
00278 fmt.i_aspect = p_vout->render.i_aspect;
00279 fmt.i_sar_num = p_vout->render.i_aspect * fmt.i_height / fmt.i_width;
00280 fmt.i_sar_den = VOUT_ASPECT_FACTOR;
00281
00282 w1 = p_vout->output.i_width / p_vout->p_sys->i_col;
00283 w1 &= ~1;
00284 h1 = w1 * VOUT_ASPECT_FACTOR / i_aspect&~1;
00285 h1 &= ~1;
00286
00287 h2 = p_vout->output.i_height / p_vout->p_sys->i_row&~1;
00288 h2 &= ~1;
00289 w2 = h2 * i_aspect / VOUT_ASPECT_FACTOR&~1;
00290 w2 &= ~1;
00291
00292 if ( h1 * p_vout->p_sys->i_row < p_vout->output.i_height )
00293 {
00294 unsigned int i_tmp;
00295 i_target_width = w2;
00296 i_target_height = h2;
00297 i_vstart = 0;
00298 i_vend = p_vout->output.i_height;
00299 i_tmp = i_target_width * p_vout->p_sys->i_col;
00300 while( i_tmp < p_vout->output.i_width ) i_tmp += p_vout->p_sys->i_col;
00301 i_hstart = (( i_tmp - p_vout->output.i_width ) / 2)&~1;
00302 i_hstart_rounded = ( ( i_tmp - p_vout->output.i_width ) % 2 ) ||
00303 ( ( ( i_tmp - p_vout->output.i_width ) / 2 ) & 1 );
00304 i_hend = i_hstart + p_vout->output.i_width;
00305 }
00306 else
00307 {
00308 unsigned int i_tmp;
00309 i_target_height = h1;
00310 i_target_width = w1;
00311 i_hstart = 0;
00312 i_hend = p_vout->output.i_width;
00313 i_tmp = i_target_height * p_vout->p_sys->i_row;
00314 while( i_tmp < p_vout->output.i_height ) i_tmp += p_vout->p_sys->i_row;
00315 i_vstart = ( ( i_tmp - p_vout->output.i_height ) / 2 ) & ~1;
00316 i_vstart_rounded = ( ( i_tmp - p_vout->output.i_height ) % 2 ) ||
00317 ( ( ( i_tmp - p_vout->output.i_height ) / 2 ) & 1 );
00318 i_vend = i_vstart + p_vout->output.i_height;
00319 }
00320 msg_Dbg( p_vout, "target resolution %dx%d", i_target_width, i_target_height );
00321
00322
00323 msg_Dbg( p_vout, "spawning the real video outputs" );
00324
00325 p_vout->p_sys->i_vout = 0;
00326 msg_Dbg( p_vout, "target window (%d,%d)-(%d,%d)", i_hstart,i_vstart,i_hend,i_vend );
00327
00328
00329 i_top = 0;
00330 i_height = 0;
00331 for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
00332 {
00333 i_left = 0;
00334 i_top += i_height;
00335 for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
00336 {
00337 i_align = 0;
00338
00339 if( i_col*i_target_width >= i_hstart &&
00340 (i_col+1)*i_target_width <= i_hend )
00341 {
00342 i_width = i_target_width;
00343 }
00344 else if( ( i_col + 1 ) * i_target_width < i_hstart ||
00345 ( i_col * i_target_width ) > i_hend )
00346 {
00347 i_width = 0;
00348 }
00349 else
00350 {
00351 i_width = ( i_target_width - i_hstart % i_target_width );
00352 if( i_col >= ( p_vout->p_sys->i_col / 2 ) )
00353 {
00354 i_align |= VOUT_ALIGN_LEFT;
00355 i_width -= i_hstart_rounded ? 2: 0;
00356 }
00357 else
00358 {
00359 i_align |= VOUT_ALIGN_RIGHT;
00360 }
00361 }
00362
00363 if( i_row * i_target_height >= i_vstart &&
00364 ( i_row + 1 ) * i_target_height <= i_vend )
00365 {
00366 i_height = i_target_height;
00367 }
00368 else if( ( i_row + 1 ) * i_target_height < i_vstart ||
00369 ( i_row * i_target_height ) > i_vend )
00370 {
00371 i_height = 0;
00372 }
00373 else
00374 {
00375 i_height = ( i_target_height -
00376 i_vstart%i_target_height );
00377 if( i_row >= ( p_vout->p_sys->i_row / 2 ) )
00378 {
00379 i_align |= VOUT_ALIGN_TOP;
00380 i_height -= i_vstart_rounded ? 2: 0;
00381 }
00382 else
00383 {
00384 i_align |= VOUT_ALIGN_BOTTOM;
00385 }
00386 }
00387 if( i_height == 0 || i_width == 0 )
00388 {
00389 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].b_active = VLC_FALSE;
00390 }
00391
00392 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_width = i_width;
00393 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_height = i_height;
00394 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_left = i_left;
00395 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].i_top = i_top;
00396 i_left += i_width;
00397
00398 if( !p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].b_active )
00399 {
00400 p_vout->p_sys->i_vout++;
00401 continue;
00402 }
00403 var_SetInteger( p_vout, "align", i_align );
00404 var_SetInteger( p_vout, "video-x", i_left + i_xpos - i_width);
00405 var_SetInteger( p_vout, "video-y", i_top + i_ypos );
00406
00407 fmt.i_width = fmt.i_visible_width = i_width;
00408 fmt.i_height = fmt.i_visible_height = i_height;
00409 fmt.i_aspect = i_aspect * i_target_height / i_height *
00410 i_width / i_target_width;
00411
00412 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout =
00413 vout_Create( p_vout, &fmt );
00414 if( !p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout )
00415 {
00416 msg_Err( p_vout, "failed to get %ix%i vout threads",
00417 p_vout->p_sys->i_col, p_vout->p_sys->i_row );
00418 RemoveAllVout( p_vout );
00419 return VLC_EGENERIC;
00420 }
00421 ADD_CALLBACKS(
00422 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout,
00423 SendEvents );
00424 p_vout->p_sys->i_vout++;
00425 }
00426 }
00427
00428 ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
00429
00430 ADD_PARENT_CALLBACKS( SendEventsToChild );
00431
00432 return VLC_SUCCESS;
00433 }
00434
00435
00436
00437
00438 static void End( vout_thread_t *p_vout )
00439 {
00440 int i_index;
00441
00442
00443 for( i_index = I_OUTPUTPICTURES ; i_index ; )
00444 {
00445 i_index--;
00446 free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
00447 }
00448 }
00449
00450
00451
00452
00453
00454
00455 static void Destroy( vlc_object_t *p_this )
00456 {
00457 vout_thread_t *p_vout = (vout_thread_t *)p_this;
00458
00459 RemoveAllVout( p_vout );
00460
00461 DEL_PARENT_CALLBACKS( SendEventsToChild );
00462
00463 free( p_vout->p_sys->pp_vout );
00464 free( p_vout->p_sys );
00465 }
00466
00467
00468
00469
00470
00471
00472
00473
00474 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
00475 {
00476 picture_t *p_outpic = NULL;
00477 int i_col, i_row, i_vout, i_plane;
00478 int pi_left_skip[VOUT_MAX_PLANES], pi_top_skip[VOUT_MAX_PLANES];
00479
00480 i_vout = 0;
00481
00482 for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
00483 {
00484 for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
00485 {
00486 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
00487 {
00488 pi_left_skip[i_plane] = p_vout->p_sys->pp_vout[ i_vout ].i_left * p_pic->p[ i_plane ].i_pitch / p_vout->output.i_width;
00489 pi_top_skip[i_plane] = (p_vout->p_sys->pp_vout[ i_vout ].i_top * p_pic->p[ i_plane ].i_lines / p_vout->output.i_height)*p_pic->p[i_plane].i_pitch;
00490 }
00491
00492 if( !p_vout->p_sys->pp_vout[ i_vout ].b_active )
00493 {
00494 i_vout++;
00495 continue;
00496 }
00497
00498 while( ( p_outpic =
00499 vout_CreatePicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
00500 0, 0, 0 )
00501 ) == NULL )
00502 {
00503 if( p_vout->b_die || p_vout->b_error )
00504 {
00505 vout_DestroyPicture(
00506 p_vout->p_sys->pp_vout[ i_vout ].p_vout, p_outpic );
00507 return;
00508 }
00509
00510 msleep( VOUT_OUTMEM_SLEEP );
00511 }
00512
00513 vout_DatePicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
00514 p_outpic, p_pic->date );
00515 vout_LinkPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
00516 p_outpic );
00517
00518 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
00519 {
00520 uint8_t *p_in, *p_in_end, *p_out;
00521 int i_in_pitch = p_pic->p[i_plane].i_pitch;
00522 int i_out_pitch = p_outpic->p[i_plane].i_pitch;
00523 int i_copy_pitch = p_outpic->p[i_plane].i_visible_pitch;
00524
00525 p_in = p_pic->p[i_plane].p_pixels
00526 + pi_top_skip[i_plane] + pi_left_skip[i_plane];
00527
00528 p_in_end = p_in + p_outpic->p[i_plane].i_visible_lines
00529 * p_pic->p[i_plane].i_pitch;
00530
00531 p_out = p_outpic->p[i_plane].p_pixels;
00532
00533 while( p_in < p_in_end )
00534 {
00535 p_vout->p_vlc->pf_memcpy( p_out, p_in, i_copy_pitch );
00536 p_in += i_in_pitch;
00537 p_out += i_out_pitch;
00538 }
00539
00540
00541 }
00542
00543 vout_UnlinkPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
00544 p_outpic );
00545 vout_DisplayPicture( p_vout->p_sys->pp_vout[ i_vout ].p_vout,
00546 p_outpic );
00547
00548 i_vout++;
00549 }
00550
00551
00552
00553
00554
00555
00556
00557
00558 }
00559 }
00560
00561
00562
00563
00564 static void RemoveAllVout( vout_thread_t *p_vout )
00565 {
00566 while( p_vout->p_sys->i_vout )
00567 {
00568 --p_vout->p_sys->i_vout;
00569 if( p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].b_active )
00570 {
00571 DEL_CALLBACKS(
00572 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout,
00573 SendEvents );
00574 vlc_object_detach(
00575 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout );
00576 vout_Destroy(
00577 p_vout->p_sys->pp_vout[ p_vout->p_sys->i_vout ].p_vout );
00578 }
00579 }
00580 }
00581
00582
00583
00584
00585 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
00586 vlc_value_t oldval, vlc_value_t newval, void *_p_vout )
00587 {
00588 vout_thread_t *p_vout = (vout_thread_t *)_p_vout;
00589 int i_vout;
00590 vlc_value_t sentval = newval;
00591
00592
00593 for( i_vout = 0; i_vout < p_vout->p_sys->i_vout; i_vout++ )
00594 {
00595 if( p_this == (vlc_object_t *)p_vout->p_sys->pp_vout[ i_vout ].p_vout )
00596 {
00597 break;
00598 }
00599 }
00600
00601 if( i_vout == p_vout->p_sys->i_vout )
00602 {
00603 return VLC_EGENERIC;
00604 }
00605
00606
00607 if( !strcmp( psz_var, "mouse-x" ) )
00608 {
00609 sentval.i_int += p_vout->output.i_width
00610 * (i_vout % p_vout->p_sys->i_col)
00611 / p_vout->p_sys->i_col;
00612 }
00613 else if( !strcmp( psz_var, "mouse-y" ) )
00614 {
00615 sentval.i_int += p_vout->output.i_height
00616 * (i_vout / p_vout->p_sys->i_row)
00617 / p_vout->p_sys->i_row;
00618 }
00619
00620 var_Set( p_vout, psz_var, sentval );
00621
00622 return VLC_SUCCESS;
00623 }
00624
00625
00626
00627
00628 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
00629 vlc_value_t oldval, vlc_value_t newval, void *p_data )
00630 {
00631 vout_thread_t *p_vout = (vout_thread_t *)p_this;
00632 int i_row, i_col, i_vout = 0;
00633
00634 for( i_row = 0; i_row < p_vout->p_sys->i_row; i_row++ )
00635 {
00636 for( i_col = 0; i_col < p_vout->p_sys->i_col; i_col++ )
00637 {
00638 var_Set( p_vout->p_sys->pp_vout[ i_vout ].p_vout, psz_var, newval);
00639 if( !strcmp( psz_var, "fullscreen" ) ) break;
00640 i_vout++;
00641 }
00642 }
00643
00644 return VLC_SUCCESS;
00645 }