X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fvideo_output%2Fvideo_output.c;h=c090c111fc1394d24c50f11260946a24204890d4;hb=0f2826187da505da4c207e2eada88da53c455d41;hp=b1003971207ad5fafda2eb9771cc0cd4e8c11009;hpb=be50cf5ba79d4e1203b87d9b32e7b921d1bc4854;p=vlc diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c index b100397120..c090c111fc 100644 --- a/src/video_output/video_output.c +++ b/src/video_output/video_output.c @@ -29,7 +29,11 @@ /***************************************************************************** * Preamble *****************************************************************************/ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include #include /* free() */ #include @@ -40,10 +44,10 @@ #endif #include -#include #include #include +#include #if defined( __APPLE__ ) /* Include darwin_specific.h here if needed */ @@ -51,62 +55,100 @@ /** 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 "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 DestroyThread ( 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 * ); -/* Video filter2 parsing */ -static int ParseVideoFilter2Chain( vout_thread_t *, char * ); -static void RemoveVideoFilters2( vout_thread_t *p_vout ); - /* 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 *****************************************************************************/ -struct filter_owner_sys_t -{ - vout_thread_t *p_vout; -}; - static picture_t *video_new_buffer_filter( filter_t *p_filter ) { - picture_t *p_picture; - vout_thread_t *p_vout = p_filter->p_owner->p_vout; + 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( p_filter->p_owner->p_vout, 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 ) +{ + p_filter->pf_vout_buffer_new = video_new_buffer_filter; + p_filter->pf_vout_buffer_del = video_del_buffer_filter; + p_filter->p_owner = p_data; /* p_vout */ + return VLC_SUCCESS; } /***************************************************************************** @@ -120,98 +162,121 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout, { if( !p_fmt ) { - /* Reattach video output to playlist before bailing out */ + /* Video output is no longer used. + * TODO: support for reusing video outputs with proper _thread-safe_ + * reference handling. */ if( p_vout ) - { - playlist_t *p_playlist = pl_Yield( p_this ); - spu_Attach( p_vout->p_spu, p_this, VLC_FALSE ); - vlc_object_detach( p_vout ); - vlc_object_attach( p_vout, p_playlist ); - pl_Release( p_this ); - } + 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 ); } - else - { - p_vout = vlc_object_find( p_this, VLC_OBJECT_VOUT, FIND_CHILD ); - if( !p_vout ) - { - playlist_t *p_playlist = pl_Yield( p_this ); - vlc_mutex_lock( &p_playlist->gc_lock ); - p_vout = vlc_object_find( p_playlist, - VLC_OBJECT_VOUT, FIND_CHILD ); - /* only first children of p_input for unused vout */ - if( p_vout && p_vout->p_parent != (vlc_object_t *)p_playlist ) - { - vlc_object_release( p_vout ); - p_vout = NULL; - } - if( p_vout ) - vlc_object_detach( p_vout ); /* Remove it from the GC */ - vlc_mutex_unlock( &p_playlist->gc_lock ); - pl_Release( p_this ); - } - } + /* TODO: find a suitable unused video output */ /* 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 = VLC_FALSE; + p_vout->p->b_filter_change = false; } - if( psz_filter_chain ) free( psz_filter_chain ); + 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_chroma != p_fmt->i_chroma ) || - ( 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 */ + vout_CloseAndRelease( p_vout ); vlc_object_release( p_vout ); - vout_Destroy( p_vout ); p_vout = NULL; } else { /* This video output is cool! Hijack it. */ - spu_Attach( p_vout->p_spu, p_this, VLC_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 ) @@ -233,24 +298,38 @@ 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 */ - p_vout = vlc_object_create( p_parent, VLC_OBJECT_VOUT ); + static const char typename[] = "video output"; + p_vout = vlc_custom_create( p_parent, sizeof( *p_vout ), VLC_OBJECT_VOUT, + typename ); if( p_vout == NULL ) + return NULL; + + /* */ + p_vout->p = calloc( 1, sizeof(*p_vout->p) ); + if( !p_vout->p ) { - msg_Err( p_parent, "out of memory" ); + vlc_object_release( p_vout ); return NULL; } @@ -271,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 */ @@ -281,9 +358,9 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) p_vout->render.i_chroma = i_chroma; p_vout->render.i_aspect = i_aspect; - p_vout->render.i_rmask = 0; - p_vout->render.i_gmask = 0; - p_vout->render.i_bmask = 0; + p_vout->render.i_rmask = p_fmt->i_rmask; + p_vout->render.i_gmask = p_fmt->i_gmask; + p_vout->render.i_bmask = p_fmt->i_bmask; p_vout->render.i_last_used_pic = -1; p_vout->render.b_allow_modify_pics = 1; @@ -301,59 +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->pf_control = 0; - p_vout->p_parent_intf = 0; - p_vout->i_par_num = p_vout->i_par_den = 1; + 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->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, &p_vout->picture_lock ); - vlc_mutex_init( p_vout, &p_vout->change_lock ); - vlc_mutex_init( p_vout, &p_vout->vfilter_lock ); + 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->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, VLC_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 */ - var_Create( p_vout, "vout-filter", VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Get( p_vout, "vout-filter", &val ); - p_vout->psz_filter_chain = val.psz_string; + p_vout->p->psz_filter_chain = + var_CreateGetStringCommand( p_vout, "vout-filter" ); /* Apply video filter2 objects on the first vout */ - var_Create( p_vout, "video-filter", - VLC_VAR_STRING | VLC_VAR_DOINHERIT ); - var_Get( p_vout, "video-filter", &val ); - ParseVideoFilter2Chain( p_vout, val.psz_string ); - free( val.psz_string ); + p_vout->p->psz_vf2 = + var_CreateGetStringCommand( p_vout, "video-filter" ); + + p_vout->p->b_first_vout = true; } else { @@ -361,115 +451,95 @@ 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 ); - ParseVideoFilter2Chain( p_vout, NULL ); + var_Create( p_vout, "video-filter", + VLC_VAR_STRING | VLC_VAR_ISCOMMAND ); + 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->b_vfilter_change = VLC_TRUE; - p_vout->i_vfilters = 0; + 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, 0 ); + 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" ); - vlc_object_detach( p_vout ); - vlc_object_destroy( 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 ); - if( val.psz_string ) 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, VLC_TRUE ) ) + /* */ + vlc_cond_init( &p_vout->p->change_wait ); + if( vlc_clone( &p_vout->p->thread, RunThread, p_vout, + VLC_THREAD_PRIORITY_OUTPUT ) ) { - msg_Err( p_vout, "out of memory" ); - module_Unneed( p_vout, p_vout->p_module ); - vlc_object_detach( p_vout ); - vlc_object_destroy( p_vout ); + 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_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 */ - vlc_object_kill( p_vout ); - vlc_thread_join( p_vout ); - - vlc_object_detach( p_vout ); - vlc_object_destroy( p_vout ); + vout_CloseAndRelease( p_vout ); return NULL; } @@ -477,49 +547,257 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) } /***************************************************************************** - * vout_Destroy: destroys a previously created video output + * vout_Close: Close a vout created by vout_Create. ***************************************************************************** - * Destroy a terminated thread. - * The function will request a destruction of the specified thread. If pi_error - * is NULL, it will return once the thread is destroyed. Else, it will be - * update using one of the THREAD_* constants. + * 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_Destroy( vout_thread_t *p_vout ) +void vout_Close( vout_thread_t *p_vout ) { - vout_thread_t *p_another_vout; - playlist_t *p_playlist = pl_Yield( 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; +} - /* Request thread destruction */ - vlc_object_kill( p_vout ); - vlc_thread_join( p_vout ); +/* */ +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->p->vfilter_lock ); - var_Destroy( p_vout, "intf-change" ); + /* */ + 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 ); - if( p_vout->psz_filter_chain ) free( p_vout->psz_filter_chain ); + /* */ + free( p_vout->p->psz_filter_chain ); + free( p_vout->p->psz_title ); config_ChainDestroy( p_vout->p_cfg ); - /* Free structure */ - vlc_object_destroy( p_vout ); + free( p_vout->p ); + #ifndef __APPLE__ - /* This is a dirty hack for mostly Linux, where there is no way to get the GUI - back if you closed it while playing video. This is solved in Mac OS X, - where we have this novelty called menubar, that will always allow you access - to the applications main functionality. They should try that on linux sometime */ - p_another_vout = vlc_object_find( p_playlist, + vout_thread_t *p_another_vout; + + /* This is a dirty hack mostly for Linux, where there is no way to get the + * GUI back if you closed it while playing video. This is solved in + * Mac OS X, where we have this novelty called menubar, that will always + * allow you access to the applications main functionality. They should try + * that on linux sometime. */ + p_another_vout = vlc_object_find( p_this->p_libvlc, VLC_OBJECT_VOUT, FIND_ANYWHERE ); if( p_another_vout == NULL ) + var_SetBool( p_this->p_libvlc, "intf-show", true ); + else + vlc_object_release( p_another_vout ); +#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 ) { - vlc_value_t val; - val.b_bool = VLC_TRUE; - var_Set( p_playlist, "intf-show", val ); + 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_object_release( p_another_vout ); + vlc_mutex_unlock( &p_vout->picture_lock ); } -#endif - vlc_object_release( p_playlist ); + 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 ); } /***************************************************************************** @@ -528,30 +806,42 @@ void vout_Destroy( vout_thread_t *p_vout ) * This function is called from RunThread and performs the second step of the * initialization. It returns 0 on success. Note that the thread's flag are not * modified inside this function. + * XXX You have to enter it with change_lock taken. *****************************************************************************/ +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; - vlc_mutex_lock( &p_vout->change_lock ); - -#ifdef STATS - p_vout->c_loops = 0; -#endif - /* Initialize output method, it allocates direct buffers for us */ if( p_vout->pf_init( p_vout ) ) - { - vlc_mutex_unlock( &p_vout->change_lock ); return VLC_EGENERIC; - } + + p_vout->p->p_picture_displayed = NULL; if( !I_OUTPUTPICTURES ) { msg_Err( p_vout, "plugin was unable to allocate at least " "one direct buffer" ); p_vout->pf_end( p_vout ); - vlc_mutex_unlock( &p_vout->change_lock ); return VLC_EGENERIC; } @@ -560,7 +850,6 @@ static int InitThread( vout_thread_t *p_vout ) msg_Err( p_vout, "plugin allocated too many direct buffers, " "our internal buffers must have overflown." ); p_vout->pf_end( p_vout ); - vlc_mutex_unlock( &p_vout->change_lock ); return VLC_EGENERIC; } @@ -568,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 = @@ -614,35 +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->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++ ) { @@ -667,18 +958,11 @@ 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; - /* Choose the best module */ - p_vout->chroma.p_module = module_Need( p_vout, "chroma", NULL, 0 ); - - if( p_vout->chroma.p_module == NULL ) + if( ChromaCreate( p_vout ) ) { - msg_Err( p_vout, "no chroma module for %4.4s to %4.4s", - (char*)&p_vout->render.i_chroma, - (char*)&p_vout->output.i_chroma ); p_vout->pf_end( p_vout ); - vlc_mutex_unlock( &p_vout->change_lock ); return VLC_EGENERIC; } @@ -699,18 +983,6 @@ static int InitThread( vout_thread_t *p_vout ) } } - /* Link pictures back to their heap */ - for( i = 0 ; i < I_RENDERPICTURES ; i++ ) - { - PP_RENDERPICTURE[ i ]->p_heap = &p_vout->render; - } - - for( i = 0 ; i < I_OUTPUTPICTURES ; i++ ) - { - PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output; - } - -/* XXX XXX mark thread ready */ return VLC_SUCCESS; } @@ -721,204 +993,116 @@ 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; - vlc_bool_t b_drop_late; - - int i_displayed = 0, i_lost = 0, i_loops = 0; + bool b_drop_late; /* * Initialize thread */ + 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 ) { - /* Destroy thread structures allocated by Create and InitThread */ - DestroyThread( p_vout ); - return; + EndThread( p_vout ); + vlc_mutex_unlock( &p_vout->change_lock ); + return NULL; } - if( p_vout->b_title_show ) - DisplayTitleOnOSD( p_vout ); - /* * Main loop - it is not executed if an error occurred during * initialization */ - while( (!p_vout->b_die) && (!p_vout->b_error) ) + while( !p_vout->p->b_done && !p_vout->b_error ) { /* Initialize loop variables */ - p_picture = NULL; - display_date = 0; - current_date = mdate(); + 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_input && p_input->b_die ) - { - vlc_object_release( p_input ); - p_input = NULL; - } + if( p_vout->p->b_title_show && p_vout->p->psz_title ) + DisplayTitleOnOSD( p_vout ); - i_loops++; - if( i_loops % 20 == 0 ) + 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; + for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) { - if( !p_input ) + 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; + + 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 ); + p_picture = p_pic; } } -#if 0 - p_vout->c_loops++; - if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) ) + if( !p_picture ) { - msg_Dbg( p_vout, "picture heap: %d/%d", - I_RENDERPICTURES, p_vout->i_heap_size ); - } -#endif + p_picture = p_last; - /* - * 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( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE) - && ( (p_picture == NULL) || - (PP_RENDERPICTURE[i_index]->date < display_date) ) ) + 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; - - 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 ("I64Fd")", - 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 " - "("I64Fd")", display_date - current_date - - p_vout->i_pts_delay ); - vlc_mutex_unlock( &p_vout->picture_lock ); - - continue; - } + p_vout->p->p_fps_sample[ p_vout->p->c_fps_samples++ % VOUT_FPS_SAMPLES ] = display_date; - 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 @@ -927,7 +1111,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 */ @@ -940,133 +1124,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; } } - } - - if( p_picture == NULL ) - { - i_idle_loops++; - } - - /* Video Filter2 stuff */ - if( p_vout->b_vfilter_change == VLC_TRUE ) - { - int i; - vlc_mutex_lock( &p_vout->vfilter_lock ); - RemoveVideoFilters2( p_vout ); - for( i = 0; i < p_vout->i_vfilters_cfg; i++ ) + else if( p_vout->p->b_paused && display_date > current_date + VOUT_DISPLAY_DELAY ) { - filter_t *p_vfilter = - p_vout->pp_vfilters[p_vout->i_vfilters] = - vlc_object_create( p_vout, VLC_OBJECT_FILTER ); - - vlc_object_attach( p_vfilter, p_vout ); - - p_vfilter->pf_vout_buffer_new = video_new_buffer_filter; - p_vfilter->pf_vout_buffer_del = video_del_buffer_filter; - - if( !p_vout->i_vfilters ) - { - p_vfilter->fmt_in.video = p_vout->fmt_render; - } - else - { - p_vfilter->fmt_in.video = (p_vfilter-1)->fmt_out.video; - } - /* TODO: one day filters in the middle of the chain might - * have a different fmt_out.video than fmt_render ... */ - p_vfilter->fmt_out.video = p_vout->fmt_render; - - p_vfilter->p_cfg = p_vout->p_vfilters_cfg[i]; - p_vfilter->p_module = module_Need( p_vfilter, "video filter2", - p_vout->psz_vfilters[i], - VLC_TRUE ); - - if( p_vfilter->p_module ) - { - p_vfilter->p_owner = - malloc( sizeof( filter_owner_sys_t ) ); - p_vfilter->p_owner->p_vout = p_vout; - p_vout->i_vfilters++; - msg_Dbg( p_vout, "video filter found (%s)", - p_vout->psz_vfilters[i] ); - } - else - { - msg_Err( p_vout, "no video filter found (%s)", - p_vout->psz_vfilters[i] ); - vlc_object_detach( p_vfilter ); - vlc_object_destroy( p_vfilter ); - } + display_date = current_date + VOUT_DISPLAY_DELAY; } - p_vout->b_vfilter_change = VLC_FALSE; - vlc_mutex_unlock( &p_vout->vfilter_lock ); - } - if( p_picture ) - { - int i; - for( i = 0; i < p_vout->i_vfilters; i++ ) + if( p_picture ) { - picture_t *p_old = p_picture; - p_picture = p_vout->pp_vfilters[i]->pf_video_filter( - p_vout->pp_vfilters[i], p_picture ); - if( !p_picture ) - { - break; - } - /* FIXME: this is kind of wrong - * if you have 2 or more vfilters and the 2nd breaks, - * on the next loop the 1st one will be applied again */ - - /* if p_old and p_picture are the same (ie the filter - * worked on the old picture), then following code is - * still alright since i_status gets changed back to - * the right value */ - if( p_old->i_refcount ) - { - p_old->i_status = DISPLAYED_PICTURE; - } - else + if( p_picture->date > 1 ) { - p_old->i_status = DESTROYED_PICTURE; + 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_picture->i_status = READY_PICTURE; + p_vout->p->p_picture_displayed = p_picture; } } - if( p_picture && p_vout->b_snapshot ) + /* */ + 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_filtered_picture = filter_chain_VideoFilter( p_vout->p->p_vf2_chain, + p_picture ); + + bool b_snapshot = false; + if( vlc_mutex_trylock( &p_vout->p->snapshot.lock ) == 0 ) { - p_vout->b_snapshot = VLC_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 : VLC_FALSE ); - } + 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 ); @@ -1079,15 +1226,18 @@ 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 */ @@ -1096,37 +1246,46 @@ static void RunThread( vout_thread_t *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. */ + /* 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 ) @@ -1143,7 +1302,17 @@ static void RunThread( vout_thread_t *p_vout) /* A fatal error occurred, and the thread must terminate * immediately, without displaying anything - setting b_error to 1 * causes the immediate end of the main while() loop. */ + // FIXME pf_end p_vout->b_error = 1; + 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 ) @@ -1156,11 +1325,21 @@ static void RunThread( vout_thread_t *p_vout) p_vout->i_changes &= ~VOUT_SIZE_CHANGE; + assert( !p_vout->p->b_direct ); + + ChromaDestroy( p_vout ); + + vlc_mutex_lock( &p_vout->picture_lock ); + 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; + if( p_vout->pf_init( p_vout ) ) { msg_Err( p_vout, "cannot resize display" ); @@ -1168,13 +1347,19 @@ static void RunThread( vout_thread_t *p_vout) p_vout->b_error = 1; } - /* Need to reinitialise the chroma plugin */ - if( p_vout->chroma.p_module ) + vlc_mutex_unlock( &p_vout->picture_lock ); + + /* Need to reinitialise the chroma plugin. Since we might need + * resizing too and it's not sure that we already had it, + * recreate the chroma plugin chain from scratch. */ + /* dionoea */ + if( ChromaCreate( p_vout ) ) { - if( p_vout->chroma.p_module->pf_deactivate ) - p_vout->chroma.p_module->pf_deactivate( VLC_OBJECT(p_vout) ); - p_vout->chroma.p_module->pf_activate( VLC_OBJECT(p_vout) ); + msg_Err( p_vout, "WOW THIS SUCKS BIG TIME!!!!!" ); + p_vout->b_error = 1; } + if( p_vout->b_error ) + break; } if( p_vout->i_changes & VOUT_PICTURE_BUFFERS_CHANGE ) @@ -1186,10 +1371,8 @@ static void RunThread( vout_thread_t *p_vout) * buffer!! */ p_vout->i_changes &= ~VOUT_PICTURE_BUFFERS_CHANGE; - if( !p_vout->b_direct ) - { - module_Unneed( p_vout, p_vout->chroma.p_module ); - } + if( !p_vout->p->b_direct ) + ChromaDestroy( p_vout ); vlc_mutex_lock( &p_vout->picture_lock ); @@ -1198,29 +1381,59 @@ static void RunThread( vout_thread_t *p_vout) I_OUTPUTPICTURES = I_RENDERPICTURES = 0; p_vout->b_error = InitThread( p_vout ); + if( p_vout->b_error ) + 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; } - } - if( p_input ) - { - vlc_object_release( p_input ); + /* 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->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->p_vf2_chain, &fmt, &fmt ); + + 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->p->psz_vf2 ); + p_vout->p->psz_vf2 = NULL; + + if( i_picture_qtype_last != QTYPE_NONE ) + PostProcessSetFilterQuality( p_vout ); + } + vlc_mutex_unlock( &p_vout->p->vfilter_lock ); } /* * Error loop - wait until the thread destruction is requested */ if( p_vout->b_error ) - { ErrorThread( p_vout ); - } /* End of thread */ + CleanThread( p_vout ); EndThread( p_vout ); + vlc_mutex_unlock( &p_vout->change_lock ); - /* Destroy thread structures allocated by CreateThread */ - DestroyThread( p_vout ); + return NULL; } /***************************************************************************** @@ -1232,38 +1445,24 @@ static void RunThread( vout_thread_t *p_vout) *****************************************************************************/ static void ErrorThread( vout_thread_t *p_vout ) { - /* Wait until a `die' order */ - while( !p_vout->b_die ) - { - /* Sleep a while */ - msleep( VOUT_IDLE_SLEEP ); - } + /* Wait until a `close' order */ + while( !p_vout->p->b_done ) + vlc_cond_wait( &p_vout->p->change_wait, &p_vout->change_lock ); } /***************************************************************************** - * EndThread: thread destruction + * CleanThread: clean up after InitThread ***************************************************************************** - * This function is called when the thread ends after a sucessful + * This function is called after a sucessful * initialization. It frees all resources allocated by InitThread. + * XXX You have to enter it with change_lock taken. *****************************************************************************/ -static void EndThread( vout_thread_t *p_vout ) +static void CleanThread( vout_thread_t *p_vout ) { int i_index; /* index in heap */ -#ifdef STATS - { - struct tms cpu_usage; - times( &cpu_usage ); - - msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)", - cpu_usage.tms_utime, cpu_usage.tms_stime ); - } -#endif - - if( !p_vout->b_direct ) - { - module_Unneed( p_vout, p_vout->chroma.p_module ); - } + if( !p_vout->p->b_direct ) + ChromaDestroy( p_vout ); /* Destroy all remaining pictures */ for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ ) @@ -1274,423 +1473,547 @@ static void EndThread( vout_thread_t *p_vout ) } } - /* Destroy subpicture unit */ - spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), VLC_FALSE ); - spu_Destroy( p_vout->p_spu ); - - /* Destroy the video filters2 */ - RemoveVideoFilters2( p_vout ); - /* Destroy translation tables */ - p_vout->pf_end( p_vout ); - - /* Release the change lock */ - vlc_mutex_unlock( &p_vout->change_lock ); + if( !p_vout->b_error ) + p_vout->pf_end( p_vout ); } /***************************************************************************** - * DestroyThread: thread destruction + * EndThread: thread destruction ***************************************************************************** - * This function is called when the thread ends. It frees all ressources - * allocated by CreateThread. Status is available at this stage. + * This function is called when the thread ends. + * It frees all resources not allocated by InitThread. + * XXX You have to enter it with change_lock taken. *****************************************************************************/ -static void DestroyThread( vout_thread_t *p_vout ) +static void EndThread( vout_thread_t *p_vout ) { - /* Destroy the locks */ - vlc_mutex_destroy( &p_vout->picture_lock ); - vlc_mutex_destroy( &p_vout->change_lock ); - vlc_mutex_destroy( &p_vout->vfilter_lock ); - - /* Release the module */ - if( p_vout && p_vout->p_module ) +#ifdef STATS { - module_Unneed( p_vout, p_vout->p_module ); + struct tms cpu_usage; + times( &cpu_usage ); + + msg_Dbg( p_vout, "cpu usage (user: %d, system: %d)", + cpu_usage.tms_utime, cpu_usage.tms_stime ); } +#endif + + /* FIXME does that function *really* need to be called inside the thread ? */ + + /* Detach subpicture unit from both input and vout */ + spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), false ); + vlc_object_detach( p_vout->p_spu ); + + /* Destroy the video filters2 */ + filter_chain_Delete( p_vout->p->p_vf2_chain ); } -/* following functions are local */ +/* Thread helpers */ +static picture_t *ChromaGetPicture( filter_t *p_filter ) +{ + picture_t *p_pic = (picture_t *)p_filter->p_owner; + p_filter->p_owner = NULL; + return p_pic; +} -static int ReduceHeight( int i_ratio ) +static int ChromaCreate( vout_thread_t *p_vout ) { - int i_dummy = VOUT_ASPECT_FACTOR; - int i_pgcd = 1; + static const char typename[] = "chroma"; + filter_t *p_chroma; - if( !i_ratio ) - { - return i_pgcd; - } + /* Choose the best module */ + p_chroma = p_vout->p->p_chroma = + vlc_custom_create( p_vout, sizeof(filter_t), VLC_OBJECT_GENERIC, + typename ); - /* 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; - } + vlc_object_attach( p_chroma, p_vout ); - while( !(i_ratio % 3) && !(i_dummy % 3) ) - { - i_ratio /= 3; - i_dummy /= 3; - i_pgcd *= 3; - } + /* 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; + 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, false ); - while( !(i_ratio % 5) && !(i_dummy % 5) ) + if( p_chroma->p_module == NULL ) { - i_ratio /= 5; - i_dummy /= 5; - i_pgcd *= 5; + msg_Err( p_vout, "no chroma module for %4.4s to %4.4s i=%dx%d o=%dx%d", + (char*)&p_vout->render.i_chroma, + (char*)&p_vout->output.i_chroma, + p_chroma->fmt_in.video.i_width, p_chroma->fmt_in.video.i_height, + p_chroma->fmt_out.video.i_width, p_chroma->fmt_out.video.i_height + ); + + vlc_object_release( p_vout->p->p_chroma ); + p_vout->p->p_chroma = NULL; + + return VLC_EGENERIC; } + p_chroma->pf_vout_buffer_new = ChromaGetPicture; + return VLC_SUCCESS; +} + +static void ChromaDestroy( vout_thread_t *p_vout ) +{ + assert( !p_vout->p->b_direct ); - return i_pgcd; + if( !p_vout->p->p_chroma ) + return; + + 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 void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y ) { - unsigned int i_pgcd = ReduceHeight( i_aspect ); + 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; } -/***************************************************************************** - * 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 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 ) { - int i_log = 0; + 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; +} - if( i == 0 ) return -31337; +/** + * 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 ) +{ + 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; +} + +/** + * This function computes rgb shifts from masks + */ +static void PictureHeapFixRgb( picture_heap_t *p_heap ) +{ + video_format_t fmt; - 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; + /* */ + fmt.i_chroma = p_heap->i_chroma; + VideoFormatImportRgb( &fmt, p_heap ); - return i_log; + /* */ + video_format_FixRgb( &fmt ); + + 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 = VLC_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; - -} suxor_thread_t; + 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 void SuxorRestartVideoES( suxor_thread_t *p_this ) +static int PostProcessCallback( vlc_object_t *p_this, char const *psz_cmd, + vlc_value_t oldval, vlc_value_t newval, void *p_data ) { - vlc_value_t val; + vout_thread_t *p_vout = (vout_thread_t *)p_this; + VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(p_data); - vlc_thread_ready( p_this ); + static const char *psz_pp = "postproc"; - /* Now restart current video stream */ - var_Get( p_this->p_input, "video-es", &val ); - if( val.i_int >= 0 ) + char *psz_vf2 = var_GetString( p_vout, "video-filter" ); + + if( newval.i_int <= 0 ) { - vlc_value_t val_es; - val_es.i_int = -VIDEO_ES; - var_Set( p_this->p_input, "video-es", val_es ); - var_Set( p_this->p_input, "video-es", val ); + 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 ) +{ + 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++ ) + { + vlc_value_t val; + vlc_value_t text; + char psz_text[1+1]; - vlc_object_release( p_this->p_input ); + 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 ); -#ifdef WIN32 - CloseHandle( p_this->p_internals->thread_id ); -#endif + /* */ + 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 ); +} - vlc_object_destroy( p_this ); + +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 ); + + free( p_vout->p->psz_title ); + + 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; + 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 } +}; - char *psz_mode = newval.psz_string; - char *psz_filter, *psz_deinterlace = NULL; - (void)psz_cmd; (void)oldval; (void)p_data; +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 ) + for( ;; ) { - 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 ); - } + 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]; } - else if( !psz_deinterlace ) - { - psz_filter = realloc( psz_filter, strlen( psz_filter ) + - sizeof(":deinterlace") ); - if( psz_filter && *psz_filter ) strcat( psz_filter, ":" ); - strcat( psz_filter, "deinterlace" ); - } - - p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, - FIND_PARENT ); - if( !p_input ) return VLC_EGENERIC; +} - if( psz_mode && *psz_mode ) - { - /* 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 ); - } - vlc_object_release( p_input ); +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" ); - val.b_bool = VLC_TRUE; - var_Set( p_vout, "intf-change", val ); + bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL; - val.psz_string = psz_filter; - var_Set( p_vout, "vout-filter", val ); - if( psz_filter ) free( psz_filter ); + free( psz_filter ); - return VLC_SUCCESS; + return b_found; } -static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) +static void DeinterlaceRemove( vout_thread_t *p_vout, bool b_vout_filter ) { - 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; + const char *psz_variable = b_vout_filter ? "vout-filter" : "video-filter"; + char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable ); - p_input = (input_thread_t *)vlc_object_find( p_this, VLC_OBJECT_INPUT, - FIND_PARENT ); - if (!p_input) + char *psz = FilterFind( psz_filter, "deinterlace" ); + if( !psz ) { - msg_Err( p_vout, "Input not found" ); - return( VLC_EGENERIC ); + free( psz_filter ); + return; } - val.b_bool = VLC_TRUE; - var_Set( p_vout, "intf-change", val ); + /* */ + strcpy( &psz[0], &psz[strlen("deinterlace")] ); + if( *psz == ':' ) + strcpy( &psz[0], &psz[1] ); - /* 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_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"; - var_Set( p_input, "vout-filter", val ); + char *psz_filter = var_GetNonEmptyString( p_vout, psz_variable ); - /* Now restart current video stream */ - var_Get( p_input, "video-es", &val ); - if( val.i_int >= 0 ) + if( FilterFind( psz_filter, "deinterlace" ) ) { - suxor_thread_t *p_suxor = - vlc_object_create( p_vout, sizeof(suxor_thread_t) ); - p_suxor->p_input = p_input; - p_vout->b_filter_change = VLC_TRUE; - vlc_object_yield( p_input ); - vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES, - VLC_THREAD_PRIORITY_LOW, VLC_FALSE ); + free( psz_filter ); + return; } - vlc_object_release( p_input ); - - return VLC_SUCCESS; -} - -/***************************************************************************** - * Video Filter2 stuff - *****************************************************************************/ -static int ParseVideoFilter2Chain( vout_thread_t *p_vout, char *psz_vfilters ) -{ - int i; - for( i = 0; i < p_vout->i_vfilters_cfg; i++ ) + /* */ + if( psz_filter ) { - struct config_chain_t *p_cfg = - p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg]; - config_ChainDestroy( p_cfg ); - if( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] ) - { - free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] ); - p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL; - } + char *psz_tmp = psz_filter; + if( asprintf( &psz_filter, "%s:%s", psz_tmp, "deinterlace" ) < 0 ) + psz_filter = psz_tmp; + else + free( psz_tmp ); } - p_vout->i_vfilters_cfg = 0; - if( psz_vfilters && *psz_vfilters ) + else { - char *psz_parser = psz_vfilters; + psz_filter = strdup( "deinterlace" ); + } - while( psz_parser && *psz_parser ) - { - psz_parser = config_ChainCreate( - &p_vout->psz_vfilters[p_vout->i_vfilters_cfg], - &p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg], - psz_parser ); - msg_Dbg( p_vout, "adding vfilter: %s", - p_vout->psz_vfilters[p_vout->i_vfilters_cfg] ); - p_vout->i_vfilters_cfg++; - if( psz_parser && *psz_parser ) - { - if( p_vout->i_vfilters_cfg == MAX_VFILTERS ) - { - msg_Warn( p_vout, - "maximum number of video filters reached. \"%s\" discarded", - psz_parser ); - break; - } - } - } + if( psz_filter ) + { + var_SetString( p_vout, psz_variable, psz_filter ); + free( psz_filter ); } - return VLC_SUCCESS; } -static int VideoFilter2Callback( 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; - (void)psz_cmd; (void)oldval; (void)p_data; - vlc_mutex_lock( &p_vout->vfilter_lock ); - ParseVideoFilter2Chain( p_vout, newval.psz_string ); - p_vout->b_vfilter_change = VLC_TRUE; - vlc_mutex_unlock( &p_vout->vfilter_lock ); + /* */ + const deinterlace_mode_t *p_mode; + for( p_mode = &p_deinterlace_mode[0]; p_mode->psz_mode; p_mode++ ) + { + if( !strcmp( p_mode->psz_mode, + newval.psz_string ? newval.psz_string : "" ) ) + break; + } + if( !p_mode->psz_mode ) + { + msg_Err( p_this, "Invalid value (%s) ignored", newval.psz_string ); + return VLC_EGENERIC; + } - return VLC_SUCCESS; -} + /* 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 ); -static void RemoveVideoFilters2( vout_thread_t *p_vout ) -{ - int i; - for( i = 0; i < p_vout->i_vfilters; i++ ) + var_Create( p_input, "deinterlace-mode", VLC_VAR_STRING ); + var_SetString( p_input, "deinterlace-mode", p_mode->psz_mode ); + + var_Create( p_input, "sout-deinterlace-mode", VLC_VAR_STRING ); + var_SetString( p_input, "sout-deinterlace-mode", p_mode->psz_mode ); + + vlc_object_release( p_input ); + } + + char *psz_old; + + if( p_mode->b_vout_filter ) { - vlc_object_detach( p_vout->pp_vfilters[i] ); - if( p_vout->pp_vfilters[i]->p_module ) - { - module_Unneed( p_vout->pp_vfilters[i], - p_vout->pp_vfilters[i]->p_module ); - } + 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 ); + } - free( p_vout->pp_vfilters[i]->p_owner ); - vlc_object_destroy( p_vout->pp_vfilters[i] ); + /* */ + 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" ); } - p_vout->i_vfilters = 0; + free( psz_old ); + + (void)psz_cmd; (void) oldval; (void) p_data; + return VLC_SUCCESS; } -static void DisplayTitleOnOSD( vout_thread_t *p_vout ) +static void DeinterlaceEnable( vout_thread_t *p_vout ) { - input_thread_t *p_input; - mtime_t i_now, i_stop; + vlc_value_t val, text; - p_input = (input_thread_t *)vlc_object_find( p_vout, - VLC_OBJECT_INPUT, FIND_ANYWHERE ); - if( p_input ) + if( !p_vout->p->b_first_vout ) + return; + + msg_Dbg( p_vout, "Deinterlacing available" ); + + /* 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 ); + + 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); - if( !EMPTY_STR(input_item_GetNowPlaying(input_GetItem(p_input))) ) - { - vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, - input_item_GetNowPlaying(input_GetItem(p_input)), 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(input_item_GetArtist(input_GetItem(p_input))) ) - { - char *psz_string = NULL; + 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 ); - psz_string = malloc( strlen(input_GetItem(p_input)->psz_name) ) + - strlen( input_item_GetArtist(input_GetItem(p_input)) ); - if( psz_string ) - { - sprintf( psz_string, "%s - %s", - input_GetItem(p_input)->psz_name, - input_item_GetArtist(input_GetItem(p_input)) ); - - 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, - input_GetItem(p_input)->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 ); + /* */ + 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 ? psz_mode : "" ); + free( psz_mode ); } +