X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fvideo_output%2Fvideo_output.c;h=a2110d5cbec63a0484de1e911ca06a9a829cbfc4;hb=724461bdf250e856eb32f6c0b7c51b065e482982;hp=4c115056e2b59c7bee5799be2eee1b2d66b5b5b7;hpb=1fe2080044a8757e464c856a8b592af3765a1a05;p=vlc diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c index 4c115056e2..a2110d5cbe 100644 --- a/src/video_output/video_output.c +++ b/src/video_output/video_output.c @@ -47,6 +47,7 @@ #include #include +#include #if defined( __APPLE__ ) /* Include darwin_specific.h here if needed */ @@ -54,56 +55,92 @@ /** FIXME This is quite ugly but needed while we don't have counters * helpers */ -#include "input/input_internal.h" +//#include "input/input_internal.h" -#include "modules/modules.h" -#include +#include +#include +#include "vout_pictures.h" +#include "vout_internal.h" /***************************************************************************** * Local prototypes *****************************************************************************/ static int InitThread ( vout_thread_t * ); -static void RunThread ( vout_thread_t * ); +static void* RunThread ( void * ); static void ErrorThread ( vout_thread_t * ); static void CleanThread ( vout_thread_t * ); static void EndThread ( vout_thread_t * ); static void AspectRatio ( int, int *, int * ); -static int BinaryLog ( uint32_t ); -static void MaskToShift ( int *, int *, uint32_t ); + +static void VideoFormatImportRgb( video_format_t *, const picture_heap_t * ); +static void PictureHeapFixRgb( picture_heap_t * ); static void vout_Destructor ( vlc_object_t * p_this ); /* Object variables callbacks */ -static int DeinterlaceCallback( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); static int FilterCallback( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); static int VideoFilter2Callback( vlc_object_t *, char const *, vlc_value_t, vlc_value_t, void * ); +/* */ +static void PostProcessEnable( vout_thread_t * ); +static void PostProcessDisable( vout_thread_t * ); +static void PostProcessSetFilterQuality( vout_thread_t *p_vout ); +static int PostProcessCallback( vlc_object_t *, char const *, + vlc_value_t, vlc_value_t, void * ); +/* */ +static void DeinterlaceEnable( vout_thread_t * ); + /* From vout_intf.c */ int vout_Snapshot( vout_thread_t *, picture_t * ); /* Display media title in OSD */ static void DisplayTitleOnOSD( vout_thread_t *p_vout ); +/* Time during which the thread will sleep if it has nothing to + * display (in micro-seconds) */ +#define VOUT_IDLE_SLEEP ((int)(0.020*CLOCK_FREQ)) + +/* Maximum lap of time allowed between the beginning of rendering and + * display. If, compared to the current date, the next image is too + * late, the thread will perform an idle loop. This time should be + * at least VOUT_IDLE_SLEEP plus the time required to render a few + * images, to avoid trashing of decoded images */ +#define VOUT_DISPLAY_DELAY ((int)(0.200*CLOCK_FREQ)) + +/* Better be in advance when awakening than late... */ +#define VOUT_MWAIT_TOLERANCE ((mtime_t)(0.020*CLOCK_FREQ)) + +/* Minimum number of direct pictures the video output will accept without + * creating additional pictures in system memory */ +#ifdef OPTIMIZE_MEMORY +# define VOUT_MIN_DIRECT_PICTURES (VOUT_MAX_PICTURES/2) +#else +# define VOUT_MIN_DIRECT_PICTURES (3*VOUT_MAX_PICTURES/4) +#endif + /***************************************************************************** * Video Filter2 functions *****************************************************************************/ static picture_t *video_new_buffer_filter( filter_t *p_filter ) { - picture_t *p_picture; vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner; + picture_t *p_picture = vout_CreatePicture( p_vout, 0, 0, 0 ); - p_picture = vout_CreatePicture( p_vout, 0, 0, 0 ); + p_picture->i_status = READY_PICTURE; return p_picture; } static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic ) { - vout_DestroyPicture( (vout_thread_t*)p_filter->p_owner, p_pic ); + vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner; + + vlc_mutex_lock( &p_vout->picture_lock ); + vout_UsePictureLocked( p_vout, p_pic ); + vlc_mutex_unlock( &p_vout->picture_lock ); } static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data ) @@ -129,20 +166,14 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout, * TODO: support for reusing video outputs with proper _thread-safe_ * reference handling. */ if( p_vout ) - { - spu_Attach( p_vout->p_spu, p_this, false ); - vlc_object_kill( p_vout ); - vlc_thread_join( p_vout ); - module_Unneed( p_vout, p_vout->p_module ); - vlc_object_release( p_vout ); - } + vout_CloseAndRelease( p_vout ); return NULL; } /* If a video output was provided, lock it, otherwise look for one. */ if( p_vout ) { - vlc_object_yield( p_vout ); + vlc_object_hold( p_vout ); } /* TODO: find a suitable unused video output */ @@ -150,54 +181,102 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout, /* If we now have a video output, check it has the right properties */ if( p_vout ) { - char *psz_filter_chain; - vlc_value_t val; + vlc_mutex_lock( &p_vout->change_lock ); /* We don't directly check for the "vout-filter" variable for obvious * performance reasons. */ - if( p_vout->b_filter_change ) + if( p_vout->p->b_filter_change ) { - var_Get( p_vout, "vout-filter", &val ); - psz_filter_chain = val.psz_string; + char *psz_filter_chain = var_GetString( p_vout, "vout-filter" ); if( psz_filter_chain && !*psz_filter_chain ) { free( psz_filter_chain ); psz_filter_chain = NULL; } - if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain ) + if( p_vout->p->psz_filter_chain && !*p_vout->p->psz_filter_chain ) { - free( p_vout->psz_filter_chain ); - p_vout->psz_filter_chain = NULL; + free( p_vout->p->psz_filter_chain ); + p_vout->p->psz_filter_chain = NULL; } - if( !psz_filter_chain && !p_vout->psz_filter_chain ) + if( !psz_filter_chain && !p_vout->p->psz_filter_chain ) { - p_vout->b_filter_change = false; + p_vout->p->b_filter_change = false; } free( psz_filter_chain ); } - if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) || - ( p_vout->fmt_render.i_height != p_fmt->i_height ) || - ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) || - p_vout->b_filter_change ) + if( p_vout->fmt_render.i_chroma != vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma ) || + p_vout->fmt_render.i_width != p_fmt->i_width || + p_vout->fmt_render.i_height != p_fmt->i_height || + p_vout->p->b_filter_change ) { + vlc_mutex_unlock( &p_vout->change_lock ); + /* We are not interested in this format, close this vout */ - vlc_object_release( p_vout ); + vout_CloseAndRelease( p_vout ); vlc_object_release( p_vout ); p_vout = NULL; } else { /* This video output is cool! Hijack it. */ - spu_Attach( p_vout->p_spu, p_this, true ); - vlc_object_attach( p_vout, p_this ); - if( p_vout->b_title_show ) - DisplayTitleOnOSD( p_vout ); + if( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) + { + /* Correct aspect ratio on change + * FIXME factorize this code with other aspect ration related code */ + unsigned int i_sar_num; + unsigned int i_sar_den; + unsigned int i_aspect; + + i_aspect = p_fmt->i_aspect; + vlc_ureduce( &i_sar_num, &i_sar_den, + p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 ); +#if 0 + /* What's that, it does not seems to be used correcly everywhere + * beside the previous p_vout->fmt_render.i_aspect != p_fmt->i_aspect + * should be fixed to use it too then */ + if( p_vout->i_par_num > 0 && p_vout->i_par_den > 0 ) + { + i_sar_num *= p_vout->i_par_den; + i_sar_den *= p_vout->i_par_num; + i_aspect = i_aspect * p_vout->i_par_den / p_vout->i_par_num; + } +#endif + + if( i_sar_num > 0 && i_sar_den > 0 && i_aspect > 0 ) + { + p_vout->fmt_in.i_sar_num = i_sar_num; + p_vout->fmt_in.i_sar_den = i_sar_den; + p_vout->fmt_in.i_aspect = i_aspect; + + p_vout->fmt_render.i_sar_num = i_sar_num; + p_vout->fmt_render.i_sar_den = i_sar_den; + p_vout->fmt_render.i_aspect = i_aspect; + + p_vout->render.i_aspect = i_aspect; + + p_vout->i_changes |= VOUT_ASPECT_CHANGE; + + } + } + vlc_mutex_unlock( &p_vout->change_lock ); + vlc_object_release( p_vout ); } + + if( p_vout ) + { + msg_Dbg( p_this, "reusing provided vout" ); + + spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), false ); + vlc_object_detach( p_vout ); + + vlc_object_attach( p_vout, p_this ); + spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), true ); + } } if( !p_vout ) @@ -219,19 +298,26 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout, vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) { vout_thread_t * p_vout; /* thread descriptor */ - input_thread_t * p_input_thread; int i_index; /* loop variable */ - vlc_value_t val, text; + vlc_value_t text; unsigned int i_width = p_fmt->i_width; unsigned int i_height = p_fmt->i_height; - vlc_fourcc_t i_chroma = p_fmt->i_chroma; + vlc_fourcc_t i_chroma = vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma ); unsigned int i_aspect = p_fmt->i_aspect; config_chain_t *p_cfg; char *psz_parser; char *psz_name; + if( i_width <= 0 || i_height <= 0 || i_aspect <= 0 ) + return NULL; + + vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den, + p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 ); + if( p_fmt->i_sar_num <= 0 || p_fmt->i_sar_den <= 0 ) + return NULL; + /* Allocate descriptor */ static const char typename[] = "video output"; p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT, @@ -239,6 +325,14 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) if( p_vout == NULL ) return NULL; + /* */ + p_vout->p = calloc( 1, sizeof(*p_vout->p) ); + if( !p_vout->p ) + { + vlc_object_release( p_vout ); + return NULL; + } + /* Initialize pictures - translation tables and functions * will be initialized later in InitThread */ for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++) @@ -256,8 +350,6 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) /* Initialize the rendering heap */ I_RENDERPICTURES = 0; - vlc_ureduce( &p_fmt->i_sar_num, &p_fmt->i_sar_den, - p_fmt->i_sar_num, p_fmt->i_sar_den, 50000 ); p_vout->fmt_render = *p_fmt; /* FIXME palette */ p_vout->fmt_in = *p_fmt; /* FIXME palette */ @@ -286,55 +378,72 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) /* Initialize misc stuff */ p_vout->i_changes = 0; - p_vout->f_gamma = 0; - p_vout->b_grayscale = 0; - p_vout->b_info = 0; - p_vout->b_interface = 0; - p_vout->b_scale = 1; + p_vout->b_autoscale = 1; + p_vout->i_zoom = ZOOM_FP_FACTOR; p_vout->b_fullscreen = 0; p_vout->i_alignment = 0; - p_vout->render_time = 10; - p_vout->c_fps_samples = 0; - p_vout->b_filter_change = 0; + p_vout->p->render_time = 10; + p_vout->p->c_fps_samples = 0; + p_vout->p->i_picture_lost = 0; + p_vout->p->i_picture_displayed = 0; + p_vout->p->b_filter_change = 0; + p_vout->p->b_paused = false; + p_vout->p->i_pause_date = 0; p_vout->pf_control = NULL; - p_vout->p_window = NULL; - p_vout->i_par_num = p_vout->i_par_den = 1; + p_vout->p->i_par_num = + p_vout->p->i_par_den = 1; + p_vout->p->p_picture_displayed = NULL; + p_vout->p->i_picture_displayed_date = 0; + p_vout->p->b_picture_displayed = false; + p_vout->p->b_picture_empty = false; + p_vout->p->i_picture_qtype = QTYPE_NONE; + + p_vout->p->snapshot.b_available = true; + p_vout->p->snapshot.i_request = 0; + p_vout->p->snapshot.p_picture = NULL; + vlc_mutex_init( &p_vout->p->snapshot.lock ); + vlc_cond_init( &p_vout->p->snapshot.wait ); /* Initialize locks */ vlc_mutex_init( &p_vout->picture_lock ); + vlc_cond_init( &p_vout->p->picture_wait ); vlc_mutex_init( &p_vout->change_lock ); - vlc_mutex_init( &p_vout->vfilter_lock ); + vlc_mutex_init( &p_vout->p->vfilter_lock ); /* Mouse coordinates */ var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER ); var_Create( p_vout, "mouse-y", VLC_VAR_INTEGER ); var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER ); var_Create( p_vout, "mouse-moved", VLC_VAR_BOOL ); - var_Create( p_vout, "mouse-clicked", VLC_VAR_INTEGER ); + var_Create( p_vout, "mouse-clicked", VLC_VAR_BOOL ); /* Initialize subpicture unit */ p_vout->p_spu = spu_Create( p_vout ); - spu_Attach( p_vout->p_spu, p_parent, true ); /* Attach the new object now so we can use var inheritance below */ vlc_object_attach( p_vout, p_parent ); + /* */ spu_Init( p_vout->p_spu ); + spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), true ); + /* Take care of some "interface/control" related initialisations */ vout_IntfInit( p_vout ); /* If the parent is not a VOUT object, that means we are at the start of * the video output pipe */ - if( p_parent->i_object_type != VLC_OBJECT_VOUT ) + if( vlc_internals( p_parent )->i_object_type != VLC_OBJECT_VOUT ) { /* Look for the default filter configuration */ - p_vout->psz_filter_chain = + p_vout->p->psz_filter_chain = var_CreateGetStringCommand( p_vout, "vout-filter" ); /* Apply video filter2 objects on the first vout */ - p_vout->psz_vf2 = + p_vout->p->psz_vf2 = var_CreateGetStringCommand( p_vout, "video-filter" ); + + p_vout->p->b_first_vout = true; } else { @@ -342,136 +451,169 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) char *psz_tmp; /* Ugly hack to jump to our configuration chain */ - p_vout->psz_filter_chain - = ((vout_thread_t *)p_parent)->psz_filter_chain; - p_vout->psz_filter_chain - = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->psz_filter_chain ); + p_vout->p->psz_filter_chain + = ((vout_thread_t *)p_parent)->p->psz_filter_chain; + p_vout->p->psz_filter_chain + = config_ChainCreate( &psz_tmp, &p_cfg, p_vout->p->psz_filter_chain ); config_ChainDestroy( p_cfg ); free( psz_tmp ); /* Create a video filter2 var ... but don't inherit values */ var_Create( p_vout, "video-filter", VLC_VAR_STRING | VLC_VAR_ISCOMMAND ); - p_vout->psz_vf2 = var_GetString( p_vout, "video-filter" ); + p_vout->p->psz_vf2 = var_GetString( p_vout, "video-filter" ); + + /* */ + p_vout->p->b_first_vout = false; } var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL ); - p_vout->p_vf2_chain = filter_chain_New( p_vout, "video filter2", + p_vout->p->p_vf2_chain = filter_chain_New( p_vout, "video filter2", false, video_filter_buffer_allocation_init, NULL, p_vout ); /* Choose the video output module */ - if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain ) + if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain ) { - var_Create( p_vout, "vout", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Get( p_vout, "vout", &val ); - psz_parser = val.psz_string; + psz_parser = var_CreateGetString( p_vout, "vout" ); } else { - psz_parser = strdup( p_vout->psz_filter_chain ); + psz_parser = strdup( p_vout->p->psz_filter_chain ); + p_vout->p->b_title_show = false; } /* Create the vout thread */ - config_ChainCreate( &psz_name, &p_cfg, psz_parser ); + char* psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser ); free( psz_parser ); + free( psz_tmp ); p_vout->p_cfg = p_cfg; - p_vout->p_module = module_Need( p_vout, - ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ? - "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain ); + p_vout->p_module = module_need( p_vout, + ( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain ) ? + "video filter" : "video output", psz_name, p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain ); free( psz_name ); + vlc_object_set_destructor( p_vout, vout_Destructor ); + if( p_vout->p_module == NULL ) { msg_Err( p_vout, "no suitable vout module" ); - // FIXME it's ugly but that's exactly the function that need to be called. - EndThread( p_vout ); - vlc_object_detach( p_vout ); + spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), false ); + spu_Destroy( p_vout->p_spu ); + p_vout->p_spu = NULL; vlc_object_release( p_vout ); return NULL; } /* Create a few object variables for interface interaction */ - var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); - text.psz_string = _("Deinterlace"); - var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL ); - val.psz_string = (char *)""; text.psz_string = _("Disable"); - var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); - val.psz_string = (char *)"discard"; text.psz_string = _("Discard"); - var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); - val.psz_string = (char *)"blend"; text.psz_string = _("Blend"); - var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); - val.psz_string = (char *)"mean"; text.psz_string = _("Mean"); - var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); - val.psz_string = (char *)"bob"; text.psz_string = _("Bob"); - var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); - val.psz_string = (char *)"linear"; text.psz_string = _("Linear"); - var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); - val.psz_string = (char *)"x"; text.psz_string = (char *)"X"; - var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); - - if( var_Get( p_vout, "deinterlace-mode", &val ) == VLC_SUCCESS ) - { - var_Set( p_vout, "deinterlace", val ); - free( val.psz_string ); - } - var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL ); - var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); text.psz_string = _("Filters"); var_Change( p_vout, "vout-filter", VLC_VAR_SETTEXT, &text, NULL ); var_AddCallback( p_vout, "vout-filter", FilterCallback, NULL ); - /* Calculate delay created by internal caching */ - p_input_thread = (input_thread_t *)vlc_object_find( p_vout, - VLC_OBJECT_INPUT, FIND_ANYWHERE ); - if( p_input_thread ) - { - p_vout->i_pts_delay = p_input_thread->i_pts_delay; - vlc_object_release( p_input_thread ); - } - else - { - p_vout->i_pts_delay = DEFAULT_PTS_DELAY; - } + /* */ + DeinterlaceEnable( p_vout ); - if( vlc_thread_create( p_vout, "video output", RunThread, - VLC_THREAD_PRIORITY_OUTPUT, true ) ) + /* */ + vlc_cond_init( &p_vout->p->change_wait ); + if( vlc_clone( &p_vout->p->thread, RunThread, p_vout, + VLC_THREAD_PRIORITY_OUTPUT ) ) { - module_Unneed( p_vout, p_vout->p_module ); + module_unneed( p_vout, p_vout->p_module ); + p_vout->p_module = NULL; + spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), false ); + spu_Destroy( p_vout->p_spu ); + p_vout->p_spu = NULL; vlc_object_release( p_vout ); return NULL; } - vlc_object_set_destructor( p_vout, vout_Destructor ); + vlc_mutex_lock( &p_vout->change_lock ); + while( !p_vout->p->b_ready ) + { /* We are (ab)using the same condition in opposite directions for + * b_ready and b_done. This works because of the strict ordering. */ + assert( !p_vout->p->b_done ); + vlc_cond_wait( &p_vout->p->change_wait, &p_vout->change_lock ); + } + vlc_mutex_unlock( &p_vout->change_lock ); if( p_vout->b_error ) { msg_Err( p_vout, "video output creation failed" ); - - /* Make sure the thread is destroyed and data released */ - vlc_object_kill( p_vout ); - vlc_thread_join( p_vout ); - module_Unneed( p_vout, p_vout->p_module ); - vlc_object_release( p_vout ); + vout_CloseAndRelease( p_vout ); return NULL; } return p_vout; } +/***************************************************************************** + * vout_Close: Close a vout created by vout_Create. + ***************************************************************************** + * You HAVE to call it on vout created by vout_Create before vlc_object_release. + * You should NEVER call it on vout not obtained through vout_Create + * (like with vout_Request or vlc_object_find.) + * You can use vout_CloseAndRelease() as a convenience method. + *****************************************************************************/ +void vout_Close( vout_thread_t *p_vout ) +{ + assert( p_vout ); + + vlc_mutex_lock( &p_vout->change_lock ); + p_vout->p->b_done = true; + vlc_cond_signal( &p_vout->p->change_wait ); + vlc_mutex_unlock( &p_vout->change_lock ); + + vlc_mutex_lock( &p_vout->p->snapshot.lock ); + p_vout->p->snapshot.b_available = false; + vlc_cond_broadcast( &p_vout->p->snapshot.wait ); + vlc_mutex_unlock( &p_vout->p->snapshot.lock ); + + vlc_join( p_vout->p->thread, NULL ); + module_unneed( p_vout, p_vout->p_module ); + p_vout->p_module = NULL; +} + +/* */ static void vout_Destructor( vlc_object_t * p_this ) { vout_thread_t *p_vout = (vout_thread_t *)p_this; + /* Make sure the vout was stopped first */ + assert( !p_vout->p_module ); + + /* */ + if( p_vout->p_spu ) + spu_Destroy( p_vout->p_spu ); + /* Destroy the locks */ + vlc_cond_destroy( &p_vout->p->change_wait ); + vlc_cond_destroy( &p_vout->p->picture_wait ); vlc_mutex_destroy( &p_vout->picture_lock ); vlc_mutex_destroy( &p_vout->change_lock ); - vlc_mutex_destroy( &p_vout->vfilter_lock ); + vlc_mutex_destroy( &p_vout->p->vfilter_lock ); - free( p_vout->psz_filter_chain ); + /* */ + for( ;; ) + { + picture_t *p_picture = p_vout->p->snapshot.p_picture; + if( !p_picture ) + break; + + p_vout->p->snapshot.p_picture = p_picture->p_next; + + picture_Release( p_picture ); + } + vlc_cond_destroy( &p_vout->p->snapshot.wait ); + vlc_mutex_destroy( &p_vout->p->snapshot.lock ); + + /* */ + free( p_vout->p->psz_filter_chain ); + free( p_vout->p->psz_title ); config_ChainDestroy( p_vout->p_cfg ); + free( p_vout->p ); + #ifndef __APPLE__ vout_thread_t *p_another_vout; @@ -489,6 +631,175 @@ static void vout_Destructor( vlc_object_t * p_this ) #endif } +/* */ +void vout_ChangePause( vout_thread_t *p_vout, bool b_paused, mtime_t i_date ) +{ + vlc_mutex_lock( &p_vout->change_lock ); + + assert( !p_vout->p->b_paused || !b_paused ); + + vlc_mutex_lock( &p_vout->picture_lock ); + + p_vout->p->i_picture_displayed_date = 0; + + if( p_vout->p->b_paused ) + { + const mtime_t i_duration = i_date - p_vout->p->i_pause_date; + + for( int i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) + { + picture_t *p_pic = PP_RENDERPICTURE[i_index]; + + if( p_pic->i_status == READY_PICTURE ) + p_pic->date += i_duration; + } + vlc_cond_signal( &p_vout->p->picture_wait ); + vlc_mutex_unlock( &p_vout->picture_lock ); + + spu_OffsetSubtitleDate( p_vout->p_spu, i_duration ); + } + else + { + vlc_mutex_unlock( &p_vout->picture_lock ); + } + p_vout->p->b_paused = b_paused; + p_vout->p->i_pause_date = i_date; + + vlc_mutex_unlock( &p_vout->change_lock ); +} + +void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost ) +{ + vlc_mutex_lock( &p_vout->change_lock ); + + *pi_displayed = p_vout->p->i_picture_displayed; + *pi_lost = p_vout->p->i_picture_lost; + + p_vout->p->i_picture_displayed = 0; + p_vout->p->i_picture_lost = 0; + + vlc_mutex_unlock( &p_vout->change_lock ); +} + +void vout_Flush( vout_thread_t *p_vout, mtime_t i_date ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + p_vout->p->i_picture_displayed_date = 0; + for( int i = 0; i < p_vout->render.i_pictures; i++ ) + { + picture_t *p_pic = p_vout->render.pp_picture[i]; + + if( p_pic->i_status == READY_PICTURE || + p_pic->i_status == DISPLAYED_PICTURE ) + { + /* We cannot change picture status if it is in READY_PICTURE state, + * Just make sure they won't be displayed */ + if( p_pic->date > i_date ) + p_pic->date = i_date; + } + } + vlc_cond_signal( &p_vout->p->picture_wait ); + vlc_mutex_unlock( &p_vout->picture_lock ); +} + +void vout_FixLeaks( vout_thread_t *p_vout, bool b_forced ) +{ + int i_pic, i_ready_pic; + + vlc_mutex_lock( &p_vout->picture_lock ); + + for( i_pic = 0, i_ready_pic = 0; i_pic < p_vout->render.i_pictures && !b_forced; i_pic++ ) + { + const picture_t *p_pic = p_vout->render.pp_picture[i_pic]; + + if( p_pic->i_status == READY_PICTURE ) + { + i_ready_pic++; + /* If we have at least 2 ready pictures, wait for the vout thread to + * process one */ + if( i_ready_pic >= 2 ) + break; + + continue; + } + + if( p_pic->i_status == DISPLAYED_PICTURE ) + { + /* If at least one displayed picture is not referenced + * let vout free it */ + if( p_pic->i_refcount == 0 ) + break; + } + } + if( i_pic < p_vout->render.i_pictures && !b_forced ) + { + vlc_mutex_unlock( &p_vout->picture_lock ); + return; + } + + /* Too many pictures are still referenced, there is probably a bug + * with the decoder */ + if( !b_forced ) + msg_Err( p_vout, "pictures leaked, resetting the heap" ); + + /* Just free all the pictures */ + for( i_pic = 0; i_pic < p_vout->render.i_pictures; i_pic++ ) + { + picture_t *p_pic = p_vout->render.pp_picture[i_pic]; + + msg_Dbg( p_vout, "[%d] %d %d", i_pic, p_pic->i_status, p_pic->i_refcount ); + p_pic->i_refcount = 0; + + switch( p_pic->i_status ) + { + case READY_PICTURE: + case DISPLAYED_PICTURE: + case RESERVED_PICTURE: + if( p_pic != p_vout->p->p_picture_displayed ) + vout_UsePictureLocked( p_vout, p_pic ); + break; + } + } + vlc_cond_signal( &p_vout->p->picture_wait ); + vlc_mutex_unlock( &p_vout->picture_lock ); +} +void vout_NextPicture( vout_thread_t *p_vout, mtime_t *pi_duration ) +{ + vlc_mutex_lock( &p_vout->picture_lock ); + + const mtime_t i_displayed_date = p_vout->p->i_picture_displayed_date; + + p_vout->p->b_picture_displayed = false; + p_vout->p->b_picture_empty = false; + if( p_vout->p->p_picture_displayed ) + { + p_vout->p->p_picture_displayed->date = 1; + vlc_cond_signal( &p_vout->p->picture_wait ); + } + + while( !p_vout->p->b_picture_displayed && !p_vout->p->b_picture_empty ) + vlc_cond_wait( &p_vout->p->picture_wait, &p_vout->picture_lock ); + + *pi_duration = __MAX( p_vout->p->i_picture_displayed_date - i_displayed_date, 0 ); + + /* TODO advance subpicture by the duration ... */ + + vlc_mutex_unlock( &p_vout->picture_lock ); +} + +void vout_DisplayTitle( vout_thread_t *p_vout, const char *psz_title ) +{ + assert( psz_title ); + + if( !config_GetInt( p_vout, "osd" ) ) + return; + + vlc_mutex_lock( &p_vout->change_lock ); + free( p_vout->p->psz_title ); + p_vout->p->psz_title = strdup( psz_title ); + vlc_mutex_unlock( &p_vout->change_lock ); +} + /***************************************************************************** * InitThread: initialize video output thread ***************************************************************************** @@ -500,18 +811,32 @@ static void vout_Destructor( vlc_object_t * p_this ) static int ChromaCreate( vout_thread_t *p_vout ); static void ChromaDestroy( vout_thread_t *p_vout ); +static bool ChromaIsEqual( const picture_heap_t *p_output, const picture_heap_t *p_render ) +{ + if( !vout_ChromaCmp( p_output->i_chroma, p_render->i_chroma ) ) + return false; + + if( p_output->i_chroma != VLC_CODEC_RGB15 && + p_output->i_chroma != VLC_CODEC_RGB16 && + p_output->i_chroma != VLC_CODEC_RGB24 && + p_output->i_chroma != VLC_CODEC_RGB32 ) + return true; + + return p_output->i_rmask == p_render->i_rmask && + p_output->i_gmask == p_render->i_gmask && + p_output->i_bmask == p_render->i_bmask; +} + static int InitThread( vout_thread_t *p_vout ) { int i, i_aspect_x, i_aspect_y; -#ifdef STATS - p_vout->c_loops = 0; -#endif - /* Initialize output method, it allocates direct buffers for us */ if( p_vout->pf_init( p_vout ) ) return VLC_EGENERIC; + p_vout->p->p_picture_displayed = NULL; + if( !I_OUTPUTPICTURES ) { msg_Err( p_vout, "plugin was unable to allocate at least " @@ -532,28 +857,8 @@ static int InitThread( vout_thread_t *p_vout ) AspectRatio( p_vout->fmt_render.i_aspect, &i_aspect_x, &i_aspect_y ); - msg_Dbg( p_vout, "picture in %ix%i (%i,%i,%ix%i), " - "chroma %4.4s, ar %i:%i, sar %i:%i", - p_vout->fmt_render.i_width, p_vout->fmt_render.i_height, - p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset, - p_vout->fmt_render.i_visible_width, - p_vout->fmt_render.i_visible_height, - (char*)&p_vout->fmt_render.i_chroma, - i_aspect_x, i_aspect_y, - p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den ); - AspectRatio( p_vout->fmt_in.i_aspect, &i_aspect_x, &i_aspect_y ); - msg_Dbg( p_vout, "picture user %ix%i (%i,%i,%ix%i), " - "chroma %4.4s, ar %i:%i, sar %i:%i", - p_vout->fmt_in.i_width, p_vout->fmt_in.i_height, - p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset, - p_vout->fmt_in.i_visible_width, - p_vout->fmt_in.i_visible_height, - (char*)&p_vout->fmt_in.i_chroma, - i_aspect_x, i_aspect_y, - p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den ); - if( !p_vout->fmt_out.i_width || !p_vout->fmt_out.i_height ) { p_vout->fmt_out.i_width = p_vout->fmt_out.i_visible_width = @@ -578,42 +883,57 @@ static int InitThread( vout_thread_t *p_vout ) AspectRatio( p_vout->fmt_out.i_aspect, &i_aspect_x, &i_aspect_y ); - msg_Dbg( p_vout, "picture out %ix%i (%i,%i,%ix%i), " - "chroma %4.4s, ar %i:%i, sar %i:%i", + /* FIXME removed the need of both fmt_* and heap infos */ + /* Calculate shifts from system-updated masks */ + PictureHeapFixRgb( &p_vout->render ); + VideoFormatImportRgb( &p_vout->fmt_render, &p_vout->render ); + + PictureHeapFixRgb( &p_vout->output ); + VideoFormatImportRgb( &p_vout->fmt_out, &p_vout->output ); + + /* print some usefull debug info about different vout formats + */ + msg_Dbg( p_vout, "pic render sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, ar %i:%i, sar %i:%i, msk r0x%x g0x%x b0x%x", + p_vout->fmt_render.i_width, p_vout->fmt_render.i_height, + p_vout->fmt_render.i_x_offset, p_vout->fmt_render.i_y_offset, + p_vout->fmt_render.i_visible_width, + p_vout->fmt_render.i_visible_height, + (char*)&p_vout->fmt_render.i_chroma, + i_aspect_x, i_aspect_y, + p_vout->fmt_render.i_sar_num, p_vout->fmt_render.i_sar_den, + p_vout->fmt_render.i_rmask, p_vout->fmt_render.i_gmask, p_vout->fmt_render.i_bmask ); + + msg_Dbg( p_vout, "pic in sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, ar %i:%i, sar %i:%i, msk r0x%x g0x%x b0x%x", + p_vout->fmt_in.i_width, p_vout->fmt_in.i_height, + p_vout->fmt_in.i_x_offset, p_vout->fmt_in.i_y_offset, + p_vout->fmt_in.i_visible_width, + p_vout->fmt_in.i_visible_height, + (char*)&p_vout->fmt_in.i_chroma, + i_aspect_x, i_aspect_y, + p_vout->fmt_in.i_sar_num, p_vout->fmt_in.i_sar_den, + p_vout->fmt_in.i_rmask, p_vout->fmt_in.i_gmask, p_vout->fmt_in.i_bmask ); + + msg_Dbg( p_vout, "pic out sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, ar %i:%i, sar %i:%i, msk r0x%x g0x%x b0x%x", p_vout->fmt_out.i_width, p_vout->fmt_out.i_height, p_vout->fmt_out.i_x_offset, p_vout->fmt_out.i_y_offset, p_vout->fmt_out.i_visible_width, p_vout->fmt_out.i_visible_height, (char*)&p_vout->fmt_out.i_chroma, i_aspect_x, i_aspect_y, - p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den ); - - /* Calculate shifts from system-updated masks */ - MaskToShift( &p_vout->render.i_lrshift, &p_vout->output.i_rrshift, - p_vout->render.i_rmask ); - MaskToShift( &p_vout->render.i_lgshift, &p_vout->output.i_rgshift, - p_vout->render.i_gmask ); - MaskToShift( &p_vout->render.i_lbshift, &p_vout->output.i_rbshift, - p_vout->render.i_bmask ); - - MaskToShift( &p_vout->output.i_lrshift, &p_vout->output.i_rrshift, - p_vout->output.i_rmask ); - MaskToShift( &p_vout->output.i_lgshift, &p_vout->output.i_rgshift, - p_vout->output.i_gmask ); - MaskToShift( &p_vout->output.i_lbshift, &p_vout->output.i_rbshift, - p_vout->output.i_bmask ); + p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den, + p_vout->fmt_out.i_rmask, p_vout->fmt_out.i_gmask, p_vout->fmt_out.i_bmask ); /* Check whether we managed to create direct buffers similar to * the render buffers, ie same size and chroma */ if( ( p_vout->output.i_width == p_vout->render.i_width ) && ( p_vout->output.i_height == p_vout->render.i_height ) - && ( vout_ChromaCmp( p_vout->output.i_chroma, p_vout->render.i_chroma ) ) ) + && ( ChromaIsEqual( &p_vout->output, &p_vout->render ) ) ) { /* Cool ! We have direct buffers, we can ask the decoder to * directly decode into them ! Map the first render buffers to * the first direct buffers, but keep the first direct buffer * for memcpy operations */ - p_vout->b_direct = 1; + p_vout->p->b_direct = true; for( i = 1; i < VOUT_MAX_PICTURES; i++ ) { @@ -638,7 +958,7 @@ static int InitThread( vout_thread_t *p_vout ) /* Rats... Something is wrong here, we could not find an output * plugin able to directly render what we decode. See if we can * find a chroma plugin to do the conversion */ - p_vout->b_direct = 0; + p_vout->p->b_direct = false; if( ChromaCreate( p_vout ) ) { @@ -684,25 +1004,13 @@ static int InitThread( vout_thread_t *p_vout ) * terminated. It handles the pictures arriving in the video heap and the * display device events. *****************************************************************************/ -static void RunThread( vout_thread_t *p_vout) +static void* RunThread( void *p_this ) { - int i_index; /* index in heap */ + vout_thread_t *p_vout = p_this; int i_idle_loops = 0; /* loops without displaying a picture */ - mtime_t current_date; /* current date */ - mtime_t display_date; /* display date */ - - picture_t * p_picture; /* picture pointer */ - picture_t * p_last_picture = NULL; /* last picture */ - picture_t * p_directbuffer; /* direct buffer to display */ - - subpicture_t * p_subpic = NULL; /* subpicture pointer */ + int i_picture_qtype_last = QTYPE_NONE; - input_thread_t *p_input = NULL ; /* Parent input, if it exists */ - - vlc_value_t val; - bool b_drop_late; - - int i_displayed = 0, i_lost = 0, i_loops = 0; + bool b_drop_late; /* * Initialize thread @@ -710,174 +1018,102 @@ static void RunThread( vout_thread_t *p_vout) vlc_mutex_lock( &p_vout->change_lock ); p_vout->b_error = InitThread( p_vout ); - var_Create( p_vout, "drop-late-frames", VLC_VAR_BOOL | VLC_VAR_DOINHERIT ); - var_Get( p_vout, "drop-late-frames", &val ); - b_drop_late = val.b_bool; + b_drop_late = var_CreateGetBool( p_vout, "drop-late-frames" ); /* signal the creation of the vout */ - vlc_thread_ready( p_vout ); + p_vout->p->b_ready = true; + vlc_cond_signal( &p_vout->p->change_wait ); if( p_vout->b_error ) { EndThread( p_vout ); vlc_mutex_unlock( &p_vout->change_lock ); - return; + return NULL; } - vlc_object_lock( p_vout ); - - if( p_vout->b_title_show ) - DisplayTitleOnOSD( p_vout ); - /* * Main loop - it is not executed if an error occurred during * initialization */ - while( (vlc_object_alive( p_vout )) && (!p_vout->b_error) ) + while( !p_vout->p->b_done && !p_vout->b_error ) { /* Initialize loop variables */ + const mtime_t current_date = mdate(); + picture_t *p_picture; + picture_t *p_filtered_picture; + mtime_t display_date; + picture_t *p_directbuffer; + int i_index; + + if( p_vout->p->b_title_show && p_vout->p->psz_title ) + DisplayTitleOnOSD( p_vout ); + + vlc_mutex_lock( &p_vout->picture_lock ); + + /* Look for the earliest picture but after the last displayed one */ + picture_t *p_last = p_vout->p->p_picture_displayed;; + p_picture = NULL; - display_date = 0; - current_date = mdate(); + for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) + { + picture_t *p_pic = PP_RENDERPICTURE[i_index]; + + if( p_pic->i_status != READY_PICTURE ) + continue; + + if( p_vout->p->b_paused && p_last && p_last->date > 1 ) + continue; - i_loops++; - if( !p_input ) + if( p_last && p_pic != p_last && p_pic->date <= p_last->date ) + { + /* Drop old picture */ + vout_UsePictureLocked( p_vout, p_pic ); + } + else if( !p_vout->p->b_paused && !p_pic->b_force && p_pic != p_last && + p_pic->date < current_date + p_vout->p->render_time && + b_drop_late ) { - p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, - FIND_PARENT ); + /* Picture is late: it will be destroyed and the thread + * will directly choose the next picture */ + vout_UsePictureLocked( p_vout, p_pic ); + p_vout->p->i_picture_lost++; + + msg_Warn( p_vout, "late picture skipped (%"PRId64" > %d)", + current_date - p_pic->date, - p_vout->p->render_time ); } - if( p_input ) + else if( ( !p_last || p_last->date < p_pic->date ) && + ( p_picture == NULL || p_pic->date < p_picture->date ) ) { - vlc_mutex_lock( &p_input->p->counters.counters_lock ); - stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures, - i_lost , NULL); - stats_UpdateInteger( p_vout, - p_input->p->counters.p_displayed_pictures, - i_displayed , NULL); - i_displayed = i_lost = 0; - vlc_mutex_unlock( &p_input->p->counters.counters_lock ); - vlc_object_release( p_input ); - p_input = NULL; + p_picture = p_pic; } -#if 0 - p_vout->c_loops++; - if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) ) - { - msg_Dbg( p_vout, "picture heap: %d/%d", - I_RENDERPICTURES, p_vout->i_heap_size ); } -#endif - - /* - * Find the picture to display (the one with the earliest date). - * This operation does not need lock, since only READY_PICTUREs - * are handled. */ - for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) + if( !p_picture ) { - if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE) - && ( (p_picture == NULL) || - (PP_RENDERPICTURE[i_index]->date < display_date) ) ) + p_picture = p_last; + + if( !p_vout->p->b_picture_empty ) { - p_picture = PP_RENDERPICTURE[i_index]; - display_date = p_picture->date; + p_vout->p->b_picture_empty = true; + vlc_cond_signal( &p_vout->p->picture_wait ); } } + display_date = 0; if( p_picture ) { - /* If we met the last picture, parse again to see whether there is - * a more appropriate one. */ - if( p_picture == p_last_picture ) - { - for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) - { - if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE) - && (PP_RENDERPICTURE[i_index] != p_last_picture) - && ((p_picture == p_last_picture) || - (PP_RENDERPICTURE[i_index]->date < display_date)) ) - { - p_picture = PP_RENDERPICTURE[i_index]; - display_date = p_picture->date; - } - } - } + display_date = p_picture->date; /* If we found better than the last picture, destroy it */ - if( p_last_picture && p_picture != p_last_picture ) + if( p_last && p_picture != p_last ) { - vlc_mutex_lock( &p_vout->picture_lock ); - if( p_last_picture->i_refcount ) - { - p_last_picture->i_status = DISPLAYED_PICTURE; - } - else - { - p_last_picture->i_status = DESTROYED_PICTURE; - p_vout->i_heap_size--; - } - vlc_mutex_unlock( &p_vout->picture_lock ); - p_last_picture = NULL; + vout_UsePictureLocked( p_vout, p_last ); + p_vout->p->p_picture_displayed = p_last = NULL; } /* Compute FPS rate */ - p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] - = display_date; + p_vout->p->p_fps_sample[ p_vout->p->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date; - if( !p_picture->b_force && - p_picture != p_last_picture && - display_date < current_date + p_vout->render_time && - b_drop_late ) - { - /* Picture is late: it will be destroyed and the thread - * will directly choose the next picture */ - vlc_mutex_lock( &p_vout->picture_lock ); - if( p_picture->i_refcount ) - { - /* Pretend we displayed the picture, but don't destroy - * it since the decoder might still need it. */ - p_picture->i_status = DISPLAYED_PICTURE; - } - else - { - /* Destroy the picture without displaying it */ - p_picture->i_status = DESTROYED_PICTURE; - p_vout->i_heap_size--; - } - msg_Warn( p_vout, "late picture skipped (%"PRId64")", - current_date - display_date ); - i_lost++; - vlc_mutex_unlock( &p_vout->picture_lock ); - - continue; - } - - if( display_date > - current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY ) - { - /* Picture is waaay too early: it will be destroyed */ - vlc_mutex_lock( &p_vout->picture_lock ); - if( p_picture->i_refcount ) - { - /* Pretend we displayed the picture, but don't destroy - * it since the decoder might still need it. */ - p_picture->i_status = DISPLAYED_PICTURE; - } - else - { - /* Destroy the picture without displaying it */ - p_picture->i_status = DESTROYED_PICTURE; - p_vout->i_heap_size--; - } - i_lost++; - msg_Warn( p_vout, "vout warning: early picture skipped " - "(%"PRId64")", display_date - current_date - - p_vout->i_pts_delay ); - vlc_mutex_unlock( &p_vout->picture_lock ); - - continue; - } - - if( display_date > current_date + VOUT_DISPLAY_DELAY ) + if( !p_vout->p->b_paused && display_date > current_date + VOUT_DISPLAY_DELAY ) { /* A picture is ready to be rendered, but its rendering date * is far from the current one so the thread will perform an @@ -886,7 +1122,7 @@ static void RunThread( vout_thread_t *p_vout) p_picture = NULL; display_date = 0; } - else if( p_picture == p_last_picture ) + else if( p_picture == p_last ) { /* We are asked to repeat the previous picture, but we first * wait for a couple of idle loops */ @@ -899,55 +1135,96 @@ static void RunThread( vout_thread_t *p_vout) { /* We set the display date to something high, otherwise * we'll have lots of problems with late pictures */ - display_date = current_date + p_vout->render_time; + display_date = current_date + p_vout->p->render_time; + } + } + else if( p_vout->p->b_paused && display_date > current_date + VOUT_DISPLAY_DELAY ) + { + display_date = current_date + VOUT_DISPLAY_DELAY; + } + + if( p_picture ) + { + if( p_picture->date > 1 ) + { + p_vout->p->i_picture_displayed_date = p_picture->date; + if( p_picture != p_last && !p_vout->p->b_picture_displayed ) + { + p_vout->p->b_picture_displayed = true; + vlc_cond_signal( &p_vout->p->picture_wait ); + } } + p_vout->p->p_picture_displayed = p_picture; } } + /* */ + const int i_postproc_type = p_vout->p->i_picture_qtype; + const int i_postproc_state = (p_vout->p->i_picture_qtype != QTYPE_NONE) - (i_picture_qtype_last != QTYPE_NONE); + + vlc_mutex_unlock( &p_vout->picture_lock ); + if( p_picture == NULL ) - { i_idle_loops++; - } + p_filtered_picture = NULL; if( p_picture ) - { - p_picture = filter_chain_VideoFilter( p_vout->p_vf2_chain, - p_picture ); - } + p_filtered_picture = filter_chain_VideoFilter( p_vout->p->p_vf2_chain, + p_picture ); - if( p_picture && p_vout->b_snapshot ) + bool b_snapshot = false; + if( vlc_mutex_trylock( &p_vout->p->snapshot.lock ) == 0 ) { - p_vout->b_snapshot = false; - vout_Snapshot( p_vout, p_picture ); + b_snapshot = p_vout->p->snapshot.i_request > 0 + && p_picture != NULL; + vlc_mutex_unlock( &p_vout->p->snapshot.lock ); } /* * Check for subpictures to display */ + subpicture_t *p_subpic = NULL; if( display_date > 0 ) - { - if( !p_input ) - { - p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, - FIND_PARENT ); - } p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date, - p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : false ); - if( p_input ) - vlc_object_release( p_input ); - p_input = NULL; - } + p_vout->p->b_paused, b_snapshot ); /* * Perform rendering */ - i_displayed++; - p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic ); + p_vout->p->i_picture_displayed++; + p_directbuffer = vout_RenderPicture( p_vout, p_filtered_picture, + p_subpic, p_vout->p->b_paused ); + + /* + * Take a snapshot if requested + */ + if( p_directbuffer && b_snapshot ) + { + vlc_mutex_lock( &p_vout->p->snapshot.lock ); + assert( p_vout->p->snapshot.i_request > 0 ); + while( p_vout->p->snapshot.i_request > 0 ) + { + picture_t *p_pic = picture_New( p_vout->fmt_out.i_chroma, + p_vout->fmt_out.i_width, + p_vout->fmt_out.i_height, + p_vout->fmt_out.i_aspect ); + if( !p_pic ) + break; + + picture_Copy( p_pic, p_directbuffer ); + + p_pic->p_next = p_vout->p->snapshot.p_picture; + p_vout->p->snapshot.p_picture = p_pic; + p_vout->p->snapshot.i_request--; + } + vlc_cond_broadcast( &p_vout->p->snapshot.wait ); + vlc_mutex_unlock( &p_vout->p->snapshot.lock ); + } /* * Call the plugin-specific rendering method if there is one */ - if( p_picture != NULL && p_directbuffer != NULL && p_vout->pf_render ) + if( p_filtered_picture != NULL && p_directbuffer != NULL && p_vout->pf_render ) { /* Render the direct buffer returned by vout_RenderPicture */ p_vout->pf_render( p_vout, p_directbuffer ); @@ -960,59 +1237,66 @@ static void RunThread( vout_thread_t *p_vout) { mtime_t current_render_time = mdate() - current_date; /* if render time is very large we don't include it in the mean */ - if( current_render_time < p_vout->render_time + + if( current_render_time < p_vout->p->render_time + VOUT_DISPLAY_DELAY ) { /* Store render time using a sliding mean weighting to * current value in a 3 to 1 ratio*/ - p_vout->render_time *= 3; - p_vout->render_time += current_render_time; - p_vout->render_time >>= 2; + p_vout->p->render_time *= 3; + p_vout->p->render_time += current_render_time; + p_vout->p->render_time >>= 2; } + else + msg_Dbg( p_vout, "skipped big render time %d > %d", (int) current_render_time, + (int) (p_vout->p->render_time +VOUT_DISPLAY_DELAY ) ) ; } /* Give back change lock */ vlc_mutex_unlock( &p_vout->change_lock ); - vlc_object_unlock( p_vout ); - /* Sleep a while or until a given date */ if( display_date != 0 ) { - /* If there are filters in the chain, better give them the picture + /* If there are *vout* filters in the chain, better give them the picture * in advance */ - if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain ) + if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain ) { mwait( display_date - VOUT_MWAIT_TOLERANCE ); } } else { - msleep( VOUT_IDLE_SLEEP ); + /* Wait until a frame is being sent or a spurious wakeup (not a problem here) */ + vlc_mutex_lock( &p_vout->picture_lock ); + vlc_cond_timedwait( &p_vout->p->picture_wait, &p_vout->picture_lock, current_date + VOUT_IDLE_SLEEP ); + vlc_mutex_unlock( &p_vout->picture_lock ); } /* On awakening, take back lock and send immediately picture * to display. */ - vlc_object_lock( p_vout ); - /* Note: vlc_object_alive() could be false here, and we - * could be dead */ + /* Note: p_vout->p->b_done could be true here and now */ vlc_mutex_lock( &p_vout->change_lock ); /* * Display the previously rendered picture */ - if( p_picture != NULL && p_directbuffer != NULL ) + if( p_filtered_picture != NULL && p_directbuffer != NULL ) { /* Display the direct buffer returned by vout_RenderPicture */ if( p_vout->pf_display ) - { p_vout->pf_display( p_vout, p_directbuffer ); - } /* Tell the vout this was the last picture and that it does not * need to be forced anymore. */ - p_last_picture = p_picture; - p_last_picture->b_force = 0; + p_picture->b_force = false; + } + + /* Drop the filtered picture if created by video filters */ + if( p_filtered_picture != NULL && p_filtered_picture != p_picture ) + { + vlc_mutex_lock( &p_vout->picture_lock ); + vout_UsePictureLocked( p_vout, p_filtered_picture ); + vlc_mutex_unlock( &p_vout->picture_lock ); } if( p_picture != NULL ) @@ -1034,6 +1318,14 @@ static void RunThread( vout_thread_t *p_vout) break; } + while( p_vout->i_changes & VOUT_ON_TOP_CHANGE ) + { + p_vout->i_changes &= ~VOUT_ON_TOP_CHANGE; + vlc_mutex_unlock( &p_vout->change_lock ); + vout_Control( p_vout, VOUT_SET_STAY_ON_TOP, p_vout->b_on_top ); + vlc_mutex_lock( &p_vout->change_lock ); + } + if( p_vout->i_changes & VOUT_SIZE_CHANGE ) { /* this must only happen when the vout plugin is incapable of @@ -1044,7 +1336,7 @@ static void RunThread( vout_thread_t *p_vout) p_vout->i_changes &= ~VOUT_SIZE_CHANGE; - assert( !p_vout->b_direct ); + assert( !p_vout->p->b_direct ); ChromaDestroy( p_vout ); @@ -1052,8 +1344,10 @@ static void RunThread( vout_thread_t *p_vout) p_vout->pf_end( p_vout ); + p_vout->p->p_picture_displayed = NULL; for( i = 0; i < I_OUTPUTPICTURES; i++ ) p_vout->p_picture[ i ].i_status = FREE_PICTURE; + vlc_cond_signal( &p_vout->p->picture_wait ); I_OUTPUTPICTURES = 0; @@ -1088,7 +1382,7 @@ static void RunThread( vout_thread_t *p_vout) * buffer!! */ p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE; - if( !p_vout->b_direct ) + if( !p_vout->p->b_direct ) ChromaDestroy( p_vout ); vlc_mutex_lock( &p_vout->picture_lock ); @@ -1099,38 +1393,44 @@ static void RunThread( vout_thread_t *p_vout) p_vout->b_error = InitThread( p_vout ); if( p_vout->b_error ) - msg_Err( p_vout, "InitThread after VOUT_PICTURE_BUFFERS_CHANGE failed\n" ); + msg_Err( p_vout, "InitThread after VOUT_PICTURE_BUFFERS_CHANGE failed" ); + vlc_cond_signal( &p_vout->p->picture_wait ); vlc_mutex_unlock( &p_vout->picture_lock ); if( p_vout->b_error ) break; } + /* Post processing */ + if( i_postproc_state == 1 ) + PostProcessEnable( p_vout ); + else if( i_postproc_state == -1 ) + PostProcessDisable( p_vout ); + if( i_postproc_state != 0 ) + i_picture_qtype_last = i_postproc_type; + /* Check for "video filter2" changes */ - vlc_mutex_lock( &p_vout->vfilter_lock ); - if( p_vout->psz_vf2 ) + vlc_mutex_lock( &p_vout->p->vfilter_lock ); + if( p_vout->p->psz_vf2 ) { es_format_t fmt; es_format_Init( &fmt, VIDEO_ES, p_vout->fmt_render.i_chroma ); fmt.video = p_vout->fmt_render; - filter_chain_Reset( p_vout->p_vf2_chain, &fmt, &fmt ); + filter_chain_Reset( p_vout->p->p_vf2_chain, &fmt, &fmt ); - if( filter_chain_AppendFromString( p_vout->p_vf2_chain, - p_vout->psz_vf2 ) < 0 ) + if( filter_chain_AppendFromString( p_vout->p->p_vf2_chain, + p_vout->p->psz_vf2 ) < 0 ) msg_Err( p_vout, "Video filter chain creation failed" ); - free( p_vout->psz_vf2 ); - p_vout->psz_vf2 = NULL; - } - vlc_mutex_unlock( &p_vout->vfilter_lock ); - } + free( p_vout->p->psz_vf2 ); + p_vout->p->psz_vf2 = NULL; - - if( p_input ) - { - vlc_object_release( p_input ); + if( i_picture_qtype_last != QTYPE_NONE ) + PostProcessSetFilterQuality( p_vout ); + } + vlc_mutex_unlock( &p_vout->p->vfilter_lock ); } /* @@ -1144,7 +1444,7 @@ static void RunThread( vout_thread_t *p_vout) EndThread( p_vout ); vlc_mutex_unlock( &p_vout->change_lock ); - vlc_object_unlock( p_vout ); + return NULL; } /***************************************************************************** @@ -1156,9 +1456,9 @@ static void RunThread( vout_thread_t *p_vout) *****************************************************************************/ static void ErrorThread( vout_thread_t *p_vout ) { - /* Wait until a `die' order */ - while( vlc_object_alive( p_vout ) ) - vlc_object_wait( p_vout ); + /* Wait until a `close' order */ + while( !p_vout->p->b_done ) + vlc_cond_wait( &p_vout->p->change_wait, &p_vout->change_lock ); } /***************************************************************************** @@ -1172,7 +1472,7 @@ static void CleanThread( vout_thread_t *p_vout ) { int i_index; /* index in heap */ - if( !p_vout->b_direct ) + if( !p_vout->p->b_direct ) ChromaDestroy( p_vout ); /* Destroy all remaining pictures */ @@ -1210,12 +1510,12 @@ static void EndThread( vout_thread_t *p_vout ) /* FIXME does that function *really* need to be called inside the thread ? */ - /* Destroy subpicture unit */ + /* Detach subpicture unit from both input and vout */ spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), false ); - spu_Destroy( p_vout->p_spu ); + vlc_object_detach( p_vout->p_spu ); /* Destroy the video filters2 */ - filter_chain_Delete( p_vout->p_vf2_chain ); + filter_chain_Delete( p_vout->p->p_vf2_chain ); } /* Thread helpers */ @@ -1226,26 +1526,13 @@ static picture_t *ChromaGetPicture( filter_t *p_filter ) return p_pic; } -static void ChromaCopyRgbInfo( es_format_t *p_fmt, picture_heap_t *p_heap ) -{ - p_fmt->video.i_rmask = p_heap->i_rmask; - p_fmt->video.i_gmask = p_heap->i_gmask; - p_fmt->video.i_bmask = p_heap->i_bmask; - p_fmt->video.i_rrshift = p_heap->i_rrshift; - p_fmt->video.i_lrshift = p_heap->i_lrshift; - p_fmt->video.i_rgshift = p_heap->i_rgshift; - p_fmt->video.i_lgshift = p_heap->i_lgshift; - p_fmt->video.i_rbshift = p_heap->i_rbshift; - p_fmt->video.i_lbshift = p_heap->i_lbshift; -} - static int ChromaCreate( vout_thread_t *p_vout ) { static const char typename[] = "chroma"; filter_t *p_chroma; /* Choose the best module */ - p_chroma = p_vout->p_chroma = + p_chroma = p_vout->p->p_chroma = vlc_custom_create( p_vout, sizeof(filter_t), VLC_OBJECT_GENERIC, typename ); @@ -1254,10 +1541,10 @@ static int ChromaCreate( vout_thread_t *p_vout ) /* TODO: Set the fmt_in and fmt_out stuff here */ p_chroma->fmt_in.video = p_vout->fmt_render; p_chroma->fmt_out.video = p_vout->fmt_out; - ChromaCopyRgbInfo( &p_chroma->fmt_in, &p_vout->render ); - ChromaCopyRgbInfo( &p_chroma->fmt_out, &p_vout->output ); + VideoFormatImportRgb( &p_chroma->fmt_in.video, &p_vout->render ); + VideoFormatImportRgb( &p_chroma->fmt_out.video, &p_vout->output ); - p_chroma->p_module = module_Need( p_chroma, "video filter2", NULL, 0 ); + p_chroma->p_module = module_need( p_chroma, "video filter2", NULL, false ); if( p_chroma->p_module == NULL ) { @@ -1268,8 +1555,9 @@ static int ChromaCreate( vout_thread_t *p_vout ) p_chroma->fmt_out.video.i_width, p_chroma->fmt_out.video.i_height ); - vlc_object_release( p_vout->p_chroma ); - p_vout->p_chroma = NULL; + vlc_object_release( p_vout->p->p_chroma ); + p_vout->p->p_chroma = NULL; + return VLC_EGENERIC; } p_chroma->pf_vout_buffer_new = ChromaGetPicture; @@ -1278,337 +1566,464 @@ static int ChromaCreate( vout_thread_t *p_vout ) static void ChromaDestroy( vout_thread_t *p_vout ) { - assert( !p_vout->b_direct ); + assert( !p_vout->p->b_direct ); - if( !p_vout->p_chroma ) + if( !p_vout->p->p_chroma ) return; - module_Unneed( p_vout->p_chroma, p_vout->p_chroma->p_module ); - vlc_object_release( p_vout->p_chroma ); - p_vout->p_chroma = NULL; + module_unneed( p_vout->p->p_chroma, p_vout->p->p_chroma->p_module ); + vlc_object_release( p_vout->p->p_chroma ); + p_vout->p->p_chroma = NULL; } - - /* following functions are local */ - -static int ReduceHeight( int i_ratio ) +static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y ) { - int i_dummy = VOUT_ASPECT_FACTOR; - int i_pgcd = 1; - - if( !i_ratio ) - { - return i_pgcd; - } - - /* VOUT_ASPECT_FACTOR is (2^7 * 3^3 * 5^3), we just check for 2, 3 and 5 */ - while( !(i_ratio & 1) && !(i_dummy & 1) ) - { - i_ratio >>= 1; - i_dummy >>= 1; - i_pgcd <<= 1; - } - - while( !(i_ratio % 3) && !(i_dummy % 3) ) - { - i_ratio /= 3; - i_dummy /= 3; - i_pgcd *= 3; - } - - while( !(i_ratio % 5) && !(i_dummy % 5) ) - { - i_ratio /= 5; - i_dummy /= 5; - i_pgcd *= 5; - } + const int i_pgcd = i_aspect ? GCD( i_aspect, VOUT_ASPECT_FACTOR ) : 1; + *i_aspect_x = i_aspect / i_pgcd; + *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd; +} - return i_pgcd; +/** + * This function copies all RGB informations from a picture_heap_t into + * a video_format_t + */ +static void VideoFormatImportRgb( video_format_t *p_fmt, const picture_heap_t *p_heap ) +{ + p_fmt->i_rmask = p_heap->i_rmask; + p_fmt->i_gmask = p_heap->i_gmask; + p_fmt->i_bmask = p_heap->i_bmask; + p_fmt->i_rrshift = p_heap->i_rrshift; + p_fmt->i_lrshift = p_heap->i_lrshift; + p_fmt->i_rgshift = p_heap->i_rgshift; + p_fmt->i_lgshift = p_heap->i_lgshift; + p_fmt->i_rbshift = p_heap->i_rbshift; + p_fmt->i_lbshift = p_heap->i_lbshift; } -static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y ) +/** + * This funtion copes all RGB informations from a video_format_t into + * a picture_heap_t + */ +static void VideoFormatExportRgb( const video_format_t *p_fmt, picture_heap_t *p_heap ) { - unsigned int i_pgcd = ReduceHeight( i_aspect ); - *i_aspect_x = i_aspect / i_pgcd; - *i_aspect_y = VOUT_ASPECT_FACTOR / i_pgcd; + p_heap->i_rmask = p_fmt->i_rmask; + p_heap->i_gmask = p_fmt->i_gmask; + p_heap->i_bmask = p_fmt->i_bmask; + p_heap->i_rrshift = p_fmt->i_rrshift; + p_heap->i_lrshift = p_fmt->i_lrshift; + p_heap->i_rgshift = p_fmt->i_rgshift; + p_heap->i_lgshift = p_fmt->i_lgshift; + p_heap->i_rbshift = p_fmt->i_rbshift; + p_heap->i_lbshift = p_fmt->i_lbshift; } -/***************************************************************************** - * BinaryLog: computes the base 2 log of a binary value - ***************************************************************************** - * This functions is used by MaskToShift, to get a bit index from a binary - * value. - *****************************************************************************/ -static int BinaryLog( uint32_t i ) +/** + * This function computes rgb shifts from masks + */ +static void PictureHeapFixRgb( picture_heap_t *p_heap ) { - int i_log = 0; + video_format_t fmt; - if( i == 0 ) return -31337; + /* */ + fmt.i_chroma = p_heap->i_chroma; + VideoFormatImportRgb( &fmt, p_heap ); - if( i & 0xffff0000 ) i_log += 16; - if( i & 0xff00ff00 ) i_log += 8; - if( i & 0xf0f0f0f0 ) i_log += 4; - if( i & 0xcccccccc ) i_log += 2; - if( i & 0xaaaaaaaa ) i_log += 1; + /* */ + video_format_FixRgb( &fmt ); - return i_log; + VideoFormatExportRgb( &fmt, p_heap ); } /***************************************************************************** - * MaskToShift: transform a color mask into right and left shifts - ***************************************************************************** - * This function is used for obtaining color shifts from masks. + * object variables callbacks: a bunch of object variables are used by the + * interfaces to interact with the vout. *****************************************************************************/ -static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask ) +static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) { - uint32_t i_low, i_high; /* lower hand higher bits of the mask */ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + input_thread_t *p_input; + (void)psz_cmd; (void)oldval; (void)p_data; - if( !i_mask ) + p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, + FIND_PARENT ); + if (!p_input) { - *pi_left = *pi_right = 0; - return; + msg_Err( p_vout, "Input not found" ); + return VLC_EGENERIC; } - /* Get bits */ - i_low = i_high = i_mask; + var_SetBool( p_vout, "intf-change", true ); - i_low &= - (int32_t)i_low; /* lower bit of the mask */ - i_high += i_low; /* higher bit of the mask */ + /* Modify input as well because the vout might have to be restarted */ + var_Create( p_input, "vout-filter", VLC_VAR_STRING ); + var_SetString( p_input, "vout-filter", newval.psz_string ); - /* Transform bits into an index. Also deal with i_high overflow, which - * is faster than changing the BinaryLog code to handle 64 bit integers. */ - i_low = BinaryLog (i_low); - i_high = i_high ? BinaryLog (i_high) : 32; + /* Now restart current video stream */ + input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES ); + vlc_object_release( p_input ); - /* Update pointers and return */ - *pi_left = i_low; - *pi_right = (8 - i_high + i_low); + return VLC_SUCCESS; } /***************************************************************************** - * vout_VarCallback: generic callback for intf variables + * Video Filter2 stuff *****************************************************************************/ -int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable, - vlc_value_t oldval, vlc_value_t newval, - void *p_data ) +static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) { - vout_thread_t * p_vout = (vout_thread_t *)p_this; - vlc_value_t val; - (void)psz_variable; (void)newval; (void)oldval; (void)p_data; - val.b_bool = true; - var_Set( p_vout, "intf-change", val ); + vout_thread_t *p_vout = (vout_thread_t *)p_this; + (void)psz_cmd; (void)oldval; (void)p_data; + + vlc_mutex_lock( &p_vout->p->vfilter_lock ); + p_vout->p->psz_vf2 = strdup( newval.psz_string ); + vlc_mutex_unlock( &p_vout->p->vfilter_lock ); + return VLC_SUCCESS; } /***************************************************************************** - * Helper thread for object variables callbacks. - * Only used to avoid deadlocks when using the video embedded mode. + * Post-processing *****************************************************************************/ -typedef struct suxor_thread_t +static bool PostProcessIsPresent( const char *psz_filter ) { - VLC_COMMON_MEMBERS - input_thread_t *p_input; + const char *psz_pp = "postproc"; + const size_t i_pp = strlen(psz_pp); + return psz_filter && + !strncmp( psz_filter, psz_pp, strlen(psz_pp) ) && + ( psz_filter[i_pp] == '\0' || psz_filter[i_pp] == ':' ); +} + +static int PostProcessCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) +{ + vout_thread_t *p_vout = (vout_thread_t *)p_this; + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); + + static const char *psz_pp = "postproc"; -} suxor_thread_t; + char *psz_vf2 = var_GetString( p_vout, "video-filter" ); -static void SuxorRestartVideoES( suxor_thread_t *p_this ) + if( newval.i_int <= 0 ) + { + if( PostProcessIsPresent( psz_vf2 ) ) + { + strcpy( psz_vf2, &psz_vf2[strlen(psz_pp)] ); + if( *psz_vf2 == ':' ) + strcpy( psz_vf2, &psz_vf2[1] ); + } + } + else + { + if( !PostProcessIsPresent( psz_vf2 ) ) + { + if( psz_vf2 ) + { + char *psz_tmp = psz_vf2; + if( asprintf( &psz_vf2, "%s:%s", psz_pp, psz_tmp ) < 0 ) + psz_vf2 = psz_tmp; + else + free( psz_tmp ); + } + else + { + psz_vf2 = strdup( psz_pp ); + } + } + } + if( psz_vf2 ) + { + var_SetString( p_vout, "video-filter", psz_vf2 ); + free( psz_vf2 ); + } + + return VLC_SUCCESS; +} +static void PostProcessEnable( vout_thread_t *p_vout ) { - /* Now restart current video stream */ - int val = var_GetInteger( p_this->p_input, "video-es" ); - if( val >= 0 ) + msg_Dbg( p_vout, "Post-processing available" ); + var_Create( p_vout, "postprocess", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); + for( int i = 0; i <= 6; i++ ) { - var_SetInteger( p_this->p_input, "video-es", -VIDEO_ES ); - var_SetInteger( p_this->p_input, "video-es", val ); + vlc_value_t val; + vlc_value_t text; + char psz_text[1+1]; + + val.i_int = i; + snprintf( psz_text, sizeof(psz_text), "%d", i ); + if( i == 0 ) + text.psz_string = _("Disable"); + else + text.psz_string = psz_text; + var_Change( p_vout, "postprocess", VLC_VAR_ADDCHOICE, &val, &text ); } + var_AddCallback( p_vout, "postprocess", PostProcessCallback, NULL ); + + /* */ + char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" ); + int i_postproc_q = 0; + if( PostProcessIsPresent( psz_filter ) ) + i_postproc_q = var_CreateGetInteger( p_vout, "postproc-q" ); + + var_SetInteger( p_vout, "postprocess", i_postproc_q ); + + free( psz_filter ); +} +static void PostProcessDisable( vout_thread_t *p_vout ) +{ + msg_Dbg( p_vout, "Post-processing no more available" ); + var_Destroy( p_vout, "postprocess" ); +} +static void PostProcessSetFilterQuality( vout_thread_t *p_vout ) +{ + vlc_object_t *p_pp = vlc_object_find_name( p_vout, "postproc", FIND_CHILD ); + if( !p_pp ) + return; + + var_SetInteger( p_pp, "postproc-q", var_GetInteger( p_vout, "postprocess" ) ); + vlc_object_release( p_pp ); +} + + +static void DisplayTitleOnOSD( vout_thread_t *p_vout ) +{ + const mtime_t i_start = mdate(); + const mtime_t i_stop = i_start + INT64_C(1000) * p_vout->p->i_title_timeout; + + if( i_stop <= i_start ) + return + + vlc_assert_locked( &p_vout->change_lock ); + + vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, + p_vout->p->psz_title, NULL, + p_vout->p->i_title_position, + 30 + p_vout->fmt_in.i_width + - p_vout->fmt_in.i_visible_width + - p_vout->fmt_in.i_x_offset, + 20 + p_vout->fmt_in.i_y_offset, + i_start, i_stop ); - vlc_object_release( p_this->p_input ); + free( p_vout->p->psz_title ); - vlc_object_release( p_this ); + p_vout->p->psz_title = NULL; } /***************************************************************************** - * object variables callbacks: a bunch of object variables are used by the - * interfaces to interact with the vout. + * Deinterlacing *****************************************************************************/ -static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) +typedef struct { - vout_thread_t *p_vout = (vout_thread_t *)p_this; - input_thread_t *p_input; - vlc_value_t val; - - char *psz_mode = newval.psz_string; - char *psz_filter, *psz_deinterlace = NULL; - (void)psz_cmd; (void)oldval; (void)p_data; + const char *psz_mode; + const char *psz_description; + bool b_vout_filter; +} deinterlace_mode_t; + +/* XXX + * You can use the non vout filter if and only if the video properties stay the + * same (width/height/chroma/fps), at least for now. + */ +static const deinterlace_mode_t p_deinterlace_mode[] = { + { "", "Disable", false }, + { "discard", "Discard", true }, + { "blend", "Blend", false }, + { "mean", "Mean", true }, + { "bob", "Bob", true }, + { "linear", "Linear", true }, + { "x", "X", false }, + { NULL, NULL, true } +}; + +static char *FilterFind( char *psz_filter_base, const char *psz_module ) +{ + const size_t i_module = strlen( psz_module ); + const char *psz_filter = psz_filter_base; - var_Get( p_vout, "vout-filter", &val ); - psz_filter = val.psz_string; - if( psz_filter ) psz_deinterlace = strstr( psz_filter, "deinterlace" ); + if( !psz_filter || i_module <= 0 ) + return NULL; - if( !psz_mode || !*psz_mode ) - { - if( psz_deinterlace ) - { - char *psz_src = psz_deinterlace + sizeof("deinterlace") - 1; - if( psz_src[0] == ':' ) psz_src++; - memmove( psz_deinterlace, psz_src, strlen(psz_src) + 1 ); - } - } - else if( !psz_deinterlace ) + for( ;; ) { - psz_filter = realloc( psz_filter, strlen( psz_filter ) + - sizeof(":deinterlace") ); - if( psz_filter && *psz_filter ) strcat( psz_filter, ":" ); - strcat( psz_filter, "deinterlace" ); + char *psz_find = strstr( psz_filter, psz_module ); + if( !psz_find ) + return NULL; + if( psz_find[i_module] == '\0' || psz_find[i_module] == ':' ) + return psz_find; + psz_filter = &psz_find[i_module]; } +} - p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, - FIND_PARENT ); - if( !p_input ) return VLC_EGENERIC; +static bool DeinterlaceIsPresent( vout_thread_t *p_vout, bool b_vout_filter ) +{ + char *psz_filter = var_GetNonEmptyString( p_vout, b_vout_filter ? "vout-filter" : "video-filter" ); + + bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL; + + free( psz_filter ); + + return b_found; +} + +static void DeinterlaceRemove( vout_thread_t *p_vout, bool b_vout_filter ) +{ + const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter"; + char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable ); - if( psz_mode && *psz_mode ) + char *psz = FilterFind( psz_filter, "deinterlace" ); + if( !psz ) { - /* Modify input as well because the vout might have to be restarted */ - val.psz_string = psz_mode; - var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING ); - var_Set( p_input, "deinterlace-mode", val ); + free( psz_filter ); + return; } - vlc_object_release( p_input ); - val.b_bool = true; - var_Set( p_vout, "intf-change", val ); + /* */ + strcpy( &psz[0], &psz[strlen("deinterlace")] ); + if( *psz == ':' ) + strcpy( &psz[0], &psz[1] ); - val.psz_string = psz_filter; - var_Set( p_vout, "vout-filter", val ); + var_SetString( p_vout, psz_variable, psz_filter ); free( psz_filter ); +} +static void DeinterlaceAdd( vout_thread_t *p_vout, bool b_vout_filter ) +{ + const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter"; - return VLC_SUCCESS; + char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable ); + + if( FilterFind( psz_filter, "deinterlace" ) ) + { + free( psz_filter ); + return; + } + + /* */ + if( psz_filter ) + { + char *psz_tmp = psz_filter; + if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 ) + psz_filter = psz_tmp; + else + free( psz_tmp ); + } + else + { + psz_filter = strdup( "deinterlace" ); + } + + if( psz_filter ) + { + var_SetString( p_vout, psz_variable, psz_filter ); + free( psz_filter ); + } } -static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) +static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) { vout_thread_t *p_vout = (vout_thread_t *)p_this; - input_thread_t *p_input; - vlc_value_t val; - (void)psz_cmd; (void)oldval; (void)p_data; - p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, - FIND_PARENT ); - if (!p_input) + /* */ + const deinterlace_mode_t *p_mode; + for( p_mode = &p_deinterlace_mode[0]; p_mode->psz_mode; p_mode++ ) { - msg_Err( p_vout, "Input not found" ); - return( VLC_EGENERIC ); + if( !strcmp( p_mode->psz_mode, newval.psz_string ?: "" ) ) + break; + } + if( !p_mode->psz_mode ) + { + msg_Err( p_this, "Invalid value (%s) ignored", newval.psz_string ); + return VLC_EGENERIC; } - val.b_bool = true; - var_Set( p_vout, "intf-change", val ); + /* We have to set input variable to ensure restart support + * XXX it is only needed because of vout-filter but must be done + * for non video filter anyway */ + input_thread_t *p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, FIND_PARENT ); + if( p_input ) + { + var_Create( p_input, "vout-deinterlace", VLC_VAR_STRING ); + var_SetString( p_input, "vout-deinterlace", p_mode->psz_mode ); - /* Modify input as well because the vout might have to be restarted */ - val.psz_string = newval.psz_string; - var_Create( p_input, "vout-filter", VLC_VAR_STRING ); + var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING ); + var_SetString( p_input, "deinterlace-mode", p_mode->psz_mode ); - var_Set( p_input, "vout-filter", val ); + var_Create( p_input, "sout-deinterlace-mode", VLC_VAR_STRING ); + var_SetString( p_input, "sout-deinterlace-mode", p_mode->psz_mode ); - /* Now restart current video stream */ - var_Get( p_input, "video-es", &val ); - if( val.i_int >= 0 ) + vlc_object_release( p_input ); + } + + char *psz_old; + + if( p_mode->b_vout_filter ) { - static const char typename[] = "kludge"; - suxor_thread_t *p_suxor = - vlc_custom_create( p_vout, sizeof(suxor_thread_t), - VLC_OBJECT_GENERIC, typename ); - p_suxor->p_input = p_input; - p_vout->b_filter_change = true; - vlc_object_yield( p_input ); - vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES, - VLC_THREAD_PRIORITY_LOW, false ); + psz_old = var_CreateGetString( p_vout, "deinterlace-mode" ); + } + else + { + psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" ); + var_SetString( p_vout, "sout-deinterlace-mode", p_mode->psz_mode ); } - vlc_object_release( p_input ); + /* */ + if( !strcmp( p_mode->psz_mode, "" ) ) + { + DeinterlaceRemove( p_vout, false ); + DeinterlaceRemove( p_vout, true ); + } + else if( !DeinterlaceIsPresent( p_vout, p_mode->b_vout_filter ) ) + { + DeinterlaceRemove( p_vout, !p_mode->b_vout_filter ); + DeinterlaceAdd( p_vout, p_mode->b_vout_filter ); + } + else + { + DeinterlaceRemove( p_vout, !p_mode->b_vout_filter ); + if( psz_old && strcmp( psz_old, p_mode->psz_mode ) ) + var_TriggerCallback( p_vout, p_mode->b_vout_filter ? "vout-filter" : "video-filter" ); + } + free( psz_old ); + (void)psz_cmd; (void) oldval; (void) p_data; return VLC_SUCCESS; } -/***************************************************************************** - * Video Filter2 stuff - *****************************************************************************/ -static int VideoFilter2Callback( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) +static void DeinterlaceEnable( vout_thread_t *p_vout ) { - vout_thread_t *p_vout = (vout_thread_t *)p_this; - (void)psz_cmd; (void)oldval; (void)p_data; + vlc_value_t val, text; - vlc_mutex_lock( &p_vout->vfilter_lock ); - p_vout->psz_vf2 = strdup( newval.psz_string ); - vlc_mutex_unlock( &p_vout->vfilter_lock ); + if( !p_vout->p->b_first_vout ) + return; - return VLC_SUCCESS; -} + msg_Dbg( p_vout, "Deinterlacing available" ); -static void DisplayTitleOnOSD( vout_thread_t *p_vout ) -{ - input_thread_t *p_input; - mtime_t i_now, i_stop; + /* Create the configuration variable */ + var_Create( p_vout, "deinterlace", VLC_VAR_STRING | VLC_VAR_HASCHOICE ); + text.psz_string = _("Deinterlace"); + var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL ); - p_input = (input_thread_t *)vlc_object_find( p_vout, - VLC_OBJECT_INPUT, FIND_ANYWHERE ); - if( p_input ) + for( int i = 0; p_deinterlace_mode[i].psz_mode; i++ ) { - i_now = mdate(); - i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000); - char *psz_nowplaying = - input_item_GetNowPlaying( input_GetItem( p_input ) ); - char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) ); - char *psz_name = input_item_GetTitle( input_GetItem( p_input ) ); - if( EMPTY_STR( psz_name ) ) - { - free( psz_name ); - psz_name = input_item_GetName( input_GetItem( p_input ) ); - } - if( !EMPTY_STR( psz_nowplaying ) ) - { - vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, - psz_nowplaying, NULL, - p_vout->i_title_position, - 30 + p_vout->fmt_in.i_width - - p_vout->fmt_in.i_visible_width - - p_vout->fmt_in.i_x_offset, - 20 + p_vout->fmt_in.i_y_offset, - i_now, i_stop ); - } - else if( !EMPTY_STR( psz_artist ) ) - { - char *psz_string = NULL; - if( asprintf( &psz_string, "%s - %s", psz_name, psz_artist ) != -1 ) - { - vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, - psz_string, NULL, - p_vout->i_title_position, - 30 + p_vout->fmt_in.i_width - - p_vout->fmt_in.i_visible_width - - p_vout->fmt_in.i_x_offset, - 20 + p_vout->fmt_in.i_y_offset, - i_now, i_stop ); - free( psz_string ); - } - } - else - { - vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, - psz_name, NULL, - p_vout->i_title_position, - 30 + p_vout->fmt_in.i_width - - p_vout->fmt_in.i_visible_width - - p_vout->fmt_in.i_x_offset, - 20 + p_vout->fmt_in.i_y_offset, - i_now, i_stop ); - } - vlc_object_release( p_input ); - free( psz_artist ); - free( psz_name ); - free( psz_nowplaying ); + val.psz_string = (char*)p_deinterlace_mode[i].psz_mode; + text.psz_string = (char*)vlc_gettext(p_deinterlace_mode[i].psz_description); + var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); + } + var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL ); + + /* */ + char *psz_mode = NULL; + if( var_Type( p_vout, "vout-deinterlace" ) != 0 ) + psz_mode = var_CreateGetNonEmptyString( p_vout, "vout-deinterlace" ); + if( !psz_mode ) + { + /* Get the initial value */ + if( DeinterlaceIsPresent( p_vout, true ) ) + psz_mode = var_CreateGetNonEmptyString( p_vout, "deinterlace-mode" ); + else if( DeinterlaceIsPresent( p_vout, false ) ) + psz_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" ); } + var_SetString( p_vout, "deinterlace", psz_mode ?: "" ); + free( psz_mode ); }