X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fvideo_output%2Fvideo_output.c;h=9790f77eee71921b37cad6626ad9aa385d2835d7;hb=d50a6fad1f2c44990abeb019d327929f23acb9e8;hp=b9516d29a5e433dd1bb6ee6ee78f62e9e1e53e19;hpb=6da90a1716250d282f16dc6bc9dacec5b9514caf;p=vlc diff --git a/src/video_output/video_output.c b/src/video_output/video_output.c index b9516d29a5..9790f77eee 100644 --- a/src/video_output/video_output.c +++ b/src/video_output/video_output.c @@ -33,7 +33,7 @@ # include "config.h" #endif -#include +#include #include /* free() */ #include @@ -44,10 +44,10 @@ #endif #include -#include #include #include +#include #if defined( __APPLE__ ) /* Include darwin_specific.h here if needed */ @@ -55,21 +55,26 @@ /** 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 ( vlc_object_t * ); static void ErrorThread ( vout_thread_t * ); +static void CleanThread ( vout_thread_t * ); static void EndThread ( vout_thread_t * ); static void AspectRatio ( int, int *, int * ); -static int BinaryLog ( uint32_t ); -static void MaskToShift ( int *, int *, uint32_t ); + +static void VideoFormatImportRgb( video_format_t *, const picture_heap_t * ); +static void PictureHeapFixRgb( picture_heap_t * ); static void vout_Destructor ( vlc_object_t * p_this ); @@ -84,34 +89,59 @@ static int VideoFilter2Callback( vlc_object_t *, char const *, /* 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; } /***************************************************************************** @@ -123,41 +153,24 @@ static void video_del_buffer_filter( filter_t *p_filter, picture_t *p_pic ) vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout, video_format_t *p_fmt ) { + const bool b_vout_provided = p_vout != NULL; 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 ) - { - spu_Attach( p_vout->p_spu, p_this, false ); - vlc_object_detach( p_vout ); - vlc_object_attach( p_vout, p_this->p_libvlc ); - } + 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 ) - { - p_vout = vlc_object_find( p_this->p_libvlc, - VLC_OBJECT_VOUT, FIND_CHILD ); - /* only first children of p_input for unused vout */ - if( p_vout && p_vout->p_parent != VLC_OBJECT(p_this->p_libvlc) ) - { - vlc_object_release( p_vout ); - p_vout = NULL; - } - if( p_vout ) - vlc_object_detach( p_vout ); /* Remove it from the GC */ - } - } + /* TODO: find a suitable unused video output */ /* If we now have a video output, check it has the right properties */ if( p_vout ) @@ -165,9 +178,11 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *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; @@ -177,38 +192,92 @@ vout_thread_t *__vout_Request( vlc_object_t *p_this, vout_thread_t *p_vout, free( psz_filter_chain ); psz_filter_chain = NULL; } - if( p_vout->psz_filter_chain && !*p_vout->psz_filter_chain ) + if( p_vout->p->psz_filter_chain && !*p_vout->p->psz_filter_chain ) { - free( p_vout->psz_filter_chain ); - p_vout->psz_filter_chain = NULL; + free( p_vout->p->psz_filter_chain ); + p_vout->p->psz_filter_chain = NULL; } - if( !psz_filter_chain && !p_vout->psz_filter_chain ) + if( !psz_filter_chain && !p_vout->p->psz_filter_chain ) { - p_vout->b_filter_change = false; + p_vout->p->b_filter_change = false; } free( psz_filter_chain ); } - if( ( p_vout->fmt_render.i_width != p_fmt->i_width ) || - ( p_vout->fmt_render.i_height != p_fmt->i_height ) || - ( p_vout->fmt_render.i_aspect != p_fmt->i_aspect ) || - p_vout->b_filter_change ) + if( p_vout->fmt_render.i_chroma != 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. */ + 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, p_this, true ); + + vlc_object_detach( p_vout ); vlc_object_attach( p_vout, p_this ); - if( p_vout->b_title_show ) + + /* Display title if we are not using the vout given to vout_Request. + * XXX for now b_vout_provided is always true at this stage */ + if( p_vout->p->b_title_show && !b_vout_provided ) DisplayTitleOnOSD( p_vout ); - vlc_object_release( p_vout ); } } @@ -231,7 +300,6 @@ 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; @@ -244,11 +312,26 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) 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; } @@ -269,8 +352,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 */ @@ -279,9 +360,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; @@ -299,24 +380,30 @@ 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_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_window = 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; /* Initialize locks */ vlc_mutex_init( &p_vout->picture_lock ); + vlc_cond_init( &p_vout->p->picture_wait ); vlc_mutex_init( &p_vout->change_lock ); - vlc_mutex_init( &p_vout->vfilter_lock ); + vlc_mutex_init( &p_vout->p->vfilter_lock ); /* Mouse coordinates */ var_Create( p_vout, "mouse-x", VLC_VAR_INTEGER ); @@ -342,16 +429,12 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) if( 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" ); } else { @@ -359,24 +442,25 @@ 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" ); } var_AddCallback( p_vout, "video-filter", VideoFilter2Callback, NULL ); - p_vout->b_vfilter_change = 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 ); @@ -384,22 +468,24 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) } else { - psz_parser = strdup( p_vout->psz_filter_chain ); + psz_parser = strdup( p_vout->p->psz_filter_chain ); + p_vout->p->b_title_show = false; } /* Create the vout thread */ - config_ChainCreate( &psz_name, &p_cfg, psz_parser ); + char* psz_tmp = config_ChainCreate( &psz_name, &p_cfg, psz_parser ); free( psz_parser ); + free( psz_tmp ); p_vout->p_cfg = p_cfg; - p_vout->p_module = module_Need( p_vout, - ( p_vout->psz_filter_chain && *p_vout->psz_filter_chain ) ? - "video filter" : "video output", psz_name, p_vout->psz_filter_chain && *p_vout->psz_filter_chain ); + p_vout->p_module = module_need( p_vout, + ( p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain ) ? + "video filter" : "video output", psz_name, p_vout->p->psz_filter_chain && *p_vout->p->psz_filter_chain ); free( psz_name ); if( p_vout->p_module == NULL ) { msg_Err( p_vout, "no suitable vout module" ); - vlc_object_detach( p_vout ); + vlc_object_set_destructor( p_vout, vout_Destructor ); vlc_object_release( p_vout ); return NULL; } @@ -435,101 +521,228 @@ vout_thread_t * __vout_Create( vlc_object_t *p_parent, video_format_t *p_fmt ) 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; - } - if( vlc_thread_create( p_vout, "video output", RunThread, VLC_THREAD_PRIORITY_OUTPUT, true ) ) { - msg_Err( p_vout, "out of memory" ); - module_Unneed( p_vout, p_vout->p_module ); + module_unneed( p_vout, p_vout->p_module ); + p_vout->p_module = NULL; + vlc_object_set_destructor( p_vout, vout_Destructor ); vlc_object_release( p_vout ); return NULL; } + vlc_object_set_destructor( p_vout, vout_Destructor ); + if( p_vout->b_error ) { msg_Err( p_vout, "video output creation failed" ); - - /* Make sure the thread is destroyed */ - vlc_object_release( p_vout ); + vout_CloseAndRelease( p_vout ); return NULL; } - vlc_object_set_destructor( p_vout, vout_Destructor ); - return p_vout; } /***************************************************************************** - * 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 convenient method. *****************************************************************************/ -void vout_Destroy( vout_thread_t *p_vout ) +void vout_Close( vout_thread_t *p_vout ) { - /* XXX: should go in the destructor */ - var_Destroy( p_vout, "intf-change" ); + assert( p_vout ); - vlc_object_release( p_vout ); + vlc_object_kill( p_vout ); + vlc_thread_join( p_vout ); + module_unneed( p_vout, p_vout->p_module ); + p_vout->p_module = NULL; } +/* */ static void vout_Destructor( vlc_object_t * p_this ) { vout_thread_t *p_vout = (vout_thread_t *)p_this; + /* Make sure the vout was stopped first */ + assert( !p_vout->p_module ); + + /* */ + spu_Destroy( p_vout->p_spu ); + /* Destroy the locks */ + vlc_cond_destroy( &p_vout->p->picture_wait ); vlc_mutex_destroy( &p_vout->picture_lock ); vlc_mutex_destroy( &p_vout->change_lock ); - vlc_mutex_destroy( &p_vout->vfilter_lock ); - - /* Release the module */ - if( p_vout->p_module ) - { - module_Unneed( p_vout, p_vout->p_module ); - } + vlc_mutex_destroy( &p_vout->p->vfilter_lock ); - free( p_vout->psz_filter_chain ); + free( p_vout->p->psz_filter_chain ); config_ChainDestroy( p_vout->p_cfg ); + free( p_vout->p ); + #ifndef __APPLE__ vout_thread_t *p_another_vout; - playlist_t *p_playlist = pl_Get( p_vout ); - if( p_playlist->b_die ) return; - vlc_object_yield( p_playlist ); -/* 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_this->p_libvlc, - VLC_OBJECT_VOUT, FIND_ANYWHERE ); - if( p_another_vout == NULL ) + /* 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_object_lock( p_vout ); + + assert( !p_vout->p->b_paused || !b_paused ); + + vlc_mutex_lock( &p_vout->picture_lock ); + + p_vout->p->i_picture_displayed_date = 0; + + if( p_vout->p->b_paused ) + { + const mtime_t i_duration = i_date - p_vout->p->i_pause_date; + + for( int i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) { - vlc_value_t val; - val.b_bool = true; - var_Set( p_playlist, "intf-show", val ); + 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 ); } - vlc_object_release( p_playlist ); -#endif + p_vout->p->b_paused = b_paused; + p_vout->p->i_pause_date = i_date; + + vlc_object_unlock( p_vout ); +} +void vout_GetResetStatistic( vout_thread_t *p_vout, int *pi_displayed, int *pi_lost ) +{ + vlc_object_lock( p_vout ); + + *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_object_unlock( p_vout ); +} +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; + vout_UsePictureLocked( p_vout, p_pic ); + } + 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 ); } /***************************************************************************** @@ -538,30 +751,42 @@ static void vout_Destructor( vlc_object_t * p_this ) * 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 != FOURCC_RV15 && + p_output->i_chroma != FOURCC_RV16 && + p_output->i_chroma != FOURCC_RV24 && + p_output->i_chroma != FOURCC_RV32 ) + 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; } @@ -570,7 +795,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; } @@ -634,25 +858,25 @@ static int InitThread( vout_thread_t *p_vout ) i_aspect_x, i_aspect_y, p_vout->fmt_out.i_sar_num, p_vout->fmt_out.i_sar_den ); + /* FIXME removed the need of both fmt_* and heap infos */ /* 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 ); + PictureHeapFixRgb( &p_vout->render ); + VideoFormatImportRgb( &p_vout->fmt_render, &p_vout->render ); + + PictureHeapFixRgb( &p_vout->output ); + VideoFormatImportRgb( &p_vout->fmt_out, &p_vout->output ); /* 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++ ) { @@ -677,18 +901,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; } @@ -720,7 +937,6 @@ static int InitThread( vout_thread_t *p_vout ) PP_OUTPUTPICTURE[ i ]->p_heap = &p_vout->output; } -/* XXX XXX mark thread ready */ return VLC_SUCCESS; } @@ -731,193 +947,119 @@ 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( vlc_object_t *p_this ) { - int i_index; /* index in heap */ + vout_thread_t *p_vout = (vout_thread_t *)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 */ + bool b_drop_late; - input_thread_t *p_input = NULL ; /* Parent input, if it exists */ - - vlc_value_t val; - bool b_drop_late; - - int i_displayed = 0, i_lost = 0, i_loops = 0; + int canc = vlc_savecancel(); /* * 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 ); if( p_vout->b_error ) - return; + { + EndThread( p_vout ); + vlc_mutex_unlock( &p_vout->change_lock ); + vlc_restorecancel( canc ); + return NULL; + } + + vlc_object_lock( p_vout ); - if( p_vout->b_title_show ) + if( p_vout->p->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( vlc_object_alive( p_vout ) && !p_vout->b_error ) { /* Initialize loop variables */ + const mtime_t current_date = mdate(); + picture_t *p_picture; + picture_t *p_filtered_picture; + mtime_t display_date; + picture_t *p_directbuffer; + int i_index; + + vlc_mutex_lock( &p_vout->picture_lock ); + + /* Look for the earliest picture but after the last displayed one */ + picture_t *p_last = p_vout->p->p_picture_displayed;; + p_picture = NULL; - display_date = 0; - current_date = mdate(); + for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) + { + picture_t *p_pic = PP_RENDERPICTURE[i_index]; + + if( p_pic->i_status != READY_PICTURE ) + continue; - i_loops++; - if( !p_input ) + 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 ) { - p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, - FIND_PARENT ); + /* Drop old picture */ + vout_UsePictureLocked( p_vout, p_pic ); } - if( p_input ) + 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 ) { - vlc_mutex_lock( &p_input->p->counters.counters_lock ); - stats_UpdateInteger( p_vout, p_input->p->counters.p_lost_pictures, - i_lost , NULL); - stats_UpdateInteger( p_vout, - p_input->p->counters.p_displayed_pictures, - i_displayed , NULL); - i_displayed = i_lost = 0; - vlc_mutex_unlock( &p_input->p->counters.counters_lock ); - vlc_object_release( p_input ); - p_input = NULL; + /* 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")", + current_date - p_pic->date ); + } + else if( ( !p_last || p_last->date < p_pic->date ) && + ( p_picture == NULL || p_pic->date < p_picture->date ) ) + { + p_picture = p_pic; } -#if 0 - p_vout->c_loops++; - if( !(p_vout->c_loops % VOUT_STATS_NB_LOOPS) ) - { - msg_Dbg( p_vout, "picture heap: %d/%d", - I_RENDERPICTURES, p_vout->i_heap_size ); } -#endif - - /* - * Find the picture to display (the one with the earliest date). - * This operation does not need lock, since only READY_PICTUREs - * are handled. */ - for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) + if( !p_picture ) { - if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE) - && ( (p_picture == NULL) || - (PP_RENDERPICTURE[i_index]->date < display_date) ) ) + p_picture = p_last; + + if( !p_vout->p->b_picture_empty ) { - p_picture = PP_RENDERPICTURE[i_index]; - display_date = p_picture->date; + p_vout->p->b_picture_empty = true; + vlc_cond_signal( &p_vout->p->picture_wait ); } } + display_date = 0; if( p_picture ) { - /* If we met the last picture, parse again to see whether there is - * a more appropriate one. */ - if( p_picture == p_last_picture ) - { - for( i_index = 0; i_index < I_RENDERPICTURES; i_index++ ) - { - if( (PP_RENDERPICTURE[i_index]->i_status == READY_PICTURE) - && (PP_RENDERPICTURE[i_index] != p_last_picture) - && ((p_picture == p_last_picture) || - (PP_RENDERPICTURE[i_index]->date < display_date)) ) - { - p_picture = PP_RENDERPICTURE[i_index]; - display_date = p_picture->date; - } - } - } + display_date = p_picture->date; /* If we found better than the last picture, destroy it */ - if( p_last_picture && p_picture != p_last_picture ) + if( p_last && p_picture != p_last ) { - vlc_mutex_lock( &p_vout->picture_lock ); - if( p_last_picture->i_refcount ) - { - p_last_picture->i_status = DISPLAYED_PICTURE; - } - else - { - p_last_picture->i_status = DESTROYED_PICTURE; - p_vout->i_heap_size--; - } - vlc_mutex_unlock( &p_vout->picture_lock ); - p_last_picture = NULL; + vout_UsePictureLocked( p_vout, p_last ); + p_vout->p->p_picture_displayed = p_last = NULL; } /* Compute FPS rate */ - p_vout->p_fps_sample[ p_vout->c_fps_samples++ % VOUT_FPS_SAMPLES ] - = display_date; - - if( !p_picture->b_force && - p_picture != p_last_picture && - display_date < current_date + p_vout->render_time && - b_drop_late ) - { - /* Picture is late: it will be destroyed and the thread - * will directly choose the next picture */ - vlc_mutex_lock( &p_vout->picture_lock ); - if( p_picture->i_refcount ) - { - /* Pretend we displayed the picture, but don't destroy - * it since the decoder might still need it. */ - p_picture->i_status = DISPLAYED_PICTURE; - } - else - { - /* Destroy the picture without displaying it */ - p_picture->i_status = DESTROYED_PICTURE; - p_vout->i_heap_size--; - } - msg_Warn( p_vout, "late picture skipped (%"PRId64")", - current_date - display_date ); - i_lost++; - vlc_mutex_unlock( &p_vout->picture_lock ); - - continue; - } - - if( display_date > - current_date + p_vout->i_pts_delay + VOUT_BOGUS_DELAY ) - { - /* Picture is waaay too early: it will be destroyed */ - vlc_mutex_lock( &p_vout->picture_lock ); - if( p_picture->i_refcount ) - { - /* Pretend we displayed the picture, but don't destroy - * it since the decoder might still need it. */ - p_picture->i_status = DISPLAYED_PICTURE; - } - else - { - /* Destroy the picture without displaying it */ - p_picture->i_status = DESTROYED_PICTURE; - p_vout->i_heap_size--; - } - i_lost++; - msg_Warn( p_vout, "vout warning: early picture skipped " - "(%"PRId64")", display_date - current_date - - p_vout->i_pts_delay ); - vlc_mutex_unlock( &p_vout->picture_lock ); - - continue; - } + 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 @@ -926,7 +1068,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 */ @@ -939,136 +1081,72 @@ 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 == 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], - 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_release( p_vfilter ); - } + display_date = current_date + VOUT_DISPLAY_DELAY; } - p_vout->b_vfilter_change = 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 ) + if( p_picture->date > 1 ) { - 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 - { - 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; } } + vlc_mutex_unlock( &p_vout->picture_lock ); - if( p_picture && p_vout->b_snapshot ) - { - p_vout->b_snapshot = false; - vout_Snapshot( p_vout, p_picture ); - } + 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 ); + + /* FIXME it is ugly that b_snapshot is not locked but I do not + * know which lock to use (here and in the snapshot callback) */ + const bool b_snapshot = p_vout->p->b_snapshot && p_picture != NULL; /* * Check for subpictures to display */ + subpicture_t *p_subpic = NULL; if( display_date > 0 ) - { - if( !p_input ) - { - p_input = vlc_object_find( p_vout, VLC_OBJECT_INPUT, - FIND_PARENT ); - } p_subpic = spu_SortSubpictures( p_vout->p_spu, display_date, - p_input ? var_GetBool( p_input, "state" ) == PAUSE_S : false ); - if( p_input ) - vlc_object_release( p_input ); - p_input = NULL; - } + p_vout->p->b_paused, b_snapshot ); /* * Perform rendering */ - i_displayed++; - p_directbuffer = vout_RenderPicture( p_vout, p_picture, p_subpic ); + p_vout->p->i_picture_displayed++; + p_directbuffer = vout_RenderPicture( p_vout, p_filtered_picture, + p_subpic, p_vout->p->b_paused ); + + /* + * Take a snapshot if requested + */ + if( p_directbuffer && b_snapshot ) + { + /* FIXME lock (see b_snapshot) */ + p_vout->p->b_snapshot = false; + + vout_Snapshot( p_vout, p_directbuffer ); + } /* * 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 ); @@ -1081,54 +1159,67 @@ 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; } } /* Give back change lock */ vlc_mutex_unlock( &p_vout->change_lock ); + vlc_object_unlock( p_vout ); + /* Sleep a while or until a given date */ if( display_date != 0 ) { - /* If there are filters in the chain, better give them the picture + /* If there are *vout* filters in the chain, better give them the picture * in advance */ - if( !p_vout->psz_filter_chain || !*p_vout->psz_filter_chain ) + if( !p_vout->p->psz_filter_chain || !*p_vout->p->psz_filter_chain ) { mwait( display_date - VOUT_MWAIT_TOLERANCE ); } } else { - msleep( VOUT_IDLE_SLEEP ); + /* Wait until a frame is being sent or a spurious wakeup (not a problem here) */ + vlc_mutex_lock( &p_vout->picture_lock ); + vlc_cond_timedwait( &p_vout->p->picture_wait, &p_vout->picture_lock, current_date + VOUT_IDLE_SLEEP ); + vlc_mutex_unlock( &p_vout->picture_lock ); } /* On awakening, take back lock and send immediately picture * to display. */ + vlc_object_lock( p_vout ); + /* Note: vlc_object_alive() could be false here, and we + * could be dead */ 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 ) @@ -1145,7 +1236,9 @@ 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; } if( p_vout->i_changes & VOUT_SIZE_CHANGE ) @@ -1158,11 +1251,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" ); @@ -1170,13 +1273,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 ) @@ -1188,10 +1297,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 ); @@ -1200,26 +1307,50 @@ 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\n" ); + 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 ); + /* 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; + } + 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 ); + + vlc_object_unlock( p_vout ); + vlc_restorecancel( canc ); + return NULL; } /***************************************************************************** @@ -1232,23 +1363,47 @@ 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 ) + while( vlc_object_alive( p_vout ) ) + vlc_object_wait( p_vout ); +} + +/***************************************************************************** + * 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 CleanThread( vout_thread_t *p_vout ) +{ + int i_index; /* index in heap */ + + 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++ ) { - /* Sleep a while */ - msleep( VOUT_IDLE_SLEEP ); + if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE ) + { + free( p_vout->p_picture[i_index].p_data_orig ); + } } + + /* Destroy translation tables */ + if( !p_vout->b_error ) + p_vout->pf_end( p_vout ); } /***************************************************************************** * EndThread: thread destruction ***************************************************************************** - * This function is called when the thread ends after a sucessful - * initialization. It frees all resources allocated by InitThread. + * 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 EndThread( vout_thread_t *p_vout ) { - int i_index; /* index in heap */ - #ifdef STATS { struct tms cpu_usage; @@ -1259,36 +1414,74 @@ static void EndThread( vout_thread_t *p_vout ) } #endif - if( !p_vout->b_direct ) - { - module_Unneed( p_vout, p_vout->chroma.p_module ); - } - - /* Destroy all remaining pictures */ - for( i_index = 0; i_index < 2 * VOUT_MAX_PICTURES + 1; i_index++ ) - { - if ( p_vout->p_picture[i_index].i_type == MEMORY_PICTURE ) - { - free( p_vout->p_picture[i_index].p_data_orig ); - } - } + /* FIXME does that function *really* need to be called inside the thread ? */ /* Destroy subpicture unit */ spu_Attach( p_vout->p_spu, VLC_OBJECT(p_vout), false ); - spu_Destroy( p_vout->p_spu ); /* Destroy the video filters2 */ - RemoveVideoFilters2( p_vout ); + filter_chain_Delete( p_vout->p->p_vf2_chain ); +} - /* Destroy translation tables */ - p_vout->pf_end( p_vout ); +/* 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; +} - /* Release the change lock */ - vlc_mutex_unlock( &p_vout->change_lock ); +static int ChromaCreate( vout_thread_t *p_vout ) +{ + static const char typename[] = "chroma"; + filter_t *p_chroma; + + /* Choose the best module */ + p_chroma = p_vout->p->p_chroma = + vlc_custom_create( p_vout, sizeof(filter_t), VLC_OBJECT_GENERIC, + typename ); + + vlc_object_attach( p_chroma, p_vout ); + + /* TODO: Set the fmt_in and fmt_out stuff here */ + p_chroma->fmt_in.video = p_vout->fmt_render; + p_chroma->fmt_out.video = p_vout->fmt_out; + VideoFormatImportRgb( &p_chroma->fmt_in.video, &p_vout->render ); + VideoFormatImportRgb( &p_chroma->fmt_out.video, &p_vout->output ); + + p_chroma->p_module = module_need( p_chroma, "video filter2", NULL, 0 ); + + if( p_chroma->p_module == NULL ) + { + 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; } -/* following functions are local */ +static void ChromaDestroy( vout_thread_t *p_vout ) +{ + assert( !p_vout->p->b_direct ); + + 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 int ReduceHeight( int i_ratio ) { int i_dummy = VOUT_ASPECT_FACTOR; @@ -1331,103 +1524,55 @@ static void AspectRatio( int i_aspect, int *i_aspect_x, int *i_aspect_y ) *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 ) -{ - int i_log = 0; - - if( i == 0 ) return -31337; - - 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; - - return i_log; -} - -/***************************************************************************** - * MaskToShift: transform a color mask into right and left shifts - ***************************************************************************** - * This function is used for obtaining color shifts from masks. - *****************************************************************************/ -static void MaskToShift( int *pi_left, int *pi_right, uint32_t i_mask ) +/** + * 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 ) { - uint32_t i_low, i_high; /* lower hand higher bits of the mask */ - - if( !i_mask ) - { - *pi_left = *pi_right = 0; - return; - } - - /* Get bits */ - i_low = i_high = i_mask; - - i_low &= - (int32_t)i_low; /* lower bit of the mask */ - i_high += i_low; /* higher bit of the mask */ - - /* 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; - - /* Update pointers and return */ - *pi_left = i_low; - *pi_right = (8 - i_high + i_low); + 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; } -/***************************************************************************** - * vout_VarCallback: generic callback for intf variables - *****************************************************************************/ -int vout_VarCallback( vlc_object_t * p_this, const char * psz_variable, - vlc_value_t oldval, vlc_value_t newval, - void *p_data ) +/** + * 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 ) { - vout_thread_t * p_vout = (vout_thread_t *)p_this; - vlc_value_t val; - (void)psz_variable; (void)newval; (void)oldval; (void)p_data; - val.b_bool = true; - var_Set( p_vout, "intf-change", val ); - return VLC_SUCCESS; + 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; } -/***************************************************************************** - * Helper thread for object variables callbacks. - * Only used to avoid deadlocks when using the video embedded mode. - *****************************************************************************/ -typedef struct suxor_thread_t -{ - VLC_COMMON_MEMBERS - input_thread_t *p_input; - -} suxor_thread_t; - -static void SuxorRestartVideoES( suxor_thread_t *p_this ) +/** + * This function computes rgb shifts from masks + */ +static void PictureHeapFixRgb( picture_heap_t *p_heap ) { - vlc_value_t val; - - vlc_thread_ready( p_this ); + video_format_t fmt; - /* Now restart current video stream */ - var_Get( p_this->p_input, "video-es", &val ); - if( val.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 ); - } + /* */ + fmt.i_chroma = p_heap->i_chroma; + VideoFormatImportRgb( &fmt, p_heap ); - vlc_object_release( p_this->p_input ); + /* */ + video_format_FixRgb( &fmt ); - vlc_object_release( p_this ); + VideoFormatExportRgb( &fmt, p_heap ); } /***************************************************************************** @@ -1462,13 +1607,21 @@ static int DeinterlaceCallback( vlc_object_t *p_this, char const *psz_cmd, { psz_filter = realloc( psz_filter, strlen( psz_filter ) + sizeof(":deinterlace") ); - if( psz_filter && *psz_filter ) strcat( psz_filter, ":" ); - strcat( psz_filter, "deinterlace" ); + if( psz_filter ) + { + if( *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( !p_input ) + { + free( psz_filter ); + return VLC_EGENERIC; + } if( psz_mode && *psz_mode ) { @@ -1515,18 +1668,7 @@ static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd, var_Set( p_input, "vout-filter", val ); /* Now restart current video stream */ - var_Get( p_input, "video-es", &val ); - if( val.i_int >= 0 ) - { - 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 = true; - vlc_object_yield( p_input ); - vlc_thread_create( p_suxor, "suxor", SuxorRestartVideoES, - VLC_THREAD_PRIORITY_LOW, false ); - } - + input_Control( p_input, INPUT_RESTART_ES, -VIDEO_ES ); vlc_object_release( p_input ); return VLC_SUCCESS; @@ -1535,89 +1677,32 @@ static int FilterCallback( vlc_object_t *p_this, char const *psz_cmd, /***************************************************************************** * 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++ ) - { - struct config_chain_t *p_cfg = - p_vout->p_vfilters_cfg[p_vout->i_vfilters_cfg]; - config_ChainDestroy( p_cfg ); - free( p_vout->psz_vfilters[p_vout->i_vfilters_cfg] ); - p_vout->psz_vfilters[p_vout->i_vfilters_cfg] = NULL; - } - p_vout->i_vfilters_cfg = 0; - if( psz_vfilters && *psz_vfilters ) - { - char *psz_parser = psz_vfilters; - - 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; - } - } - } - } - 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 ) { 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 = true; - vlc_mutex_unlock( &p_vout->vfilter_lock ); + 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; } -static void RemoveVideoFilters2( vout_thread_t *p_vout ) -{ - int i; - for( i = 0; i < p_vout->i_vfilters; i++ ) - { - 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 ); - } - - free( p_vout->pp_vfilters[i]->p_owner ); - vlc_object_release( p_vout->pp_vfilters[i] ); - } - p_vout->i_vfilters = 0; -} - static void DisplayTitleOnOSD( vout_thread_t *p_vout ) { input_thread_t *p_input; mtime_t i_now, i_stop; + if( !config_GetInt( p_vout, "osd" ) ) return; + p_input = (input_thread_t *)vlc_object_find( p_vout, VLC_OBJECT_INPUT, FIND_ANYWHERE ); if( p_input ) { i_now = mdate(); - i_stop = i_now + (mtime_t)(p_vout->i_title_timeout * 1000); + i_stop = i_now + (mtime_t)(p_vout->p->i_title_timeout * 1000); char *psz_nowplaying = input_item_GetNowPlaying( input_GetItem( p_input ) ); char *psz_artist = input_item_GetArtist( input_GetItem( p_input ) ); @@ -1631,7 +1716,7 @@ static void DisplayTitleOnOSD( vout_thread_t *p_vout ) { vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, psz_nowplaying, NULL, - p_vout->i_title_position, + 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, @@ -1645,7 +1730,7 @@ static void DisplayTitleOnOSD( vout_thread_t *p_vout ) { vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, psz_string, NULL, - p_vout->i_title_position, + 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, @@ -1658,7 +1743,7 @@ static void DisplayTitleOnOSD( vout_thread_t *p_vout ) { vout_ShowTextAbsolute( p_vout, DEFAULT_CHAN, psz_name, NULL, - p_vout->i_title_position, + 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,