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
00030
00031
00032 #include <stdlib.h>
00033
00034 #include <vlc/vlc.h>
00035
00036 #ifdef HAVE_SYS_TIMES_H
00037 # include <sys/times.h>
00038 #endif
00039
00040 #include "vlc_video.h"
00041 #include "video_output.h"
00042 #include "vlc_spu.h"
00043 #include <vlc/input.h>
00044 #include "vlc_playlist.h"
00045
00046 #if defined( SYS_DARWIN )
00047 #include "darwin_specific.h"
00048 #endif
00049
00050
00051
00052
00053 static int InitThread ( vout_thread_t * );
00054 static void RunThread ( vout_thread_t * );
00055 static void ErrorThread ( vout_thread_t * );
00056 static void EndThread ( vout_thread_t * );
00057 static void DestroyThread ( vout_thread_t * );
00058
00059 static void AspectRatio ( int, int *, int * );
00060 static int BinaryLog ( uint32_t );
00061 static void MaskToShift ( int *, int *, uint32_t );
00062
00063
00064 static int DeinterlaceCallback( vlc_object_t *, char const *,
00065 vlc_value_t, vlc_value_t, void * );
00066 static int FilterCallback( vlc_object_t *, char const *,
00067 vlc_value_t, vlc_value_t, void * );
00068
00069
00070 int vout_Snapshot( vout_thread_t *, picture_t * );
00071
00072
00073
00074
00075
00076
00077
00078 vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout,
00079 video_format_t *p_fmt )
00080 {
00081 if( !p_fmt )
00082 {
00083
00084 if( p_vout )
00085 {
00086 vlc_object_t *p_playlist;
00087
00088 p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST,
00089 FIND_ANYWHERE );
00090
00091 if( p_playlist )
00092 {
00093 spu_Attach( p_vout->p_spu, p_this, VLC_FALSE );
00094 vlc_object_detach( p_vout );
00095 vlc_object_attach( p_vout, p_playlist );
00096
00097 vlc_object_release( p_playlist );
00098 }
00099 else
00100 {
00101 msg_Dbg( p_this, "cannot find playlist, destroying vout" );
00102 vlc_object_detach( p_vout );
00103 vout_Destroy( p_vout );
00104 }
00105 }
00106 return NULL;
00107 }
00108
00109
00110 if( p_vout )
00111 {
00112 vlc_object_yield( p_vout );
00113 }
00114 else
00115 {
00116 p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD );
00117
00118 if( !p_vout )
00119 {
00120 playlist_t *p_playlist;
00121
00122 p_playlist = vlc_object_find( p_this,
00123 VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
00124 if( p_playlist )
00125 {
00126 vlc_mutex_lock( &p_playlist->gc_lock );
00127 p_vout = vlc_object_find( p_playlist,
00128 VLC_OBJECT_VOUT, FIND_CHILD );
00129
00130 if( p_vout && p_vout->p_parent != (vlc_object_t *)p_playlist )
00131 {
00132 vlc_object_release( p_vout );
00133 p_vout = NULL;
00134 }
00135 vlc_mutex_unlock( &p_playlist->gc_lock );
00136 vlc_object_release( p_playlist );
00137 }
00138 }
00139 }
00140
00141
00142 if( p_vout )
00143 {
00144 char *psz_filter_chain;
00145 vlc_value_t val;
00146
00147
00148
00149 if( p_vout->b_filter_change )
00150 {
00151 var_Get( p_vout, "vout-filter", &val );
00152 psz_filter_chain = val.psz_string;
00153
00154 if( psz_filter_chain && !*psz_filter_chain )
00155 {
00156 free( psz_filter_chain );
00157 psz_filter_chain = NULL;
00158 }
00159 if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain )
00160 {
00161 free( p_vout->psz_filter_chain );
00162 p_vout->psz_filter_chain = NULL;
00163 }
00164
00165 if( !psz_filter_chain && !p_vout->psz_filter_chain )
00166 {
00167 p_vout->b_filter_change = VLC_FALSE;
00168 }
00169
00170 if( psz_filter_chain ) free( psz_filter_chain );
00171 }
00172
00173 if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) ||
00174 ( p_vout->fmt_render.i_height != p_fmt->i_height ) ||
00175 ( p_vout->fmt_render.i_chroma != p_fmt->i_chroma ) ||
00176 ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) ||
00177 p_vout->b_filter_change )
00178 {
00179
00180 vlc_object_detach( p_vout );
00181 vlc_object_release( p_vout );
00182 vout_Destroy( p_vout );
00183 p_vout = NULL;
00184 }
00185 else
00186 {
00187
00188 vlc_object_detach( p_vout );
00189 spu_Attach( p_vout->p_spu, p_this, VLC_TRUE );
00190 vlc_object_attach( p_vout, p_this );
00191 vlc_object_release( p_vout );
00192 }
00193 }
00194
00195 if( !p_vout )
00196 {
00197 msg_Dbg( p_this, "no usable vout present, spawning one" );
00198
00199 p_vout = vout_Create( p_this, p_fmt );
00200 }
00201
00202 return p_vout;
00203 }
00204
00205
00206
00207
00208
00209
00210
00211 vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt )
00212 {
00213 vout_thread_t * p_vout;
00214 input_thread_t * p_input_thread;
00215 int i_index;
00216 char * psz_plugin;
00217 vlc_value_t val, text;
00218
00219 unsigned int i_width = p_fmt->i_width;
00220 unsigned int i_height = p_fmt->i_height;
00221 vlc_fourcc_t i_chroma = p_fmt->i_chroma;
00222 unsigned int i_aspect = p_fmt->i_aspect;
00223
00224
00225 p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT );
00226 if( p_vout == NULL )
00227 {
00228 msg_Err( p_parent, "out of memory" );
00229 return NULL;
00230 }
00231
00232
00233
00234 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++)
00235 {
00236 p_vout->p_picture[i_index].pf_lock = NULL;
00237 p_vout->p_picture[i_index].pf_unlock = NULL;
00238 p_vout->p_picture[i_index].i_status = FREE_PICTURE;
00239 p_vout->p_picture[i_index].i_type = EMPTY_PICTURE;
00240 p_vout->p_picture[i_index].b_slow = 0;
00241 }
00242
00243
00244 p_vout->i_heap_size = 0;
00245
00246
00247 I_RENDERPICTURES = 0;
00248
00249 vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den,
00250 p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 );
00251 p_vout->fmt_render = *p_fmt;
00252 p_vout->fmt_in = *p_fmt;
00253
00254 p_vout->render.i_width = i_width;
00255 p_vout->render.i_height = i_height;
00256 p_vout->render.i_chroma = i_chroma;
00257 p_vout->render.i_aspect = i_aspect;
00258
00259 p_vout->render.i_rmask = 0;
00260 p_vout->render.i_gmask = 0;
00261 p_vout->render.i_bmask = 0;
00262
00263 p_vout->render.i_last_used_pic = -1;
00264 p_vout->render.b_allow_modify_pics = 1;
00265
00266
00267 I_OUTPUTPICTURES = 0;
00268 p_vout->output.i_width = 0;
00269 p_vout->output.i_height = 0;
00270 p_vout->output.i_chroma = 0;
00271 p_vout->output.i_aspect = 0;
00272
00273 p_vout->output.i_rmask = 0;
00274 p_vout->output.i_gmask = 0;
00275 p_vout->output.i_bmask = 0;
00276
00277
00278 p_vout->i_changes = 0;
00279 p_vout->f_gamma = 0;
00280 p_vout->b_grayscale = 0;
00281 p_vout->b_info = 0;
00282 p_vout->b_interface = 0;
00283 p_vout->b_scale = 1;
00284 p_vout->b_fullscreen = 0;
00285 p_vout->i_alignment = 0;
00286 p_vout->render_time = 10;
00287 p_vout->c_fps_samples = 0;
00288 p_vout->b_filter_change = 0;
00289 p_vout->pf_control = 0;
00290 p_vout->p_parent_intf = 0;
00291 p_vout->i_par_num = p_vout->i_par_den = 1;
00292
00293
00294 vlc_mutex_init( p_vout, &p_vout->picture_lock );
00295 vlc_mutex_init( p_vout, &p_vout->change_lock );
00296
00297
00298 var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER );
00299 var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER );
00300 var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER );
00301 var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL );
00302 var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER );
00303
00304
00305 p_vout->p_spu = spu_Create( p_vout );
00306 spu_Attach( p_vout->p_spu, p_parent, VLC_TRUE );
00307
00308
00309 vlc_object_attach( p_vout, p_parent );
00310
00311 spu_Init( p_vout->p_spu );
00312
00313
00314 vout_IntfInit( p_vout );
00315
00316
00317
00318 if( p_parent->i_object_type != VLC_OBJECT_VOUT )
00319 {
00320
00321 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
00322 var_Get( p_vout, "vout-filter", &val );
00323 p_vout->psz_filter_chain = val.psz_string;
00324 }
00325 else
00326 {
00327
00328 char *psz_end;
00329
00330 psz_end = strchr( ((vout_thread_t *)p_parent)->psz_filter_chain, ':' );
00331 if( psz_end && *(psz_end+1) )
00332 p_vout->psz_filter_chain = strdup( psz_end+1 );
00333 else p_vout->psz_filter_chain = NULL;
00334 }
00335
00336
00337 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
00338 {
00339 var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
00340 var_Get( p_vout, "vout", &val );
00341 psz_plugin = val.psz_string;
00342 }
00343 else
00344 {
00345
00346
00347 char *psz_end;
00348
00349 psz_end = strchr( p_vout->psz_filter_chain, ':' );
00350 if( psz_end )
00351 psz_plugin = strndup( p_vout->psz_filter_chain,
00352 psz_end - p_vout->psz_filter_chain );
00353 else psz_plugin = strdup( p_vout->psz_filter_chain );
00354 }
00355
00356
00357 p_vout->p_module = module_Need( p_vout,
00358 ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ?
00359 "video filter" : "video output", psz_plugin, 0 );
00360
00361 if( psz_plugin ) free( psz_plugin );
00362 if( p_vout->p_module == NULL )
00363 {
00364 msg_Err( p_vout, "no suitable vout module" );
00365 vlc_object_detach( p_vout );
00366 vlc_object_destroy( p_vout );
00367 return NULL;
00368 }
00369
00370
00371 var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE );
00372 text.psz_string = _("Deinterlace");
00373 var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL );
00374 val.psz_string = ""; text.psz_string = _("Disable");
00375 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
00376 val.psz_string = "discard"; text.psz_string = _("Discard");
00377 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
00378 val.psz_string = "blend"; text.psz_string = _("Blend");
00379 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
00380 val.psz_string = "mean"; text.psz_string = _("Mean");
00381 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
00382 val.psz_string = "bob"; text.psz_string = _("Bob");
00383 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
00384 val.psz_string = "linear"; text.psz_string = _("Linear");
00385 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
00386 val.psz_string = "x"; text.psz_string = "X";
00387 var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text );
00388
00389 if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS )
00390 {
00391 var_Set( p_vout, "deinterlace", val );
00392 if( val.psz_string ) free( val.psz_string );
00393 }
00394 var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL );
00395
00396
00397 var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
00398 text.psz_string = _("Filters");
00399 var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL );
00400 var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL );
00401
00402
00403 p_input_thread = (input_thread_t *)vlc_object_find( p_vout,
00404 VLC_OBJECT_INPUT, FIND_ANYWHERE );
00405 if( p_input_thread )
00406 {
00407 p_vout->i_pts_delay = p_input_thread->i_pts_delay;
00408 vlc_object_release( p_input_thread );
00409 }
00410 else
00411 {
00412 p_vout->i_pts_delay = DEFAULT_PTS_DELAY;
00413 }
00414
00415 if( vlc_thread_create( p_vout, "video output", RunThread,
00416 VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE ) )
00417 {
00418 msg_Err( p_vout, "out of memory" );
00419 module_Unneed( p_vout, p_vout->p_module );
00420 vlc_object_detach( p_vout );
00421 vlc_object_destroy( p_vout );
00422 return NULL;
00423 }
00424
00425 if( p_vout->b_error )
00426 {
00427 msg_Err( p_vout, "video output creation failed" );
00428
00429
00430 p_vout->b_die = VLC_TRUE;
00431
00432 vlc_thread_join( p_vout );
00433
00434 vlc_object_detach( p_vout );
00435 vlc_object_destroy( p_vout );
00436 return NULL;
00437 }
00438
00439 return p_vout;
00440 }
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450 void vout_Destroy( vout_thread_t *p_vout )
00451 {
00452 vlc_object_t *p_playlist;
00453
00454
00455 p_vout->b_die = VLC_TRUE;
00456 vlc_thread_join( p_vout );
00457
00458 var_Destroy( p_vout, "intf-change" );
00459
00460 p_playlist = vlc_object_find( p_vout, VLC_OBJECT_PLAYLIST,
00461 FIND_ANYWHERE );
00462
00463 if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain );
00464
00465
00466 vlc_object_destroy( p_vout );
00467
00468
00469 if( p_playlist != NULL )
00470 {
00471 vout_thread_t *p_another_vout = vlc_object_find( p_playlist,
00472 VLC_OBJECT_VOUT, FIND_ANYWHERE );
00473 if( p_another_vout == NULL )
00474 {
00475 vlc_value_t val;
00476 val.b_bool = VLC_TRUE;
00477 var_Set( p_playlist, "intf-show", val );
00478 }
00479 else
00480 {
00481 vlc_object_release( p_another_vout );
00482 }
00483 vlc_object_release( p_playlist );
00484 }
00485 }
00486
00487
00488
00489
00490
00491
00492
00493
00494 static int InitThread( vout_thread_t *p_vout )
00495 {
00496 int i, i_aspect_x, i_aspect_y;
00497
00498 vlc_mutex_lock( &p_vout->change_lock );
00499
00500 #ifdef STATS
00501 p_vout->c_loops = 0;
00502 #endif
00503
00504
00505 if( p_vout->pf_init( p_vout ) )
00506 {
00507 vlc_mutex_unlock( &p_vout->change_lock );
00508 return VLC_EGENERIC;
00509 }
00510
00511 if( !I_OUTPUTPICTURES )
00512 {
00513 msg_Err( p_vout, "plugin was unable to allocate at least "
00514 "one direct buffer" );
00515 p_vout->pf_end( p_vout );
00516 vlc_mutex_unlock( &p_vout->change_lock );
00517 return VLC_EGENERIC;
00518 }
00519
00520 if( I_OUTPUTPICTURES > VOUT_MAX_PICTURES )
00521 {
00522 msg_Err( p_vout, "plugin allocated too many direct buffers, "
00523 "our internal buffers must have overflown." );
00524 p_vout->pf_end( p_vout );
00525 vlc_mutex_unlock( &p_vout->change_lock );
00526 return VLC_EGENERIC;
00527 }
00528
00529 msg_Dbg( p_vout, "got %i direct buffer(s)", I_OUTPUTPICTURES );
00530
00531 AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y );
00532
00533 msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), "
00534 "chroma %4.4s, ar %i:%i, sar %i:%i",
00535 p_vout->fmt_render.i_width, p_vout->fmt_render.i_height,
00536 p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset,
00537 p_vout->fmt_render.i_visible_width,
00538 p_vout->fmt_render.i_visible_height,
00539 (char*)&p_vout->fmt_render.i_chroma,
00540 i_aspect_x, i_aspect_y,
00541 p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den );
00542
00543 AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y );
00544
00545 msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), "
00546 "chroma %4.4s, ar %i:%i, sar %i:%i",
00547 p_vout->fmt_in.i_width, p_vout->fmt_in.i_height,
00548 p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset,
00549 p_vout->fmt_in.i_visible_width,
00550 p_vout->fmt_in.i_visible_height,
00551 (char*)&p_vout->fmt_in.i_chroma,
00552 i_aspect_x, i_aspect_y,
00553 p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den );
00554
00555 if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height )
00556 {
00557 p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width =
00558 p_vout->output.i_width;
00559 p_vout->fmt_out.i_height = p_vout->fmt_out.i_visible_height =
00560 p_vout->output.i_height;
00561 p_vout->fmt_out.i_x_offset = p_vout->fmt_out.i_y_offset = 0;
00562
00563 p_vout->fmt_out.i_aspect = p_vout->output.i_aspect;
00564 p_vout->fmt_out.i_chroma = p_vout->output.i_chroma;
00565 }
00566 if( !p_vout->fmt_out.i_sar_num || !p_vout->fmt_out.i_sar_num )
00567 {
00568 p_vout->fmt_out.i_sar_num = p_vout->fmt_out.i_aspect *
00569 p_vout->fmt_out.i_height;
00570 p_vout->fmt_out.i_sar_den = VOUT_ASPECT_FACTOR *
00571 p_vout->fmt_out.i_width;
00572 }
00573
00574 vlc_ureduce( &p_vout->fmt_out.i_sar_num, &p_vout->fmt_out.i_sar_den,
00575 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, 0 );
00576
00577 AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y );
00578
00579 msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), "
00580 "chroma %4.4s, ar %i:%i, sar %i:%i",
00581 p_vout->fmt_out.i_width, p_vout->fmt_out.i_height,
00582 p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset,
00583 p_vout->fmt_out.i_visible_width,
00584 p_vout->fmt_out.i_visible_height,
00585 (char*)&p_vout->fmt_out.i_chroma,
00586 i_aspect_x, i_aspect_y,
00587 p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den );
00588
00589
00590 MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift,
00591 p_vout->output.i_rmask );
00592 MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift,
00593 p_vout->output.i_gmask );
00594 MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift,
00595 p_vout->output.i_bmask );
00596
00597
00598
00599 if( ( p_vout->output.i_width == p_vout->render.i_width )
00600 && ( p_vout->output.i_height == p_vout->render.i_height )
00601 && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) )
00602 {
00603
00604
00605
00606
00607 p_vout->b_direct = 1;
00608
00609 for( i = 1; i < VOUT_MAX_PICTURES; i++ )
00610 {
00611 if( p_vout->p_picture[ i ].i_type != DIRECT_PICTURE &&
00612 I_RENDERPICTURES >= VOUT_MIN_DIRECT_PICTURES - 1 &&
00613 p_vout->p_picture[ i - 1 ].i_type == DIRECT_PICTURE )
00614 {
00615
00616
00617 break;
00618 }
00619 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
00620 I_RENDERPICTURES++;
00621 }
00622
00623 msg_Dbg( p_vout, "direct render, mapping "
00624 "render pictures 0-%i to system pictures 1-%i",
00625 VOUT_MAX_PICTURES - 2, VOUT_MAX_PICTURES - 1 );
00626 }
00627 else
00628 {
00629
00630
00631
00632 p_vout->b_direct = 0;
00633
00634
00635 p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 );
00636
00637 if( p_vout->chroma.p_module == NULL )
00638 {
00639 msg_Err( p_vout, "no chroma module for %4.4s to %4.4s",
00640 (char*)&p_vout->render.i_chroma,
00641 (char*)&p_vout->output.i_chroma );
00642 p_vout->pf_end( p_vout );
00643 vlc_mutex_unlock( &p_vout->change_lock );
00644 return VLC_EGENERIC;
00645 }
00646
00647 msg_Dbg( p_vout, "indirect render, mapping "
00648 "render pictures 0-%i to system pictures %i-%i",
00649 VOUT_MAX_PICTURES - 1, I_OUTPUTPICTURES,
00650 I_OUTPUTPICTURES + VOUT_MAX_PICTURES - 1 );
00651
00652
00653 for( i = I_OUTPUTPICTURES; i < 2 * VOUT_MAX_PICTURES; i++ )
00654 {
00655 PP_RENDERPICTURE[ I_RENDERPICTURES ] = &p_vout->p_picture[ i ];
00656 I_RENDERPICTURES++;
00657
00658
00659 if( I_RENDERPICTURES == VOUT_MAX_PICTURES )
00660 break;
00661 }
00662 }
00663
00664
00665 for( i = 0 ; i < I_RENDERPICTURES ; i++ )
00666 {
00667 PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render;
00668 }
00669
00670 for( i = 0 ; i < I_OUTPUTPICTURES ; i++ )
00671 {
00672 PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output;
00673 }
00674
00675
00676 return VLC_SUCCESS;
00677 }
00678
00679
00680
00681
00682
00683
00684
00685
00686 static void RunThread( vout_thread_t *p_vout)
00687 {
00688 int i_index;
00689 int i_idle_loops = 0;
00690 mtime_t current_date;
00691 mtime_t display_date;
00692
00693 picture_t * p_picture;
00694 picture_t * p_last_picture = NULL;
00695 picture_t * p_directbuffer;
00696
00697 subpicture_t * p_subpic = NULL;
00698
00699
00700
00701
00702 p_vout->b_error = InitThread( p_vout );
00703
00704
00705 vlc_thread_ready( p_vout );
00706
00707 if( p_vout->b_error )
00708 {
00709
00710 DestroyThread( p_vout );
00711 return;
00712 }
00713
00714
00715
00716
00717
00718 while( (!p_vout->b_die) && (!p_vout->b_error) )
00719 {
00720
00721 p_picture = NULL;
00722 display_date = 0;
00723 current_date = mdate();
00724
00725 #if 0
00726 p_vout->c_loops++;
00727 if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) )
00728 {
00729 msg_Dbg( p_vout, "picture heap: %d/%d",
00730 I_RENDERPICTURES, p_vout->i_heap_size );
00731 }
00732 #endif
00733
00734
00735
00736
00737
00738 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
00739 {
00740 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
00741 && ( (p_picture == NULL) ||
00742 (PP_RENDERPICTURE[i_index]->date < display_date) ) )
00743 {
00744 p_picture = PP_RENDERPICTURE[i_index];
00745 display_date = p_picture->date;
00746 }
00747 }
00748
00749 if( p_picture )
00750 {
00751
00752
00753 if( p_picture == p_last_picture )
00754 {
00755 for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ )
00756 {
00757 if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE)
00758 && (PP_RENDERPICTURE[i_index] != p_last_picture)
00759 && ((p_picture == p_last_picture) ||
00760 (PP_RENDERPICTURE[i_index]->date < display_date)) )
00761 {
00762 p_picture = PP_RENDERPICTURE[i_index];
00763 display_date = p_picture->date;
00764 }
00765 }
00766 }
00767
00768
00769 if( p_last_picture && p_picture != p_last_picture )
00770 {
00771 vlc_mutex_lock( &p_vout->picture_lock );
00772 if( p_last_picture->i_refcount )
00773 {
00774 p_last_picture->i_status = DISPLAYED_PICTURE;
00775 }
00776 else
00777 {
00778 p_last_picture->i_status = DESTROYED_PICTURE;
00779 p_vout->i_heap_size--;
00780 }
00781 vlc_mutex_unlock( &p_vout->picture_lock );
00782 p_last_picture = NULL;
00783 }
00784
00785
00786 p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ]
00787 = display_date;
00788
00789 if( !p_picture->b_force &&
00790 p_picture != p_last_picture &&
00791 display_date < current_date + p_vout->render_time )
00792 {
00793
00794
00795 vlc_mutex_lock( &p_vout->picture_lock );
00796 if( p_picture->i_refcount )
00797 {
00798
00799
00800 p_picture->i_status = DISPLAYED_PICTURE;
00801 }
00802 else
00803 {
00804
00805 p_picture->i_status = DESTROYED_PICTURE;
00806 p_vout->i_heap_size--;
00807 }
00808 msg_Warn( p_vout, "late picture skipped ("I64Fd")",
00809 current_date - display_date );
00810 vlc_mutex_unlock( &p_vout->picture_lock );
00811
00812 continue;
00813 }
00814
00815 if( display_date >
00816 current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY )
00817 {
00818
00819 vlc_mutex_lock( &p_vout->picture_lock );
00820 if( p_picture->i_refcount )
00821 {
00822
00823
00824 p_picture->i_status = DISPLAYED_PICTURE;
00825 }
00826 else
00827 {
00828
00829 p_picture->i_status = DESTROYED_PICTURE;
00830 p_vout->i_heap_size--;
00831 }
00832 msg_Warn( p_vout, "vout warning: early picture skipped "
00833 "("I64Fd")", display_date - current_date
00834 - p_vout->i_pts_delay );
00835 vlc_mutex_unlock( &p_vout->picture_lock );
00836
00837 continue;
00838 }
00839
00840 if( display_date > current_date + VOUT_DISPLAY_DELAY )
00841 {
00842
00843
00844
00845
00846 p_picture = NULL;
00847 display_date = 0;
00848 }
00849 else if( p_picture == p_last_picture )
00850 {
00851
00852
00853 if( i_idle_loops < 4 )
00854 {
00855 p_picture = NULL;
00856 display_date = 0;
00857 }
00858 else
00859 {
00860
00861
00862 display_date = current_date + p_vout->render_time;
00863 }
00864 }
00865 }
00866
00867 if( p_picture == NULL )
00868 {
00869 i_idle_loops++;
00870 }
00871
00872 if( p_picture && p_vout->b_snapshot )
00873 {
00874 p_vout->b_snapshot = VLC_FALSE;
00875 vout_Snapshot( p_vout, p_picture );
00876 }
00877
00878
00879
00880
00881 if( display_date > 0 )
00882 {
00883 p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date );
00884 }
00885
00886
00887
00888
00889 p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic );
00890
00891
00892
00893
00894 if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render )
00895 {
00896
00897 p_vout->pf_render( p_vout, p_directbuffer );
00898 }
00899
00900
00901
00902
00903 if( display_date != 0 && p_directbuffer != NULL )
00904 {
00905 mtime_t current_render_time = mdate() - current_date;
00906
00907 if( current_render_time < p_vout->render_time +
00908 VOUT_DISPLAY_DELAY )
00909 {
00910
00911
00912 p_vout->render_time *= 3;
00913 p_vout->render_time += current_render_time;
00914 p_vout->render_time >>= 2;
00915 }
00916 }
00917
00918
00919 vlc_mutex_unlock( &p_vout->change_lock );
00920
00921
00922 if( display_date != 0 )
00923 {
00924
00925
00926 if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain )
00927 {
00928 mwait( display_date - VOUT_MWAIT_TOLERANCE );
00929 }
00930 }
00931 else
00932 {
00933 msleep( VOUT_IDLE_SLEEP );
00934 }
00935
00936
00937
00938 vlc_mutex_lock( &p_vout->change_lock );
00939
00940
00941
00942
00943 if( p_picture != NULL && p_directbuffer != NULL )
00944 {
00945
00946 if( p_vout->pf_display )
00947 {
00948 p_vout->pf_display( p_vout, p_directbuffer );
00949 }
00950
00951
00952
00953 p_last_picture = p_picture;
00954 p_last_picture->b_force = 0;
00955 }
00956
00957 if( p_picture != NULL )
00958 {
00959
00960 i_idle_loops = 0;
00961 }
00962
00963
00964
00965
00966 if( p_vout->pf_manage && p_vout->pf_manage( p_vout ) )
00967 {
00968
00969
00970
00971 p_vout->b_error = 1;
00972 }
00973
00974 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
00975 {
00976
00977
00978
00979
00980 int i;
00981
00982 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
00983
00984 p_vout->pf_end( p_vout );
00985 for( i = 0; i < I_OUTPUTPICTURES; i++ )
00986 p_vout->p_picture[ i ].i_status = FREE_PICTURE;
00987
00988 I_OUTPUTPICTURES = 0;
00989 if( p_vout->pf_init( p_vout ) )
00990 {
00991 msg_Err( p_vout, "cannot resize display" );
00992
00993 p_vout->b_error = 1;
00994 }
00995
00996
00997 if( p_vout->chroma.p_module )
00998 {
00999 if( p_vout->chroma.p_module->pf_deactivate )
01000 p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) );
01001 p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) );
01002 }
01003 }
01004
01005 if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE )
01006 {
01007
01008
01009
01010
01011
01012 p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE;
01013
01014 if( !p_vout->b_direct )
01015 {
01016 module_Unneed( p_vout, p_vout->chroma.p_module );
01017 }
01018
01019 vlc_mutex_lock( &p_vout->picture_lock );
01020
01021 p_vout->pf_end( p_vout );
01022
01023 I_OUTPUTPICTURES = I_RENDERPICTURES = 0;
01024
01025 p_vout->b_error = InitThread( p_vout );
01026
01027 vlc_mutex_unlock( &p_vout->picture_lock );
01028 }
01029 }
01030
01031
01032
01033
01034 if( p_vout->b_error )
01035 {
01036 ErrorThread( p_vout );
01037 }
01038
01039
01040 EndThread( p_vout );
01041
01042
01043 DestroyThread( p_vout );
01044 }
01045
01046
01047
01048
01049
01050
01051
01052
01053 static void ErrorThread( vout_thread_t *p_vout )
01054 {
01055
01056 while( !p_vout->b_die )
01057 {
01058
01059 msleep( VOUT_IDLE_SLEEP );
01060 }
01061 }
01062
01063
01064
01065
01066
01067
01068
01069 static void EndThread( vout_thread_t *p_vout )
01070 {
01071 int i_index;
01072
01073 #ifdef STATS
01074 {
01075 struct tms cpu_usage;
01076 times( &cpu_usage );
01077
01078 msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)",
01079 cpu_usage.tms_utime, cpu_usage.tms_stime );
01080 }
01081 #endif
01082
01083 if( !p_vout->b_direct )
01084 {
01085 module_Unneed( p_vout, p_vout->chroma.p_module );
01086 }
01087
01088
01089 for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ )
01090 {
01091 if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE )
01092 {
01093 free( p_vout->p_picture[i_index].p_data_orig );
01094 }
01095 }
01096
01097
01098 spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE );
01099 spu_Destroy( p_vout->p_spu );
01100
01101
01102 p_vout->pf_end( p_vout );
01103
01104
01105 vlc_mutex_unlock( &p_vout->change_lock );
01106 }
01107
01108
01109
01110
01111
01112
01113
01114 static void DestroyThread( vout_thread_t *p_vout )
01115 {
01116
01117 vlc_mutex_destroy( &p_vout->picture_lock );
01118 vlc_mutex_destroy( &p_vout->change_lock );
01119
01120
01121 if( p_vout && p_vout->p_module )
01122 {
01123 module_Unneed( p_vout, p_vout->p_module );
01124 }
01125 }
01126
01127
01128
01129 static int ReduceHeight( int i_ratio )
01130 {
01131 int i_dummy = VOUT_ASPECT_FACTOR;
01132 int i_pgcd = 1;
01133
01134 if( !i_ratio )
01135 {
01136 return i_pgcd;
01137 }
01138
01139
01140 while( !(i_ratio & 1) && !(i_dummy & 1) )
01141 {
01142 i_ratio >>= 1;
01143 i_dummy >>= 1;
01144 i_pgcd <<= 1;
01145 }
01146
01147 while( !(i_ratio % 3) && !(i_dummy % 3) )
01148 {
01149 i_ratio /= 3;
01150 i_dummy /= 3;
01151 i_pgcd *= 3;
01152 }
01153
01154 while( !(i_ratio % 5) && !(i_dummy % 5) )
01155 {
01156 i_ratio /= 5;
01157 i_dummy /= 5;
01158 i_pgcd *= 5;
01159 }
01160
01161 return i_pgcd;
01162 }
01163
01164 static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y )
01165 {
01166 unsigned int i_pgcd = ReduceHeight( i_aspect );
01167 *i_aspect_x = i_aspect / i_pgcd;
01168 *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd;
01169 }
01170
01171
01172
01173
01174
01175
01176
01177 static int BinaryLog( uint32_t i )
01178 {
01179 int i_log = 0;
01180
01181 if( i == 0 ) return -31337;
01182
01183 if( i & 0xffff0000 ) i_log += 16;
01184 if( i & 0xff00ff00 ) i_log += 8;
01185 if( i & 0xf0f0f0f0 ) i_log += 4;
01186 if( i & 0xcccccccc ) i_log += 2;
01187 if( i & 0xaaaaaaaa ) i_log += 1;
01188
01189 return i_log;
01190 }
01191
01192
01193
01194
01195
01196
01197 static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask )
01198 {
01199 uint32_t i_low, i_high;
01200
01201 if( !i_mask )
01202 {
01203 *pi_left = *pi_right = 0;
01204 return;
01205 }
01206
01207
01208 i_low = i_mask & (- (int32_t)i_mask);
01209 i_high = i_mask + i_low;
01210
01211
01212 i_low = BinaryLog (i_low);
01213 i_high = BinaryLog (i_high);
01214
01215
01216 *pi_left = i_low;
01217 *pi_right = (8 - i_high + i_low);
01218 }
01219
01220
01221
01222
01223 int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable,
01224 vlc_value_t old_value, vlc_value_t new_value,
01225 void * unused )
01226 {
01227 vout_thread_t * p_vout = (vout_thread_t *)p_this;
01228 vlc_value_t val;
01229 val.b_bool = VLC_TRUE;
01230 var_Set( p_vout, "intf-change", val );
01231 return VLC_SUCCESS;
01232 }
01233
01234
01235
01236
01237
01238 typedef struct suxor_thread_t
01239 {
01240 VLC_COMMON_MEMBERS
01241 input_thread_t *p_input;
01242
01243 } suxor_thread_t;
01244
01245 static void SuxorRestartVideoES( suxor_thread_t *p_this )
01246 {
01247 vlc_value_t val;
01248
01249 vlc_thread_ready( p_this );
01250
01251
01252 var_Get( p_this->p_input, "video-es", &val );
01253 if( val.i_int >= 0 )
01254 {
01255 vlc_value_t val_es;
01256 val_es.i_int = -VIDEO_ES;
01257 var_Set( p_this->p_input, "video-es", val_es );
01258 var_Set( p_this->p_input, "video-es", val );
01259 }
01260
01261 vlc_object_release( p_this->p_input );
01262
01263 #ifdef WIN32
01264 CloseHandle( p_this->thread_id );
01265 #endif
01266
01267 vlc_object_destroy( p_this );
01268 }
01269
01270
01271
01272
01273
01274 static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd,
01275 vlc_value_t oldval, vlc_value_t newval, void *p_data )
01276 {
01277 vout_thread_t *p_vout = (vout_thread_t *)p_this;
01278 input_thread_t *p_input;
01279 vlc_value_t val;
01280
01281 char *psz_mode = newval.psz_string;
01282 char *psz_filter, *psz_deinterlace = NULL;
01283
01284 var_Get( p_vout, "vout-filter", &val );
01285 psz_filter = val.psz_string;
01286 if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" );
01287
01288 if( !psz_mode || !*psz_mode )
01289 {
01290 if( psz_deinterlace )
01291 {
01292 char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1;
01293 if( psz_src[0] == ':' ) psz_src++;
01294 memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 );
01295 }
01296 }
01297 else if( !psz_deinterlace )
01298 {
01299 psz_filter = realloc( psz_filter, strlen( psz_filter ) +
01300 sizeof(":deinterlace") );
01301 if( psz_filter && *psz_filter ) strcat( psz_filter, ":" );
01302 strcat( psz_filter, "deinterlace" );
01303 }
01304
01305 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
01306 FIND_PARENT );
01307 if( !p_input ) return VLC_EGENERIC;
01308
01309 if( psz_mode && *psz_mode )
01310 {
01311
01312 val.psz_string = psz_mode;
01313 var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING );
01314 var_Set( p_input, "deinterlace-mode", val );
01315 }
01316 vlc_object_release( p_input );
01317
01318 val.b_bool = VLC_TRUE;
01319 var_Set( p_vout, "intf-change", val );
01320
01321 val.psz_string = psz_filter;
01322 var_Set( p_vout, "vout-filter", val );
01323 if( psz_filter ) free( psz_filter );
01324
01325 return VLC_SUCCESS;
01326 }
01327
01328 static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd,
01329 vlc_value_t oldval, vlc_value_t newval, void *p_data )
01330 {
01331 vout_thread_t *p_vout = (vout_thread_t *)p_this;
01332 input_thread_t *p_input;
01333 vlc_value_t val;
01334
01335 p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT,
01336 FIND_PARENT );
01337 if (!p_input)
01338 {
01339 msg_Err( p_vout, "Input not found" );
01340 return( VLC_EGENERIC );
01341 }
01342
01343 val.b_bool = VLC_TRUE;
01344 var_Set( p_vout, "intf-change", val );
01345
01346
01347 val.psz_string = newval.psz_string;
01348 var_Create( p_input, "vout-filter", VLC_VAR_STRING );
01349
01350 var_Set( p_input, "vout-filter", val );
01351
01352
01353 var_Get( p_input, "video-es", &val );
01354 if( val.i_int >= 0 )
01355 {
01356 suxor_thread_t *p_suxor =
01357 vlc_object_create( p_vout, sizeof(suxor_thread_t) );
01358 p_suxor->p_input = p_input;
01359 p_vout->b_filter_change = VLC_TRUE;
01360 vlc_object_yield( p_input );
01361 vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES,
01362 VLC_THREAD_PRIORITY_LOW, VLC_FALSE );
01363 }
01364
01365 vlc_object_release( p_input );
01366
01367 return VLC_SUCCESS;
01368 }