X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fvideo_output%2Fvideo_output.c;h=d827b41db8816593d6fbed133a6c463cdfc116fd;hb=4b9c63a36eded246ad9ef1ea0de0cf7b38d29ad9;hp=75c72643181e3fe601e9c5751e0cebab9e10f0b1;hpb=b0e3202d1dc599580987c8f7abfa4b5f5fa2f5b2;p=vlc diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c index 75c7264318..d827b41db8 100644 --- a/src/video_output/video_output.c +++ b/src/video_output/video_output.c @@ -43,40 +43,19 @@ #include #include -#include +#include #include -#include -#include "vout_pictures.h" #include "vout_internal.h" +#include "interlacing.h" +#include "postprocessing.h" +#include "display.h" /***************************************************************************** * Local prototypes *****************************************************************************/ -static void *Thread(void *); -static void vout_Destructor(vlc_object_t *); - -/* Object variables callbacks */ -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 *, bool is_interlaced ); -static void DeinterlaceNeeded( vout_thread_t *, bool ); - -/* Display media title in OSD */ -static void DisplayTitleOnOSD( vout_thread_t *p_vout ); - -/* */ -static void PrintVideoFormat(vout_thread_t *, const char *, const video_format_t *); +static void *Thread(void *); +static void VoutDestructor(vlc_object_t *); /* Maximum delay between 2 displayed pictures. * XXX it is needed for now but should be removed in the long term. @@ -91,496 +70,238 @@ static void PrintVideoFormat(vout_thread_t *, const char *, const video_format_t /* Better be in advance when awakening than late... */ #define VOUT_MWAIT_TOLERANCE (INT64_C(1000)) -/***************************************************************************** - * Video Filter2 functions - *****************************************************************************/ -static picture_t *video_new_buffer_filter( filter_t *p_filter ) -{ - vout_thread_t *p_vout = (vout_thread_t*)p_filter->p_owner; - return picture_pool_Get(p_vout->p->private_pool); -} - -static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic ) +/* */ +static int VoutValidateFormat(video_format_t *dst, + const video_format_t *src) { - VLC_UNUSED(p_filter); - picture_Release(p_pic); -} + if (src->i_width <= 0 || src->i_height <= 0) + return VLC_EGENERIC; + if (src->i_sar_num <= 0 || src->i_sar_den <= 0) + return VLC_EGENERIC; -static int video_filter_buffer_allocation_init( filter_t *p_filter, void *p_data ) -{ - p_filter->pf_video_buffer_new = video_new_buffer_filter; - p_filter->pf_video_buffer_del = video_del_buffer_filter; - p_filter->p_owner = p_data; /* p_vout */ + /* */ + video_format_Copy(dst, src); + dst->i_chroma = vlc_fourcc_GetCodec(VIDEO_ES, src->i_chroma); + vlc_ureduce( &dst->i_sar_num, &dst->i_sar_den, + src->i_sar_num, src->i_sar_den, 50000 ); + if (dst->i_sar_num <= 0 || dst->i_sar_den <= 0) { + dst->i_sar_num = 1; + dst->i_sar_den = 1; + } + video_format_FixRgb(dst); return VLC_SUCCESS; } -#undef vout_Request -/***************************************************************************** - * vout_Request: find a video output thread, create one, or destroy one. - ***************************************************************************** - * This function looks for a video output thread matching the current - * properties. If not found, it spawns a new one. - *****************************************************************************/ -vout_thread_t *vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout, - video_format_t *p_fmt ) +static vout_thread_t *VoutCreate(vlc_object_t *object, + const vout_configuration_t *cfg) { - if( !p_fmt ) - { - /* Video output is no longer used. - * TODO: support for reusing video outputs with proper _thread-safe_ - * reference handling. */ - if( p_vout ) - vout_CloseAndRelease( p_vout ); + video_format_t original; + if (VoutValidateFormat(&original, cfg->fmt)) return NULL; - } - - /* If a video output was provided, lock it, otherwise look for one. */ - if( p_vout ) - { - vlc_object_hold( p_vout ); - } - - /* TODO: find a suitable unused video output */ - - /* If we now have a video output, check it has the right properties */ - if( p_vout ) - { - vlc_mutex_lock( &p_vout->p->change_lock ); - - /* We don't directly check for the "vout-filter" variable for obvious - * performance reasons. */ - if( p_vout->p->b_filter_change ) - { - 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->p->psz_filter_chain && !*p_vout->p->psz_filter_chain ) - { - free( p_vout->p->psz_filter_chain ); - p_vout->p->psz_filter_chain = NULL; - } - - if( !psz_filter_chain && !p_vout->p->psz_filter_chain ) - { - p_vout->p->b_filter_change = false; - } - - free( psz_filter_chain ); - } - -#warning "FIXME: Check RGB masks in vout_Request" - /* FIXME: check RGB masks */ - 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->p->change_lock ); - - /* We are not interested in this format, close this vout */ - vout_CloseAndRelease( p_vout ); - vlc_object_release( p_vout ); - p_vout = NULL; - } - else - { - /* This video output is cool! Hijack it. */ - /* 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; - 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 */ - 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; - } -#endif - - if( i_sar_num > 0 && i_sar_den > 0 && - ( i_sar_num != p_vout->fmt_render.i_sar_num || - i_sar_den != p_vout->fmt_render.i_sar_den ) ) - { - p_vout->fmt_in.i_sar_num = i_sar_num; - p_vout->fmt_in.i_sar_den = i_sar_den; - - p_vout->fmt_render.i_sar_num = i_sar_num; - p_vout->fmt_render.i_sar_den = i_sar_den; - p_vout->p->i_changes |= VOUT_ASPECT_CHANGE; - } - vlc_mutex_unlock( &p_vout->p->change_lock ); - - vlc_object_release( p_vout ); - } - - if( p_vout ) - { - msg_Dbg( p_this, "reusing provided vout" ); - spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false ); - vlc_object_detach( p_vout ); - - vlc_object_attach( p_vout, p_this ); - spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true ); - } - } - - if( !p_vout ) - { - msg_Dbg( p_this, "no usable vout present, spawning one" ); - - p_vout = vout_Create( p_this, p_fmt ); + /* Allocate descriptor */ + vout_thread_t *vout = vlc_custom_create(object, + sizeof(*vout) + sizeof(*vout->p), + VLC_OBJECT_VOUT, "video output"); + if (!vout) { + video_format_Clean(&original); + return NULL; } - return p_vout; -} - -/***************************************************************************** - * vout_Create: creates a new video output thread - ***************************************************************************** - * This function creates a new video output thread, and returns a pointer - * to its description. On error, it returns NULL. - *****************************************************************************/ -vout_thread_t * (vout_Create)( vlc_object_t *p_parent, video_format_t *p_fmt ) -{ - vout_thread_t *p_vout; /* thread descriptor */ - vlc_value_t text; - + /* */ + vout->p = (vout_thread_sys_t*)&vout[1]; - config_chain_t *p_cfg; - char *psz_parser; - char *psz_name; + vout->p->original = original; - if( p_fmt->i_width <= 0 || p_fmt->i_height <= 0 ) - return NULL; - const vlc_fourcc_t i_chroma = vlc_fourcc_GetCodec( VIDEO_ES, p_fmt->i_chroma ); + vout_control_Init(&vout->p->control); + vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_INIT); - 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; + vout_statistic_Init(&vout->p->statistic); + vout->p->i_par_num = + vout->p->i_par_den = 1; - /* Allocate descriptor */ - 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 ) - { - vlc_object_release( p_vout ); - return NULL; - } - - /* */ - p_vout->fmt_render = *p_fmt; /* FIXME palette */ - p_vout->fmt_in = *p_fmt; /* FIXME palette */ - - p_vout->fmt_render.i_chroma = - p_vout->fmt_in.i_chroma = i_chroma; - video_format_FixRgb( &p_vout->fmt_render ); - video_format_FixRgb( &p_vout->fmt_in ); - - /* Initialize misc stuff */ - p_vout->p->i_changes = 0; - p_vout->p->b_fullscreen = 0; - vout_chrono_Init( &p_vout->p->render, 5, 10000 ); /* Arbitrary initial time */ - vout_statistic_Init( &p_vout->p->statistic ); - p_vout->p->b_filter_change = 0; - p_vout->p->i_par_num = - p_vout->p->i_par_den = 1; - p_vout->p->b_picture_empty = false; - p_vout->p->displayed.date = VLC_TS_INVALID; - p_vout->p->displayed.decoded = NULL; - p_vout->p->displayed.timestamp = VLC_TS_INVALID; - p_vout->p->displayed.qtype = QTYPE_NONE; - p_vout->p->displayed.is_interlaced = false; - - p_vout->p->step.is_requested = false; - p_vout->p->step.last = VLC_TS_INVALID; - p_vout->p->step.timestamp = VLC_TS_INVALID; - - p_vout->p->pause.is_on = false; - p_vout->p->pause.date = VLC_TS_INVALID; - - p_vout->p->decoder_fifo = picture_fifo_New(); - p_vout->p->decoder_pool = NULL; - - vlc_mouse_Init( &p_vout->p->mouse ); - - vout_snapshot_Init( &p_vout->p->snapshot ); + vout_snapshot_Init(&vout->p->snapshot); /* Initialize locks */ - vlc_mutex_init( &p_vout->p->picture_lock ); - vlc_cond_init( &p_vout->p->picture_wait ); - vlc_mutex_init( &p_vout->p->change_lock ); - vlc_mutex_init( &p_vout->p->vfilter_lock ); - - /* Mouse coordinates */ - var_Create( p_vout, "mouse-button-down", VLC_VAR_INTEGER ); - var_Create( p_vout, "mouse-moved", VLC_VAR_COORDS ); - var_Create( p_vout, "mouse-clicked", VLC_VAR_COORDS ); - /* Mouse object (area of interest in a video filter) */ - var_Create( p_vout, "mouse-object", VLC_VAR_BOOL ); + vlc_mutex_init(&vout->p->picture_lock); + vlc_mutex_init(&vout->p->vfilter_lock); /* Attach the new object now so we can use var inheritance below */ - vlc_object_attach( p_vout, p_parent ); + vlc_object_attach(vout, object); /* Initialize subpicture unit */ - p_vout->p->p_spu = spu_Create( p_vout ); - - /* */ - spu_Init( p_vout->p->p_spu ); - - spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), true ); - - p_vout->p->is_late_dropped = var_InheritBool( p_vout, "drop-late-frames" ); + vout->p->p_spu = spu_Create(vout); /* Take care of some "interface/control" related initialisations */ - vout_IntfInit( p_vout ); - - /* Look for the default filter configuration */ - p_vout->p->psz_filter_chain = - var_CreateGetStringCommand( p_vout, "vout-filter" ); - - /* Apply video filter2 objects on the first vout */ - p_vout->p->psz_vf2 = - var_CreateGetStringCommand( p_vout, "video-filter" ); - - var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL ); - 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->p->psz_filter_chain || !*p_vout->p->psz_filter_chain ) - { - psz_parser = NULL; - } - else - { - psz_parser = strdup( p_vout->p->psz_filter_chain ); - p_vout->p->title.show = false; + vout_IntfInit(vout); + + /* Get splitter name if present */ + char *splitter_name = var_GetNonEmptyString(vout, "vout-filter"); + if (splitter_name) { + if (asprintf(&vout->p->splitter_name, "%s,none", splitter_name) < 0) + vout->p->splitter_name = NULL; + free(splitter_name); + } else { + vout->p->splitter_name = NULL; } - /* Create the vout thread */ - char *psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser ); - free( psz_parser ); - free( psz_tmp ); - p_vout->p->p_cfg = p_cfg; - - /* Create a few object variables for interface interaction */ - 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 ); - /* */ - DeinterlaceEnable( p_vout, p_vout->p->displayed.is_interlaced ); - - if( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain ) - { - char *psz_tmp; - if( asprintf( &psz_tmp, "%s,none", psz_name ) < 0 ) - psz_tmp = strdup( "" ); - free( psz_name ); - psz_name = psz_tmp; - } - p_vout->p->psz_module_name = psz_name; + vout_InitInterlacingSupport(vout, vout->p->displayed.is_interlaced); /* */ - vlc_object_set_destructor( p_vout, vout_Destructor ); + vlc_object_set_destructor(vout, VoutDestructor); /* */ - vlc_cond_init( &p_vout->p->change_wait ); - if( vlc_clone( &p_vout->p->thread, Thread, p_vout, - VLC_THREAD_PRIORITY_OUTPUT ) ) - { - spu_Attach( p_vout->p->p_spu, VLC_OBJECT(p_vout), false ); - spu_Destroy( p_vout->p->p_spu ); - p_vout->p->p_spu = NULL; - vlc_object_release( p_vout ); + if (vlc_clone(&vout->p->thread, Thread, vout, + VLC_THREAD_PRIORITY_OUTPUT)) { + vlc_object_release(vout); return NULL; } - vlc_mutex_lock( &p_vout->p->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->p->change_lock ); + vout_control_WaitEmpty(&vout->p->control); + + if (vout->p->dead) { + msg_Err(vout, "video output creation failed"); + vout_CloseAndRelease(vout); + return NULL; } - vlc_mutex_unlock( &p_vout->p->change_lock ); - if( p_vout->p->b_error ) - { - msg_Err( p_vout, "video output creation failed" ); - vout_CloseAndRelease( p_vout ); + vout->p->input = cfg->input; + if (vout->p->input) + spu_Attach(vout->p->p_spu, vout->p->input, true); + + return vout; +} + +vout_thread_t *(vout_Request)(vlc_object_t *object, + const vout_configuration_t *cfg) +{ + vout_thread_t *vout = cfg->vout; + if (!cfg->fmt) { + if (vout) + vout_CloseAndRelease(vout); return NULL; } - return p_vout; + /* If a vout is provided, try reusing it */ + if (vout) { + vlc_object_detach(vout); + vlc_object_attach(vout, object); + + if (vout->p->input != cfg->input) { + if (vout->p->input) + spu_Attach(vout->p->p_spu, vout->p->input, false); + vout->p->input = cfg->input; + if (vout->p->input) + spu_Attach(vout->p->p_spu, vout->p->input, true); + } + + vout_control_cmd_t cmd; + vout_control_cmd_Init(&cmd, VOUT_CONTROL_REINIT); + cmd.u.cfg = cfg; + + vout_control_Push(&vout->p->control, &cmd); + vout_control_WaitEmpty(&vout->p->control); + if (!vout->p->dead) { + msg_Dbg(object, "reusing provided vout"); + return vout; + } + vout_CloseAndRelease(vout); + + msg_Warn(object, "cannot reuse provided vout"); + } + return VoutCreate(object, cfg); } /***************************************************************************** - * vout_Close: Close a vout created by vout_Create. + * vout_Close: Close a vout created by VoutCreate. ***************************************************************************** - * 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 + * You HAVE to call it on vout created by VoutCreate before vlc_object_release. + * You should NEVER call it on vout not obtained through VoutCreate * (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 ) +void vout_Close(vout_thread_t *vout) { - assert( p_vout ); + assert(vout); - vlc_mutex_lock( &p_vout->p->change_lock ); - p_vout->p->b_done = true; - vlc_cond_signal( &p_vout->p->change_wait ); - vlc_mutex_unlock( &p_vout->p->change_lock ); + if (vout->p->input) + spu_Attach(vout->p->p_spu, vout->p->input, false); + vlc_object_detach(vout->p->p_spu); - vout_snapshot_End( &p_vout->p->snapshot ); + vout_snapshot_End(&vout->p->snapshot); - vlc_join( p_vout->p->thread, NULL ); + vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_CLEAN); + vlc_join(vout->p->thread, NULL); } /* */ -static void vout_Destructor( vlc_object_t * p_this ) +static void VoutDestructor(vlc_object_t *object) { - vout_thread_t *p_vout = (vout_thread_t *)p_this; + vout_thread_t *vout = (vout_thread_t *)object; /* Make sure the vout was stopped first */ - //assert( !p_vout->p_module ); + //assert(!vout->p_module); - free( p_vout->p->psz_module_name ); + free(vout->p->splitter_name); /* */ - if( p_vout->p->p_spu ) - spu_Destroy( p_vout->p->p_spu ); - - vout_chrono_Clean( &p_vout->p->render ); - - if( p_vout->p->decoder_fifo ) - picture_fifo_Delete( p_vout->p->decoder_fifo ); - assert( !p_vout->p->decoder_pool ); + spu_Destroy(vout->p->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->p->picture_lock ); - vlc_mutex_destroy( &p_vout->p->change_lock ); - vlc_mutex_destroy( &p_vout->p->vfilter_lock ); - - /* */ - vout_statistic_Clean( &p_vout->p->statistic ); + vlc_mutex_destroy(&vout->p->picture_lock); + vlc_mutex_destroy(&vout->p->vfilter_lock); + vout_control_Clean(&vout->p->control); /* */ - vout_snapshot_Clean( &p_vout->p->snapshot ); + vout_statistic_Clean(&vout->p->statistic); /* */ - free( p_vout->p->psz_filter_chain ); - free( p_vout->p->title.value ); - - config_ChainDestroy( p_vout->p->p_cfg ); - - free( p_vout->p ); + vout_snapshot_Clean(&vout->p->snapshot); + video_format_Clean(&vout->p->original); } /* */ -void vout_ChangePause( vout_thread_t *p_vout, bool b_paused, mtime_t i_date ) +void vout_ChangePause(vout_thread_t *vout, bool is_paused, mtime_t date) { - vlc_mutex_lock( &p_vout->p->change_lock ); - - assert( !p_vout->p->pause.is_on || !b_paused ); - - vlc_mutex_lock( &p_vout->p->picture_lock ); - - if( p_vout->p->pause.is_on ) - { - const mtime_t i_duration = i_date - p_vout->p->pause.date; - - if (p_vout->p->step.timestamp > VLC_TS_INVALID) - p_vout->p->step.timestamp += i_duration; - if (!b_paused) - p_vout->p->step.last = p_vout->p->step.timestamp; - picture_fifo_OffsetDate( p_vout->p->decoder_fifo, i_duration ); - if (p_vout->p->displayed.decoded) - p_vout->p->displayed.decoded->date += i_duration; - - vlc_cond_signal( &p_vout->p->picture_wait ); - vlc_mutex_unlock( &p_vout->p->picture_lock ); - - spu_OffsetSubtitleDate( p_vout->p->p_spu, i_duration ); - } - else - { - if (b_paused) - p_vout->p->step.last = p_vout->p->step.timestamp; - vlc_mutex_unlock( &p_vout->p->picture_lock ); - } - p_vout->p->pause.is_on = b_paused; - p_vout->p->pause.date = i_date; + vout_control_cmd_t cmd; + vout_control_cmd_Init(&cmd, VOUT_CONTROL_PAUSE); + cmd.u.pause.is_on = is_paused; + cmd.u.pause.date = date; + vout_control_Push(&vout->p->control, &cmd); - vlc_mutex_unlock( &p_vout->p->change_lock ); + vout_control_WaitEmpty(&vout->p->control); } -void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost ) +void vout_GetResetStatistic(vout_thread_t *vout, int *displayed, int *lost) { - vout_statistic_GetReset( &p_vout->p->statistic, - pi_displayed, pi_lost ); + vout_statistic_GetReset( &vout->p->statistic, displayed, lost ); } -static void Flush(vout_thread_t *vout, mtime_t date, bool reset, bool below) +void vout_Flush(vout_thread_t *vout, mtime_t date) { - vlc_assert_locked(&vout->p->picture_lock); - vout->p->step.timestamp = VLC_TS_INVALID; - vout->p->step.last = VLC_TS_INVALID; - - picture_t *last = vout->p->displayed.decoded; - if (last) { - if (reset) { - picture_Release(last); - vout->p->displayed.decoded = NULL; - } else if (( below && last->date <= date) || - (!below && last->date >= date)) { - vout->p->step.is_requested = true; - } - } - picture_fifo_Flush( vout->p->decoder_fifo, date, below ); + vout_control_PushTime(&vout->p->control, VOUT_CONTROL_FLUSH, date); + vout_control_WaitEmpty(&vout->p->control); } -void vout_Flush(vout_thread_t *vout, mtime_t date) +void vout_Reset(vout_thread_t *vout) { - vlc_mutex_lock(&vout->p->picture_lock); - - Flush(vout, date, false, false); - - vlc_cond_signal(&vout->p->picture_wait); - vlc_mutex_unlock(&vout->p->picture_lock); + vout_control_PushVoid(&vout->p->control, VOUT_CONTROL_RESET); + vout_control_WaitEmpty(&vout->p->control); } -void vout_Reset(vout_thread_t *vout) +bool vout_IsEmpty(vout_thread_t *vout) { vlc_mutex_lock(&vout->p->picture_lock); - Flush(vout, INT64_MAX, true, true); - if (vout->p->decoder_pool) - picture_pool_NonEmpty(vout->p->decoder_pool, true); - vout->p->pause.is_on = false; - vout->p->pause.date = mdate(); + picture_t *picture = picture_fifo_Peek(vout->p->decoder_fifo); + if (picture) + picture_Release(picture); - vlc_cond_signal( &vout->p->picture_wait ); vlc_mutex_unlock(&vout->p->picture_lock); + + return !picture; } void vout_FixLeaks( vout_thread_t *vout ) @@ -607,107 +328,224 @@ void vout_FixLeaks( vout_thread_t *vout ) /* */ picture_pool_NonEmpty(vout->p->decoder_pool, false); - vlc_cond_signal(&vout->p->picture_wait); vlc_mutex_unlock(&vout->p->picture_lock); } void vout_NextPicture(vout_thread_t *vout, mtime_t *duration) { - vlc_mutex_lock(&vout->p->picture_lock); + vout_control_cmd_t cmd; + vout_control_cmd_Init(&cmd, VOUT_CONTROL_STEP); + cmd.u.time_ptr = duration; - vout->p->b_picture_empty = false; - vout->p->step.is_requested = true; - - /* FIXME I highly doubt that it can works with only one cond_t FIXME */ - vlc_cond_signal(&vout->p->picture_wait); - - while (vout->p->step.is_requested && !vout->p->b_picture_empty) - vlc_cond_wait(&vout->p->picture_wait, &vout->p->picture_lock); - - if (vout->p->step.last > VLC_TS_INVALID && - vout->p->step.timestamp > vout->p->step.last) { - *duration = vout->p->step.timestamp - vout->p->step.last; - vout->p->step.last = vout->p->step.timestamp; - } else { - *duration = 0; - } - - /* TODO advance subpicture by the duration ... */ - vlc_mutex_unlock(&vout->p->picture_lock); + vout_control_Push(&vout->p->control, &cmd); + vout_control_WaitEmpty(&vout->p->control); } -void vout_DisplayTitle( vout_thread_t *p_vout, const char *psz_title ) +void vout_DisplayTitle(vout_thread_t *vout, const char *title) { - assert( psz_title ); - - if( !var_InheritBool( p_vout, "osd" ) ) - return; + assert(title); + vout_control_PushString(&vout->p->control, VOUT_CONTROL_OSD_TITLE, title); +} - vlc_mutex_lock( &p_vout->p->change_lock ); - free( p_vout->p->title.value ); - p_vout->p->title.value = strdup( psz_title ); - vlc_mutex_unlock( &p_vout->p->change_lock ); +void vout_PutSubpicture( vout_thread_t *vout, subpicture_t *subpic ) +{ + spu_DisplaySubpicture(vout->p->p_spu, subpic); +} +int vout_RegisterSubpictureChannel( vout_thread_t *vout ) +{ + return spu_RegisterChannel(vout->p->p_spu); +} +void vout_FlushSubpictureChannel( vout_thread_t *vout, int channel ) +{ + spu_ClearChannel(vout->p->p_spu, channel); } -spu_t *vout_GetSpu( vout_thread_t *p_vout ) +/* vout_Control* are usable by anyone at anytime */ +void vout_ControlChangeFullscreen(vout_thread_t *vout, bool fullscreen) +{ + vout_control_PushBool(&vout->p->control, VOUT_CONTROL_FULLSCREEN, + fullscreen); +} +void vout_ControlChangeOnTop(vout_thread_t *vout, bool is_on_top) +{ + vout_control_PushBool(&vout->p->control, VOUT_CONTROL_ON_TOP, + is_on_top); +} +void vout_ControlChangeDisplayFilled(vout_thread_t *vout, bool is_filled) +{ + vout_control_PushBool(&vout->p->control, VOUT_CONTROL_DISPLAY_FILLED, + is_filled); +} +void vout_ControlChangeZoom(vout_thread_t *vout, int num, int den) { - return p_vout->p->p_spu; + vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ZOOM, + num, den); +} +void vout_ControlChangeSampleAspectRatio(vout_thread_t *vout, + unsigned num, unsigned den) +{ + vout_control_PushPair(&vout->p->control, VOUT_CONTROL_ASPECT_RATIO, + num, den); +} +void vout_ControlChangeCropRatio(vout_thread_t *vout, + unsigned num, unsigned den) +{ + vout_control_PushPair(&vout->p->control, VOUT_CONTROL_CROP_RATIO, + num, den); +} +void vout_ControlChangeCropWindow(vout_thread_t *vout, + int x, int y, int width, int height) +{ + vout_control_cmd_t cmd; + vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_WINDOW); + cmd.u.window.x = x; + cmd.u.window.y = y; + cmd.u.window.width = width; + cmd.u.window.height = height; + + vout_control_Push(&vout->p->control, &cmd); +} +void vout_ControlChangeCropBorder(vout_thread_t *vout, + int left, int top, int right, int bottom) +{ + vout_control_cmd_t cmd; + vout_control_cmd_Init(&cmd, VOUT_CONTROL_CROP_BORDER); + cmd.u.border.left = left; + cmd.u.border.top = top; + cmd.u.border.right = right; + cmd.u.border.bottom = bottom; + + vout_control_Push(&vout->p->control, &cmd); +} +void vout_ControlChangeFilters(vout_thread_t *vout, const char *filters) +{ + vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_FILTERS, + filters); +} +void vout_ControlChangeSubFilters(vout_thread_t *vout, const char *filters) +{ + vout_control_PushString(&vout->p->control, VOUT_CONTROL_CHANGE_SUB_FILTERS, + filters); } -/***************************************************************************** - * InitThread: initialize video output thread - ***************************************************************************** - * This function is called from Thread 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 ThreadInit(vout_thread_t *vout) +/* */ +static void VoutGetDisplayCfg(vout_thread_t *vout, vout_display_cfg_t *cfg, const char *title) { - /* Initialize output method, it allocates direct buffers for us */ - if (vout_InitWrapper(vout)) - return VLC_EGENERIC; - assert(vout->p->decoder_pool); + /* Load configuration */ + cfg->is_fullscreen = var_CreateGetBool(vout, "fullscreen"); + cfg->display.title = title; + const int display_width = var_CreateGetInteger(vout, "width"); + const int display_height = var_CreateGetInteger(vout, "height"); + cfg->display.width = display_width > 0 ? display_width : 0; + cfg->display.height = display_height > 0 ? display_height : 0; + cfg->is_display_filled = var_CreateGetBool(vout, "autoscale"); + cfg->display.sar.num = 1; /* TODO monitor AR */ + cfg->display.sar.den = 1; + unsigned zoom_den = 1000; + unsigned zoom_num = zoom_den * var_CreateGetFloat(vout, "scale"); + vlc_ureduce(&zoom_num, &zoom_den, zoom_num, zoom_den, 0); + cfg->zoom.num = zoom_num; + cfg->zoom.den = zoom_den; + cfg->align.vertical = VOUT_DISPLAY_ALIGN_CENTER; + cfg->align.horizontal = VOUT_DISPLAY_ALIGN_CENTER; + const int align_mask = var_CreateGetInteger(vout, "align"); + if (align_mask & 0x1) + cfg->align.horizontal = VOUT_DISPLAY_ALIGN_LEFT; + else if (align_mask & 0x2) + cfg->align.horizontal = VOUT_DISPLAY_ALIGN_RIGHT; + if (align_mask & 0x4) + cfg->align.horizontal = VOUT_DISPLAY_ALIGN_TOP; + else if (align_mask & 0x8) + cfg->align.horizontal = VOUT_DISPLAY_ALIGN_BOTTOM; +} - vout->p->displayed.decoded = NULL; +vout_window_t * vout_NewDisplayWindow(vout_thread_t *vout, vout_display_t *vd, + const vout_window_cfg_t *cfg) +{ + VLC_UNUSED(vd); + vout_window_cfg_t cfg_override = *cfg; + + if (!var_InheritBool( vout, "embedded-video")) + cfg_override.is_standalone = true; + + if (vout->p->window.is_unused && vout->p->window.object) { + assert(!vout->p->splitter_name); + if (!cfg_override.is_standalone == !vout->p->window.cfg.is_standalone && + cfg_override.type == vout->p->window.cfg.type) { + /* Reuse the stored window */ + msg_Dbg(vout, "Reusing previous vout window"); + vout_window_t *window = vout->p->window.object; + if (cfg_override.width != vout->p->window.cfg.width || + cfg_override.height != vout->p->window.cfg.height) + vout_window_SetSize(window, + cfg_override.width, cfg_override.height); + vout->p->window.is_unused = false; + vout->p->window.cfg = cfg_override; + return window; + } - /* print some usefull debug info about different vout formats - */ - PrintVideoFormat(vout, "pic render", &vout->fmt_render); - PrintVideoFormat(vout, "pic in", &vout->fmt_in); - PrintVideoFormat(vout, "pic out", &vout->fmt_out); + vout_window_Delete(vout->p->window.object); + vout->p->window.is_unused = true; + vout->p->window.object = NULL; + } - assert(vout->fmt_out.i_width == vout->fmt_render.i_width && - vout->fmt_out.i_height == vout->fmt_render.i_height && - vout->fmt_out.i_chroma == vout->fmt_render.i_chroma); - return VLC_SUCCESS; + vout_window_t *window = vout_window_New(VLC_OBJECT(vout), NULL, + &cfg_override); + if (!window) + return NULL; + if (!vout->p->splitter_name) { + vout->p->window.is_unused = false; + vout->p->window.cfg = cfg_override; + vout->p->window.object = window; + } + return window; } -/***************************************************************************** - * CleanThread: clean up after InitThread - ***************************************************************************** - * 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 ThreadClean(vout_thread_t *vout) +void vout_DeleteDisplayWindow(vout_thread_t *vout, vout_display_t *vd, + vout_window_t *window) { - /* Destroy translation tables */ - if (!vout->p->b_error) { - picture_fifo_Flush(vout->p->decoder_fifo, INT64_MAX, false); - vout_EndWrapper(vout); + VLC_UNUSED(vd); + if (!vout->p->window.is_unused && vout->p->window.object == window) { + vout->p->window.is_unused = true; + } else if (vout->p->window.is_unused && vout->p->window.object && !window) { + vout_window_Delete(vout->p->window.object); + vout->p->window.is_unused = true; + vout->p->window.object = NULL; + } else if (window) { + vout_window_Delete(window); } } +/* */ +static picture_t *VoutVideoFilterNewPicture(filter_t *filter) +{ + vout_thread_t *vout = (vout_thread_t*)filter->p_owner; + return picture_pool_Get(vout->p->private_pool); +} +static void VoutVideoFilterDelPicture(filter_t *filter, picture_t *picture) +{ + VLC_UNUSED(filter); + picture_Release(picture); +} +static int VoutVideoFilterAllocationSetup(filter_t *filter, void *data) +{ + filter->pf_video_buffer_new = VoutVideoFilterNewPicture; + filter->pf_video_buffer_del = VoutVideoFilterDelPicture; + filter->p_owner = data; /* vout */ + return VLC_SUCCESS; +} + +/* */ static int ThreadDisplayPicture(vout_thread_t *vout, bool now, mtime_t *deadline) { + vout_display_t *vd = vout->p->display.vd; int displayed_count = 0; int lost_count = 0; for (;;) { const mtime_t date = mdate(); const bool is_paused = vout->p->pause.is_on; - bool redisplay = is_paused && !now; + bool redisplay = is_paused && !now && vout->p->displayed.decoded; bool is_forced; /* FIXME/XXX we must redisplay the last decoded picture (because @@ -786,7 +624,7 @@ static int ThreadDisplayPicture(vout_thread_t *vout, picture_t *filtered = NULL; if (decoded) { vlc_mutex_lock(&vout->p->vfilter_lock); - filtered = filter_chain_VideoFilter(vout->p->p_vf2_chain, decoded); + filtered = filter_chain_VideoFilter(vout->p->vfilter_chain, decoded); //assert(filtered == decoded); // TODO implement vlc_mutex_unlock(&vout->p->vfilter_lock); if (!filtered) @@ -818,7 +656,7 @@ static int ThreadDisplayPicture(vout_thread_t *vout, (vout->p->decoder_pool != vout->p->display_pool || subpic)) { picture_t *render; if (vout->p->is_decoder_pool_slow) - render = picture_NewFromFormat(&vout->fmt_out); + render = picture_NewFromFormat(&vd->source); else if (vout->p->decoder_pool != vout->p->display_pool) render = picture_pool_Get(vout->p->display_pool); else @@ -828,8 +666,8 @@ static int ThreadDisplayPicture(vout_thread_t *vout, picture_Copy(render, filtered); spu_RenderSubpictures(vout->p->p_spu, - render, &vout->fmt_out, - subpic, &vout->fmt_in, spu_render_time); + render, &vd->source, + subpic, &vd->source, spu_render_time); } if (vout->p->is_decoder_pool_slow) { direct = picture_pool_Get(vout->p->display_pool); @@ -850,7 +688,7 @@ static int ThreadDisplayPicture(vout_thread_t *vout, * Take a snapshot if requested */ if (direct && do_snapshot) - vout_snapshot_Set(&vout->p->snapshot, &vout->fmt_out, direct); + vout_snapshot_Set(&vout->p->snapshot, &vd->source, direct); /* Render the direct buffer returned by vout_RenderPicture */ if (direct) { @@ -886,611 +724,433 @@ static int ThreadDisplayPicture(vout_thread_t *vout, return VLC_SUCCESS; } -/***************************************************************************** - * Thread: video output thread - ***************************************************************************** - * Video output thread. This function does only returns when the thread is - * terminated. It handles the pictures arriving in the video heap and the - * display device events. - *****************************************************************************/ -static void *Thread(void *object) +static void ThreadManage(vout_thread_t *vout, + mtime_t *deadline, + vout_interlacing_support_t *interlacing, + vout_postprocessing_support_t *postprocessing) { - vout_thread_t *vout = object; - bool has_wrapper; + vlc_mutex_lock(&vout->p->picture_lock); - /* - * Initialize thread - */ - has_wrapper = !vout_OpenWrapper(vout, vout->p->psz_module_name); + *deadline = VLC_TS_INVALID; + ThreadDisplayPicture(vout, false, deadline); - vlc_mutex_lock(&vout->p->change_lock); + const int picture_qtype = vout->p->displayed.qtype; + const bool picture_interlaced = vout->p->displayed.is_interlaced; - if (has_wrapper) - vout->p->b_error = ThreadInit(vout); - else - vout->p->b_error = true; + vlc_mutex_unlock(&vout->p->picture_lock); - /* signal the creation of the vout */ - vout->p->b_ready = true; - vlc_cond_signal(&vout->p->change_wait); + /* Post processing */ + vout_SetPostProcessingState(vout, postprocessing, picture_qtype); - if (vout->p->b_error) - goto exit_thread; + /* Deinterlacing */ + vout_SetInterlacingState(vout, interlacing, picture_interlaced); - /* */ - bool last_picture_interlaced = false; - int last_picture_qtype = QTYPE_NONE; - mtime_t last_picture_interlaced_date = mdate(); - - /* - * Main loop - it is not executed if an error occurred during - * initialization - */ - while (!vout->p->b_done && !vout->p->b_error) { - /* */ - if(vout->p->title.show && vout->p->title.value) - DisplayTitleOnOSD(vout); + vout_ManageWrapper(vout); +} - vlc_mutex_lock(&vout->p->picture_lock); +static void ThreadDisplayOsdTitle(vout_thread_t *vout, const char *string) +{ + if (!vout->p->title.show) + return; - mtime_t deadline = VLC_TS_INVALID; - bool has_displayed = !ThreadDisplayPicture(vout, vout->p->step.is_requested, &deadline); + vout_OSDText(vout, SPU_DEFAULT_CHANNEL, + vout->p->title.position, INT64_C(1000) * vout->p->title.timeout, + string); +} - if (has_displayed) { - vout->p->step.timestamp = vout->p->displayed.timestamp; - if (vout->p->step.last <= VLC_TS_INVALID) - vout->p->step.last = vout->p->step.timestamp; - } - if (vout->p->step.is_requested) { - if (!has_displayed && !vout->p->b_picture_empty) { - picture_t *peek = picture_fifo_Peek(vout->p->decoder_fifo); - if (peek) - picture_Release(peek); - if (!peek) { - vout->p->b_picture_empty = true; - vlc_cond_signal(&vout->p->picture_wait); - } - } - if (has_displayed) { - vout->p->step.is_requested = false; - vlc_cond_signal(&vout->p->picture_wait); - } - } +static void ThreadChangeFilters(vout_thread_t *vout, const char *filters) +{ + es_format_t fmt; + es_format_Init(&fmt, VIDEO_ES, vout->p->original.i_chroma); + fmt.video = vout->p->original; - const int picture_qtype = vout->p->displayed.qtype; - const bool picture_interlaced = vout->p->displayed.is_interlaced; + vlc_mutex_lock(&vout->p->vfilter_lock); - vlc_mutex_unlock(&vout->p->picture_lock); + filter_chain_Reset(vout->p->vfilter_chain, &fmt, &fmt); + if (filter_chain_AppendFromString(vout->p->vfilter_chain, + filters) < 0) + msg_Err(vout, "Video filter chain creation failed"); - if (vout_ManageWrapper(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 - vout->p->b_error = true; - break; - } + vlc_mutex_unlock(&vout->p->vfilter_lock); +} - /* Post processing */ - const int postproc_change = (picture_qtype != QTYPE_NONE) - (last_picture_qtype != QTYPE_NONE); - if (postproc_change == 1) - PostProcessEnable(vout); - else if (postproc_change == -1) - PostProcessDisable(vout); - if (postproc_change) - last_picture_qtype = picture_qtype; - - /* Deinterlacing - * Wait 30s before quiting interlacing mode */ - const int interlacing_change = (!!picture_interlaced) - (!!last_picture_interlaced); - if ((interlacing_change == 1) || - (interlacing_change == -1 && last_picture_interlaced_date + 30000000 < mdate())) { - DeinterlaceNeeded(vout, picture_interlaced); - last_picture_interlaced = picture_interlaced; - } - if (picture_interlaced) - last_picture_interlaced_date = mdate(); +static void ThreadChangeSubFilters(vout_thread_t *vout, const char *filters) +{ + spu_ChangeFilters(vout->p->p_spu, filters); +} - /* Check for "video filter2" changes */ - vlc_mutex_lock(&vout->p->vfilter_lock); - if (vout->p->psz_vf2) { - es_format_t fmt; +static void ThreadChangePause(vout_thread_t *vout, bool is_paused, mtime_t date) +{ + assert(!vout->p->pause.is_on || !is_paused); - es_format_Init(&fmt, VIDEO_ES, vout->fmt_render.i_chroma); - fmt.video = vout->fmt_render; - filter_chain_Reset(vout->p->p_vf2_chain, &fmt, &fmt); + if (vout->p->pause.is_on) { + const mtime_t duration = date - vout->p->pause.date; - if (filter_chain_AppendFromString(vout->p->p_vf2_chain, - vout->p->psz_vf2) < 0) - msg_Err(vout, "Video filter chain creation failed"); + if (vout->p->step.timestamp > VLC_TS_INVALID) + vout->p->step.timestamp += duration; + if (vout->p->step.last > VLC_TS_INVALID) + vout->p->step.last += duration; + picture_fifo_OffsetDate(vout->p->decoder_fifo, duration); + if (vout->p->displayed.decoded) + vout->p->displayed.decoded->date += duration; - free(vout->p->psz_vf2); - vout->p->psz_vf2 = NULL; + spu_OffsetSubtitleDate(vout->p->p_spu, duration); + } else { + vout->p->step.timestamp = VLC_TS_INVALID; + vout->p->step.last = VLC_TS_INVALID; + } + vout->p->pause.is_on = is_paused; + vout->p->pause.date = date; +} - if (last_picture_qtype != QTYPE_NONE) - PostProcessSetFilterQuality(vout); - } - vlc_mutex_unlock(&vout->p->vfilter_lock); +static void ThreadFlush(vout_thread_t *vout, bool below, mtime_t date) +{ + vout->p->step.timestamp = VLC_TS_INVALID; + vout->p->step.last = VLC_TS_INVALID; - vlc_mutex_unlock(&vout->p->change_lock); + picture_t *last = vout->p->displayed.decoded; + if (last) { + if (( below && last->date <= date) || + (!below && last->date >= date)) { + picture_Release(last); - if (deadline > VLC_TS_INVALID) { - vlc_mutex_lock(&vout->p->picture_lock); - vlc_cond_timedwait(&vout->p->picture_wait, &vout->p->picture_lock, deadline); - vlc_mutex_unlock(&vout->p->picture_lock); + vout->p->displayed.decoded = NULL; + vout->p->displayed.date = VLC_TS_INVALID; + vout->p->displayed.timestamp = VLC_TS_INVALID; } - - vlc_mutex_lock(&vout->p->change_lock); } + picture_fifo_Flush(vout->p->decoder_fifo, date, below); +} - /* - * Error loop - wait until the thread destruction is requested - * - * XXX I wonder if we should periodically clean the decoder_fifo - * or have a way to prevent it filling up. - */ - while (vout->p->b_error && !vout->p->b_done) - vlc_cond_wait(&vout->p->change_wait, &vout->p->change_lock); - - /* Clean thread */ - ThreadClean(vout); - -exit_thread: - /* Detach subpicture unit from both input and vout */ - spu_Attach(vout->p->p_spu, VLC_OBJECT(vout), false); - vlc_object_detach(vout->p->p_spu); - - /* Destroy the video filters2 */ - filter_chain_Delete(vout->p->p_vf2_chain); - - vlc_mutex_unlock(&vout->p->change_lock); - - if (has_wrapper) - vout_CloseWrapper(vout); - - return NULL; +static void ThreadReset(vout_thread_t *vout) +{ + ThreadFlush(vout, true, INT64_MAX); + if (vout->p->decoder_pool) + picture_pool_NonEmpty(vout->p->decoder_pool, true); + vout->p->pause.is_on = false; + vout->p->pause.date = mdate(); } -/***************************************************************************** - * object variables callbacks: a bunch of object variables are used by the - * interfaces to interact with the vout. - *****************************************************************************/ -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 ThreadStep(vout_thread_t *vout, mtime_t *duration) { - vout_thread_t *p_vout = (vout_thread_t *)p_this; - input_thread_t *p_input; - (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) - { - msg_Err( p_vout, "Input not found" ); - return VLC_EGENERIC; - } + *duration = 0; - var_SetBool( p_vout, "intf-change", true ); + if (vout->p->step.last <= VLC_TS_INVALID) + vout->p->step.last = vout->p->displayed.timestamp; - /* 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 ); + mtime_t dummy; + if (ThreadDisplayPicture(vout, true, &dummy)) + return; - /* Now restart current video stream */ - input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES ); - vlc_object_release( p_input ); + vout->p->step.timestamp = vout->p->displayed.timestamp; - return VLC_SUCCESS; + if (vout->p->step.last > VLC_TS_INVALID && + vout->p->step.timestamp > vout->p->step.last) { + *duration = vout->p->step.timestamp - vout->p->step.last; + vout->p->step.last = vout->p->step.timestamp; + /* TODO advance subpicture by the duration ... */ + } } -/***************************************************************************** - * 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 ThreadChangeFullscreen(vout_thread_t *vout, bool fullscreen) { - 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; + /* FIXME not sure setting "fullscreen" is good ... */ + var_SetBool(vout, "fullscreen", fullscreen); + vout_SetDisplayFullscreen(vout->p->display.vd, fullscreen); } -/***************************************************************************** - * Post-processing - *****************************************************************************/ -static bool PostProcessIsPresent( const char *psz_filter ) +static void ThreadChangeOnTop(vout_thread_t *vout, bool is_on_top) { - 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] == ':' ); + vout_SetWindowState(vout->p->display.vd, + is_on_top ? VOUT_WINDOW_STATE_ABOVE : + VOUT_WINDOW_STATE_NORMAL); } -static int PostProcessCallback( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) +static void ThreadChangeDisplayFilled(vout_thread_t *vout, bool is_filled) { - 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"; - - char *psz_vf2 = var_GetString( p_vout, "video-filter" ); - - 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; + vout_SetDisplayFilled(vout->p->display.vd, is_filled); } -static void PostProcessEnable( vout_thread_t *p_vout ) + +static void ThreadChangeZoom(vout_thread_t *vout, int num, int den) { - vlc_value_t text; - msg_Dbg( p_vout, "Post-processing available" ); - var_Create( p_vout, "postprocess", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE ); - text.psz_string = _("Post processing"); - var_Change( p_vout, "postprocess", VLC_VAR_SETTEXT, &text, NULL ); - - for( int i = 0; i <= 6; i++ ) - { - 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 ); + if (num * 10 < den) { + num = den; + den *= 10; + } else if (num > den * 10) { + num = den * 10; } - 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" ); + vout_SetDisplayZoom(vout->p->display.vd, num, den); } -static void PostProcessSetFilterQuality( vout_thread_t *p_vout ) + +static void ThreadChangeAspectRatio(vout_thread_t *vout, + unsigned num, unsigned den) { - vlc_object_t *p_pp = vlc_object_find_name( p_vout, "postproc", FIND_CHILD ); - if( !p_pp ) - return; + const video_format_t *source = &vout->p->original; - var_SetInteger( p_pp, "postproc-q", var_GetInteger( p_vout, "postprocess" ) ); - vlc_object_release( p_pp ); + if (num > 0 && den > 0) { + num *= source->i_visible_height; + den *= source->i_visible_width; + vlc_ureduce(&num, &den, num, den, 0); + } + vout_SetDisplayAspect(vout->p->display.vd, num, den); } -static void DisplayTitleOnOSD( vout_thread_t *p_vout ) +static void ThreadExecuteCropWindow(vout_thread_t *vout, + unsigned crop_num, unsigned crop_den, + unsigned x, unsigned y, + unsigned width, unsigned height) { - const mtime_t i_start = mdate(); - const mtime_t i_stop = i_start + INT64_C(1000) * p_vout->p->title.timeout; - - if( i_stop <= i_start ) - return; + const video_format_t *source = &vout->p->original; - vlc_assert_locked( &p_vout->p->change_lock ); - - vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, - p_vout->p->title.value, NULL, - p_vout->p->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->title.value ); - - p_vout->p->title.value = NULL; + vout_SetDisplayCrop(vout->p->display.vd, + crop_num, crop_den, + source->i_x_offset + x, + source->i_y_offset + y, + width, height); } - -/***************************************************************************** - * Deinterlacing - *****************************************************************************/ -/* 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 char *deinterlace_modes[] = { - "" - //"discard", - "blend", - //"mean", - //"bob", - //"linear", - "x", - //"yadif", - //"yadif2x", - NULL -}; -static bool DeinterlaceIsModeValid(const char *mode) +static void ThreadExecuteCropBorder(vout_thread_t *vout, + unsigned left, unsigned top, + unsigned right, unsigned bottom) { - for (unsigned i = 0; deinterlace_modes[i]; i++) { - if( !strcmp(deinterlace_modes[i], mode)) - return true; - } - return false; + const video_format_t *source = &vout->p->original; + ThreadExecuteCropWindow(vout, 0, 0, + left, + top, + /* At worst, it becomes < 0 (but unsigned) and will be rejected */ + source->i_visible_width - (left + right), + source->i_visible_height - (top + bottom)); } -static char *FilterFind( char *psz_filter_base, const char *psz_module ) +static void ThreadExecuteCropRatio(vout_thread_t *vout, + unsigned num, unsigned den) { - const size_t i_module = strlen( psz_module ); - const char *psz_filter = psz_filter_base; - - if( !psz_filter || i_module <= 0 ) - return NULL; - - for( ;; ) - { - 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]; - } + const video_format_t *source = &vout->p->original; + ThreadExecuteCropWindow(vout, num, den, + 0, 0, + source->i_visible_width, + source->i_visible_height); } -static bool DeinterlaceIsPresent( vout_thread_t *p_vout ) +static int ThreadStart(vout_thread_t *vout, const vout_display_state_t *state) { - char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" ); + vlc_mouse_Init(&vout->p->mouse); + vout->p->decoder_fifo = picture_fifo_New(); + vout->p->decoder_pool = NULL; + vout->p->display_pool = NULL; + vout->p->private_pool = NULL; + + vout->p->vfilter_chain = + filter_chain_New( vout, "video filter2", false, + VoutVideoFilterAllocationSetup, NULL, vout); + + vout_display_state_t state_default; + if (!state) { + VoutGetDisplayCfg(vout, &state_default.cfg, vout->p->display.title); + state_default.wm_state = var_CreateGetBool(vout, "video-on-top") ? VOUT_WINDOW_STATE_ABOVE : + VOUT_WINDOW_STATE_NORMAL; + state_default.sar.num = 0; + state_default.sar.den = 0; + + state = &state_default; + } - bool b_found = FilterFind( psz_filter, "deinterlace" ) != NULL; + if (vout_OpenWrapper(vout, vout->p->splitter_name, state)) + return VLC_EGENERIC; + if (vout_InitWrapper(vout)) + return VLC_EGENERIC; + assert(vout->p->decoder_pool); - free( psz_filter ); + vout->p->displayed.decoded = NULL; + vout->p->displayed.date = VLC_TS_INVALID; + vout->p->displayed.decoded = NULL; + vout->p->displayed.timestamp = VLC_TS_INVALID; + vout->p->displayed.qtype = QTYPE_NONE; + vout->p->displayed.is_interlaced = false; - return b_found; + vout->p->step.last = VLC_TS_INVALID; + vout->p->step.timestamp = VLC_TS_INVALID; + + video_format_Print(VLC_OBJECT(vout), "original format", &vout->p->original); + return VLC_SUCCESS; } -static void DeinterlaceRemove( vout_thread_t *p_vout ) +static void ThreadStop(vout_thread_t *vout, vout_display_state_t *state) { - char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" ); + /* Destroy the video filters2 */ + filter_chain_Delete(vout->p->vfilter_chain); - char *psz = FilterFind( psz_filter, "deinterlace" ); - if( !psz ) - { - free( psz_filter ); - return; + /* Destroy translation tables */ + if (vout->p->display.vd) { + if (vout->p->decoder_pool) { + ThreadFlush(vout, true, INT64_MAX); + vout_EndWrapper(vout); + } + vout_CloseWrapper(vout, state); } - /* */ - strcpy( &psz[0], &psz[strlen("deinterlace")] ); - if( *psz == ':' ) - strcpy( &psz[0], &psz[1] ); - - var_SetString( p_vout, "video-filter", psz_filter ); - free( psz_filter ); + if (vout->p->decoder_fifo) + picture_fifo_Delete(vout->p->decoder_fifo); + assert(!vout->p->decoder_pool); } -static void DeinterlaceAdd( vout_thread_t *p_vout ) -{ - char *psz_filter = var_GetNonEmptyString( p_vout, "video-filter" ); - 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, "video-filter", psz_filter ); - free( psz_filter ); - } +static void ThreadInit(vout_thread_t *vout) +{ + vout->p->window.is_unused = true; + vout->p->window.object = NULL; + vout->p->dead = false; + vout->p->is_late_dropped = var_InheritBool(vout, "drop-late-frames"); + vout->p->pause.is_on = false; + vout->p->pause.date = VLC_TS_INVALID; + + vout_chrono_Init(&vout->p->render, 5, 10000); /* Arbitrary initial time */ } -static void DeinterlaceSave( vout_thread_t *p_vout, int i_deinterlace, const char *psz_mode ) +static void ThreadClean(vout_thread_t *vout) { - /* We have to set input variable to ensure restart support - * FIXME to be removed when vout_Request does the right job. - */ - vlc_object_t *p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_PARENT ); - if( !p_input ) - return; - - var_Create( p_input, "deinterlace", VLC_VAR_INTEGER ); - var_SetInteger( p_input, "deinterlace", i_deinterlace ); - - static const char * const ppsz_variable[] = { - "deinterlace-mode", - "sout-deinterlace-mode", - NULL - }; - for( int i = 0; ppsz_variable[i]; i++ ) - { - var_Create( p_input, ppsz_variable[i], VLC_VAR_STRING ); - var_SetString( p_input, ppsz_variable[i], psz_mode ); + if (vout->p->window.object) { + assert(vout->p->window.is_unused); + vout_window_Delete(vout->p->window.object); } - - vlc_object_release( p_input ); + vout_chrono_Clean(&vout->p->render); + vout->p->dead = true; + vout_control_Dead(&vout->p->control); } -static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) -{ - VLC_UNUSED(psz_cmd); VLC_UNUSED(oldval); VLC_UNUSED(newval); VLC_UNUSED(p_data); - vout_thread_t *p_vout = (vout_thread_t *)p_this; - /* */ - const int i_deinterlace = var_GetInteger( p_this, "deinterlace" ); - char *psz_mode = var_GetString( p_this, "deinterlace-mode" ); - const bool is_needed = var_GetBool( p_this, "deinterlace-needed" ); - if( !psz_mode || !DeinterlaceIsModeValid(psz_mode) ) +static int ThreadReinit(vout_thread_t *vout, + const vout_configuration_t *cfg) +{ + video_format_t original; + if (VoutValidateFormat(&original, cfg->fmt)) { + ThreadStop(vout, NULL); + ThreadClean(vout); return VLC_EGENERIC; + } + if (video_format_IsSimilar(&original, &vout->p->original)) + return VLC_SUCCESS; - DeinterlaceSave( p_vout, i_deinterlace, psz_mode ); + vout_display_state_t state; + memset(&state, 0, sizeof(state)); - /* */ - char *psz_old = var_CreateGetString( p_vout, "sout-deinterlace-mode" ); + ThreadStop(vout, &state); - msg_Dbg( p_vout, "deinterlace %d, mode %s, is_needed %d", i_deinterlace, psz_mode, is_needed ); - if( i_deinterlace == 0 || ( i_deinterlace == -1 && !is_needed ) ) - { - DeinterlaceRemove( p_vout ); - } - else if( !DeinterlaceIsPresent( p_vout ) ) - { - DeinterlaceAdd( p_vout ); + if (!state.cfg.is_fullscreen) { + state.cfg.display.width = 0; + state.cfg.display.height = 0; } - else if( psz_old && strcmp( psz_old, psz_mode ) ) - { - var_TriggerCallback( p_vout, "video-filter" ); + state.sar.num = 0; + state.sar.den = 0; + /* FIXME current vout "variables" are not in sync here anymore + * and I am not sure what to do */ + + vout->p->original = original; + if (ThreadStart(vout, &state)) { + ThreadClean(vout); + return VLC_EGENERIC; } - - /* */ - free( psz_old ); - free( psz_mode ); return VLC_SUCCESS; } -static void DeinterlaceEnable( vout_thread_t *p_vout, bool is_interlaced ) +/***************************************************************************** + * Thread: video output thread + ***************************************************************************** + * Video output thread. This function does only returns when the thread is + * terminated. It handles the pictures arriving in the video heap and the + * display device events. + *****************************************************************************/ +static void *Thread(void *object) { - vlc_value_t val, text; - - msg_Dbg( p_vout, "Deinterlacing available" ); - - /* Create the configuration variables */ - /* */ - var_Create( p_vout, "deinterlace", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE ); - int i_deinterlace = var_GetInteger( p_vout, "deinterlace" ); - - text.psz_string = _("Deinterlace"); - var_Change( p_vout, "deinterlace", VLC_VAR_SETTEXT, &text, NULL ); - - const module_config_t *p_optd = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace" ); - var_Change( p_vout, "deinterlace", VLC_VAR_CLEARCHOICES, NULL, NULL ); - for( int i = 0; p_optd && i < p_optd->i_list; i++ ) - { - val.i_int = p_optd->pi_list[i]; - text.psz_string = (char*)vlc_gettext(p_optd->ppsz_list_text[i]); - var_Change( p_vout, "deinterlace", VLC_VAR_ADDCHOICE, &val, &text ); - } - var_AddCallback( p_vout, "deinterlace", DeinterlaceCallback, NULL ); - /* */ - var_Create( p_vout, "deinterlace-mode", VLC_VAR_STRING | VLC_VAR_DOINHERIT | VLC_VAR_HASCHOICE ); - char *psz_deinterlace = var_GetNonEmptyString( p_vout, "deinterlace-mode" ); - - text.psz_string = _("Deinterlace mode"); - var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETTEXT, &text, NULL ); - - const module_config_t *p_optm = config_FindConfig( VLC_OBJECT(p_vout), "deinterlace-mode" ); - var_Change( p_vout, "deinterlace-mode", VLC_VAR_CLEARCHOICES, NULL, NULL ); - for( int i = 0; p_optm && i < p_optm->i_list; i++ ) - { - if( !DeinterlaceIsModeValid( p_optm->ppsz_list[i] ) ) - continue; - - val.psz_string = p_optm->ppsz_list[i]; - text.psz_string = (char*)vlc_gettext(p_optm->ppsz_list_text[i]); - var_Change( p_vout, "deinterlace-mode", VLC_VAR_ADDCHOICE, &val, &text ); - } - var_AddCallback( p_vout, "deinterlace-mode", DeinterlaceCallback, NULL ); - /* */ - var_Create( p_vout, "deinterlace-needed", VLC_VAR_BOOL ); - var_AddCallback( p_vout, "deinterlace-needed", DeinterlaceCallback, NULL ); - - /* Override the initial value from filters if present */ - char *psz_filter_mode = NULL; - if( DeinterlaceIsPresent( p_vout ) ) - psz_filter_mode = var_CreateGetNonEmptyString( p_vout, "sout-deinterlace-mode" ); - if( psz_filter_mode ) - { - free( psz_deinterlace ); - if( i_deinterlace >= -1 ) - i_deinterlace = 1; - psz_deinterlace = psz_filter_mode; - } - - /* */ - if( i_deinterlace < 0 ) - i_deinterlace = -1; + vout_thread_t *vout = object; - /* */ - val.psz_string = psz_deinterlace ? psz_deinterlace : p_optm->orig.psz; - var_Change( p_vout, "deinterlace-mode", VLC_VAR_SETVALUE, &val, NULL ); - val.b_bool = is_interlaced; - var_Change( p_vout, "deinterlace-needed", VLC_VAR_SETVALUE, &val, NULL ); + vout_interlacing_support_t interlacing = { + .is_interlaced = false, + .date = mdate(), + }; + vout_postprocessing_support_t postprocessing = { + .qtype = QTYPE_NONE, + }; - var_SetInteger( p_vout, "deinterlace", i_deinterlace ); - free( psz_deinterlace ); -} + mtime_t deadline = VLC_TS_INVALID; + for (;;) { + vout_control_cmd_t cmd; -static void DeinterlaceNeeded( vout_thread_t *p_vout, bool is_interlaced ) -{ - msg_Dbg( p_vout, "Detected %s video", - is_interlaced ? "interlaced" : "progressive" ); - var_SetBool( p_vout, "deinterlace-needed", is_interlaced ); -} + /* FIXME remove thoses ugly timeouts + */ + while (!vout_control_Pop(&vout->p->control, &cmd, deadline, 100000)) { + switch(cmd.type) { + case VOUT_CONTROL_INIT: + ThreadInit(vout); + if (ThreadStart(vout, NULL)) { + ThreadStop(vout, NULL); + ThreadClean(vout); + return NULL; + } + break; + case VOUT_CONTROL_CLEAN: + ThreadStop(vout, NULL); + ThreadClean(vout); + return NULL; + case VOUT_CONTROL_REINIT: + if (ThreadReinit(vout, cmd.u.cfg)) + return NULL; + break; + case VOUT_CONTROL_OSD_TITLE: + ThreadDisplayOsdTitle(vout, cmd.u.string); + break; + case VOUT_CONTROL_CHANGE_FILTERS: + ThreadChangeFilters(vout, cmd.u.string); + break; + case VOUT_CONTROL_CHANGE_SUB_FILTERS: + ThreadChangeSubFilters(vout, cmd.u.string); + break; + case VOUT_CONTROL_PAUSE: + ThreadChangePause(vout, cmd.u.pause.is_on, cmd.u.pause.date); + break; + case VOUT_CONTROL_FLUSH: + ThreadFlush(vout, false, cmd.u.time); + break; + case VOUT_CONTROL_RESET: + ThreadReset(vout); + break; + case VOUT_CONTROL_STEP: + ThreadStep(vout, cmd.u.time_ptr); + break; + case VOUT_CONTROL_FULLSCREEN: + ThreadChangeFullscreen(vout, cmd.u.boolean); + break; + case VOUT_CONTROL_ON_TOP: + ThreadChangeOnTop(vout, cmd.u.boolean); + break; + case VOUT_CONTROL_DISPLAY_FILLED: + ThreadChangeDisplayFilled(vout, cmd.u.boolean); + break; + case VOUT_CONTROL_ZOOM: + ThreadChangeZoom(vout, cmd.u.pair.a, cmd.u.pair.b); + break; + case VOUT_CONTROL_ASPECT_RATIO: + ThreadChangeAspectRatio(vout, cmd.u.pair.a, cmd.u.pair.b); + break; + case VOUT_CONTROL_CROP_RATIO: + ThreadExecuteCropRatio(vout, cmd.u.pair.a, cmd.u.pair.b); + break; + case VOUT_CONTROL_CROP_WINDOW: + ThreadExecuteCropWindow(vout, 0, 0, + cmd.u.window.x, cmd.u.window.y, + cmd.u.window.width, cmd.u.window.height); + break; + case VOUT_CONTROL_CROP_BORDER: + ThreadExecuteCropBorder(vout, + cmd.u.border.left, cmd.u.border.top, + cmd.u.border.right, cmd.u.border.bottom); + break; + default: + break; + } + vout_control_cmd_Clean(&cmd); + } -/* */ -static void PrintVideoFormat(vout_thread_t *vout, - const char *description, - const video_format_t *fmt) -{ - msg_Dbg(vout, "%s sz %ix%i, of (%i,%i), vsz %ix%i, 4cc %4.4s, sar %i:%i, msk r0x%x g0x%x b0x%x", - description, - fmt->i_width, fmt->i_height, fmt->i_x_offset, fmt->i_y_offset, - fmt->i_visible_width, fmt->i_visible_height, - (char*)&fmt->i_chroma, - fmt->i_sar_num, fmt->i_sar_den, - fmt->i_rmask, fmt->i_gmask, fmt->i_bmask); + ThreadManage(vout, &deadline, &interlacing, &postprocessing); + } }