X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fvideo_filter%2Fmotiondetect.c;h=79946d989a678581cd66c165711ecf47bbd9b8fb;hb=5d51dfe0711b6c1539e39fe43995875a4eff3f78;hp=37abd9ce5c3375b44b521b1fe9b176e1d42a5ca6;hpb=543574fd1d30e5be90ec4adcc9d15046c90af350;p=vlc diff --git a/modules/video_filter/motiondetect.c b/modules/video_filter/motiondetect.c index 37abd9ce5c..79946d989a 100644 --- a/modules/video_filter/motiondetect.c +++ b/modules/video_filter/motiondetect.c @@ -1,10 +1,10 @@ /***************************************************************************** - * motiondetect.c : Motion detect video effect plugin for vlc + * motiondetec.c : Second version of a motion detection plugin. ***************************************************************************** - * Copyright (C) 2005 the VideoLAN team + * Copyright (C) 2000-2008 the VideoLAN team * $Id$ * - * Authors: Jérôme Decoodt + * Authors: Antoine Cellerier * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,425 +24,574 @@ /***************************************************************************** * Preamble *****************************************************************************/ -#include /* malloc(), free() */ -#include -#include -#include -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif -#include "filter_common.h" +#include +#include +#include + +#include +#include "filter_picture.h" /***************************************************************************** - * Local prototypes + * Module descriptor *****************************************************************************/ static int Create ( vlc_object_t * ); static void Destroy ( vlc_object_t * ); -static int Init ( vout_thread_t * ); -static void End ( vout_thread_t * ); -static void Render ( vout_thread_t *, picture_t * ); -static void MotionDetect( vout_thread_t *p_vout, picture_t *p_inpic, - picture_t *p_outpic ); +#define FILTER_PREFIX "motiondetect-" + +vlc_module_begin () + set_description( N_("Motion detect video filter") ) + set_shortname( N_( "Motion Detect" )) + set_category( CAT_VIDEO ) + set_subcategory( SUBCAT_VIDEO_VFILTER ) + set_capability( "video filter2", 0 ) + + add_shortcut( "motion" ) + set_callbacks( Create, Destroy ) +vlc_module_end () -static int SendEvents ( vlc_object_t *, char const *, - vlc_value_t, vlc_value_t, void * ); /***************************************************************************** - * Module descriptor + * Local prototypes *****************************************************************************/ -#define DESC_TEXT N_("Description file") -#define DESC_LONGTEXT N_("Description file, file containing simple playlist") -#define HISTORY_TEXT N_("History parameter") -#define HISTORY_LONGTEXT N_("History parameter, number of frames used for detection") - -vlc_module_begin(); - set_description( _("Motion detect video filter") ); - set_shortname( N_( "Motion detect" )); - set_category( CAT_VIDEO ); - set_subcategory( SUBCAT_VIDEO_VFILTER ); - set_capability( "video filter", 0 ); - - add_integer( "motiondetect-history", 1, NULL, HISTORY_TEXT, - HISTORY_LONGTEXT, VLC_FALSE ); - add_string( "motiondetect-description", "motiondetect", NULL, DESC_TEXT, - DESC_LONGTEXT, VLC_FALSE ); - - set_callbacks( Create, Destroy ); -vlc_module_end(); +static picture_t *Filter( filter_t *, picture_t * ); +static picture_t *FilterPacked( filter_t *, picture_t * ); +static void GaussianConvolution( uint32_t *, uint32_t *, int, int, int ); +static int FindShapes( uint32_t *, uint32_t *, int, int, int, + int *, int *, int *, int *, int *); +static void Draw( filter_t *p_filter, uint8_t *p_pix, int i_pix_pitch, int i_pix_size ); +#define NUM_COLORS (5000) + +struct filter_sys_t +{ + bool b_old; + picture_t *p_old; + uint32_t *p_buf; + uint32_t *p_buf2; + + /* */ + int i_colors; + int colors[NUM_COLORS]; + int color_x_min[NUM_COLORS]; + int color_x_max[NUM_COLORS]; + int color_y_min[NUM_COLORS]; + int color_y_max[NUM_COLORS]; +}; /***************************************************************************** - * vout_sys_t: Motion detect video output method descriptor - ***************************************************************************** - * This structure is part of the video output thread descriptor. - * It describes the Motion detect specific properties of an output thread. + * Create *****************************************************************************/ -typedef struct area_t -{ - int i_x1, i_y1; - int i_x2, i_y2; - int i_matches; - int i_level; - int i_downspeed, i_upspeed; - char *psz_mrl; -} area_t; - -struct vout_sys_t +static int Create( vlc_object_t *p_this ) { - vout_thread_t *p_vout; - playlist_t *p_playlist; - - uint8_t *p_bufferY; - int i_stack; - area_t** pp_areas; - int i_areas; - int i_history; -}; + filter_t *p_filter = (filter_t *)p_this; + const video_format_t *p_fmt = &p_filter->fmt_in.video; + filter_sys_t *p_sys; + + switch( p_fmt->i_chroma ) + { + CASE_PLANAR_YUV + p_filter->pf_video_filter = Filter; + break; + + CASE_PACKED_YUV_422 + p_filter->pf_video_filter = FilterPacked; + break; + + default: + msg_Err( p_filter, "Unsupported input chroma (%4.4s)", + (char*)&(p_fmt->i_chroma) ); + return VLC_EGENERIC; + } + + /* Allocate structure */ + p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) ); + if( p_filter->p_sys == NULL ) + return VLC_ENOMEM; + + p_sys->b_old = false; + p_sys->p_old = picture_NewFromFormat( p_fmt ); + p_sys->p_buf = calloc( p_fmt->i_width * p_fmt->i_height, sizeof(*p_sys->p_buf) ); + p_sys->p_buf2 = calloc( p_fmt->i_width * p_fmt->i_height, sizeof(*p_sys->p_buf) ); + + if( !p_sys->p_old || !p_sys->p_buf || !p_sys->p_buf2 ) + { + free( p_sys->p_buf2 ); + free( p_sys->p_buf ); + if( p_sys->p_old ) + picture_Release( p_sys->p_old ); + return VLC_ENOMEM; + } + + return VLC_SUCCESS; +} /***************************************************************************** - * Control: control facility for the vout (forwards to child vout) + * Destroy *****************************************************************************/ -static int Control( vout_thread_t *p_vout, int i_query, va_list args ) +static void Destroy( vlc_object_t *p_this ) { - return vout_vaControl( p_vout->p_sys->p_vout, i_query, args ); + filter_t *p_filter = (filter_t *)p_this; + filter_sys_t *p_sys = p_filter->p_sys; + + free( p_sys->p_buf2 ); + free( p_sys->p_buf ); + picture_Release( p_sys->p_old ); + free( p_sys ); } + /***************************************************************************** - * Create: allocates Distort video thread output method - ***************************************************************************** - * This function allocates and initializes a Distort vout method. + * Filter YUV Planar *****************************************************************************/ -static int Create( vlc_object_t *p_this ) +static picture_t *Filter( filter_t *p_filter, picture_t *p_inpic ) { - vout_thread_t *p_vout = (vout_thread_t *)p_this; - char *psz_descfilename; - char buffer[256]; - int x1, x2, y1, y2, i_level, i_downspeed, i_upspeed, i; - area_t *p_area; - FILE * p_file; + filter_sys_t *p_sys = p_filter->p_sys; + const video_format_t *p_fmt = &p_filter->fmt_in.video; - /* Allocate structure */ - p_vout->p_sys = malloc( sizeof( vout_sys_t ) ); - if( p_vout->p_sys == NULL ) - { - msg_Err( p_vout, "out of memory" ); - return VLC_ENOMEM; - } + picture_t *p_outpic; + + uint8_t *p_oldpix = p_sys->p_old->p[Y_PLANE].p_pixels; + const int i_old_pitch = p_sys->p_old->p[Y_PLANE].i_pitch; + uint32_t *p_buf = p_sys->p_buf; + uint32_t *p_buf2= p_sys->p_buf2; - p_vout->pf_init = Init; - p_vout->pf_end = End; - p_vout->pf_manage = NULL; - p_vout->pf_render = Render; - p_vout->pf_display = NULL; - p_vout->pf_control = Control; + unsigned x, y; - memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) ); + if( !p_inpic ) + return NULL; - p_vout->p_sys->i_history = config_GetInt( p_vout, - "motiondetect-history" ); + const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels; + const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch; - if( !(psz_descfilename = config_GetPsz( p_vout, - "motiondetect-description" ) ) ) + if( !p_sys->b_old ) { - free( p_vout->p_sys ); - return VLC_EGENERIC; + picture_Copy( p_sys->p_old, p_inpic ); + p_sys->b_old = true; + return p_inpic; } - p_vout->p_sys->p_playlist = vlc_object_find( p_this, VLC_OBJECT_PLAYLIST, - FIND_ANYWHERE ); - if( !p_vout->p_sys->p_playlist ) + p_outpic = filter_NewPicture( p_filter ); + if( !p_outpic ) { - msg_Err( p_vout, "playlist not found" ); - free( p_vout->p_sys ); - return VLC_EGENERIC; + picture_Release( p_inpic ); + return NULL; } + picture_Copy( p_outpic, p_inpic ); - /* Parse description file and allocate areas */ - p_file = utf8_fopen( psz_descfilename, "r" ); - if( !p_file ) + /** + * Substract Y planes + */ + for( y = 0; y < p_fmt->i_height; y++ ) { - msg_Err( p_this, "Failed to open descritpion file %s", - psz_descfilename ); - free( psz_descfilename ); - free( p_vout->p_sys ); - return VLC_EGENERIC; + for( x = 0; x < p_fmt->i_width; x++ ) + p_buf2[y*p_fmt->i_width+x] = abs( p_inpix[y*i_src_pitch+x] - p_oldpix[y*i_old_pitch+x] ); } - p_vout->p_sys->i_areas = 0; - while( fscanf( p_file, "%d,%d,%d,%d,%d,%d,%d,", - &x1, &y1, &x2, &y2, &i_level, - &i_downspeed, &i_upspeed ) == 7 ) + + int i_chroma_dx; + int i_chroma_dy; + switch( p_inpic->format.i_chroma ) { - for( i = 0 ; i < 255 ; i++ ) - { - fread( buffer + i, 1, 1, p_file ); - if( *( buffer + i ) == '\n' ) - break; - } - *( buffer + i ) = 0; - p_vout->p_sys->i_areas++; - p_vout->p_sys->pp_areas = realloc( p_vout->p_sys->pp_areas, - p_vout->p_sys->i_areas * - sizeof( area_t ) ); - if( !p_vout->p_sys->pp_areas ) - /*FIXME: clean this... */ - return VLC_ENOMEM; - p_area = malloc( sizeof( area_t ) ); - if( !p_area ) + case VLC_CODEC_I420: + case VLC_CODEC_J420: + case VLC_CODEC_YV12: + i_chroma_dx = 2; + i_chroma_dy = 2; break; - p_area->i_x1 = x1; - p_area->i_x2 = x2; - p_area->i_y1 = y1; - p_area->i_y2 = y2; - p_area->i_matches = 0; - p_area->i_level = i_level; - p_area->i_downspeed = i_downspeed; - p_area->i_upspeed = i_upspeed; - - p_area->psz_mrl = strdup(buffer); - p_vout->p_sys->pp_areas[p_vout->p_sys->i_areas-1] = p_area; - } - fclose( p_file ); - return VLC_SUCCESS; -} - -/***************************************************************************** - * Init: initialize Motion detect video thread output method - *****************************************************************************/ -static int Init( vout_thread_t *p_vout ) -{ - int i_index; - picture_t *p_pic; - video_format_t fmt = {0}; - - I_OUTPUTPICTURES = 0; - - /* Initialize the output structure */ - p_vout->output.i_chroma = p_vout->render.i_chroma; - p_vout->output.i_width = p_vout->render.i_width; - p_vout->output.i_height = p_vout->render.i_height; - p_vout->output.i_aspect = p_vout->render.i_aspect; - p_vout->fmt_out = p_vout->fmt_in; - fmt = p_vout->fmt_out; - - /* Try to open the real video output */ - msg_Dbg( p_vout, "spawning the real video output" ); + case VLC_CODEC_I422: + case VLC_CODEC_J422: + i_chroma_dx = 2; + i_chroma_dy = 1; + break; - p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt ); + default: + msg_Warn( p_filter, "Not taking chroma into account" ); + i_chroma_dx = 0; + i_chroma_dy = 0; + break; + } - /* Everything failed */ - if( p_vout->p_sys->p_vout == NULL ) + if( i_chroma_dx != 0 && i_chroma_dy != 0 ) { - msg_Err( p_vout, "cannot open vout, aborting" ); - return VLC_EGENERIC; - } + const uint8_t *p_inpix_u = p_inpic->p[U_PLANE].p_pixels; + const uint8_t *p_inpix_v = p_inpic->p[V_PLANE].p_pixels; + const int i_src_pitch_u = p_inpic->p[U_PLANE].i_pitch; + const int i_src_pitch_v = p_inpic->p[V_PLANE].i_pitch; - ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES ); + const uint8_t *p_oldpix_u = p_sys->p_old->p[U_PLANE].p_pixels; + const uint8_t *p_oldpix_v = p_sys->p_old->p[V_PLANE].p_pixels; + const int i_old_pitch_u = p_sys->p_old->p[U_PLANE].i_pitch; + const int i_old_pitch_v = p_sys->p_old->p[V_PLANE].i_pitch; - ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents ); + for( y = 0; y < p_fmt->i_height/i_chroma_dy; y++ ) + { + for( x = 0; x < p_fmt->i_width/i_chroma_dx; x ++ ) + { + const int d = abs( p_inpix_u[y*i_src_pitch_u+x] - p_oldpix_u[y*i_old_pitch_u+x] ) + + abs( p_inpix_v[y*i_src_pitch_v+x] - p_oldpix_v[y*i_old_pitch_v+x] ); + int i, j; - ADD_PARENT_CALLBACKS( SendEventsToChild ); + for( j = 0; j < i_chroma_dy; j++ ) + { + for( i = 0; i < i_chroma_dx; i++ ) + p_buf2[i_chroma_dy*p_fmt->i_width*j + i_chroma_dx*i] = d; + } + } + } + } - return VLC_SUCCESS; + /** + * Get the areas where movement was detected + */ + p_sys->i_colors = FindShapes( p_buf2, p_buf, p_fmt->i_width, p_fmt->i_width, p_fmt->i_height, + p_sys->colors, p_sys->color_x_min, p_sys->color_x_max, p_sys->color_y_min, p_sys->color_y_max ); + + /** + * Count final number of shapes + * Draw rectangles (there can be more than 1 moving shape in 1 rectangle) + */ + Draw( p_filter, p_outpic->p[Y_PLANE].p_pixels, p_outpic->p[Y_PLANE].i_pitch, 1 ); + + /** + * We're done. Lets keep a copy of the picture + * TODO we may just picture_Release with a latency of 1 if the filters/vout + * handle it correctly */ + picture_Copy( p_sys->p_old, p_inpic ); + + picture_Release( p_inpic ); + return p_outpic; } /***************************************************************************** - * End: terminate Motion detect video thread output method + * Filter YUV Packed *****************************************************************************/ -static void End( vout_thread_t *p_vout ) +static picture_t *FilterPacked( filter_t *p_filter, picture_t *p_inpic ) { - int i_index; + filter_sys_t *p_sys = p_filter->p_sys; + const video_format_t *p_fmt = &p_filter->fmt_in.video; + picture_t *p_outpic; + + const uint8_t *p_inpix = p_inpic->p[Y_PLANE].p_pixels; + const int i_src_pitch = p_inpic->p[Y_PLANE].i_pitch; - /* Free the fake output buffers we allocated */ - for( i_index = I_OUTPUTPICTURES ; i_index ; ) + uint8_t *p_oldpix = p_sys->p_old->p[Y_PLANE].p_pixels; + const int i_old_pitch = p_sys->p_old->p[Y_PLANE].i_pitch; + uint32_t *p_buf = p_sys->p_buf; + uint32_t *p_buf2= p_sys->p_buf2; + + int i_y_offset, i_u_offset, i_v_offset; + + unsigned x, y; + + if( GetPackedYuvOffsets( p_fmt->i_chroma, + &i_y_offset, &i_u_offset, &i_v_offset ) ) { - i_index--; - free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig ); + msg_Warn( p_filter, "Unsupported input chroma (%4.4s)", + (char*)&p_fmt->i_chroma ); + return p_inpic; } -} -/***************************************************************************** - * Destroy: destroy Motion detect video thread output method - ***************************************************************************** - * Terminate an output method created by DistortCreateOutputMethod - *****************************************************************************/ -static void Destroy( vlc_object_t *p_this ) -{ - vout_thread_t *p_vout = (vout_thread_t *)p_this; - int i; + if( !p_sys->b_old ) + { + picture_Copy( p_sys->p_old, p_inpic ); + p_sys->b_old = true; + return p_inpic; + } - if( p_vout->p_sys->p_vout ) + p_outpic = filter_NewPicture( p_filter ); + if( !p_outpic ) { - DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents ); - vlc_object_detach( p_vout->p_sys->p_vout ); - vout_Destroy( p_vout->p_sys->p_vout ); + picture_Release( p_inpic ); + return NULL; } + picture_Copy( p_outpic, p_inpic ); - DEL_PARENT_CALLBACKS( SendEventsToChild ); + /* Substract all planes at once */ - for( i = 0 ; i < p_vout->p_sys->i_areas ; i++ ) + for( y = 0; y < p_fmt->i_height; y++ ) { - free( p_vout->p_sys->pp_areas[i]->psz_mrl ); - free( p_vout->p_sys->pp_areas[i] ); + for( x = 0; x < p_fmt->i_width; x+=2 ) + { + int i; + int d; + + d = abs( p_inpix[y*i_src_pitch+2*x+i_u_offset] - p_oldpix[y*i_old_pitch+2*x+i_u_offset] ) + + abs( p_inpix[y*i_src_pitch+2*x+i_v_offset] - p_oldpix[y*i_old_pitch+2*x+i_v_offset] ); + + for( i = 0; i < 2; i++ ) + p_buf2[y*p_fmt->i_width+x+i] = + abs( p_inpix[y*i_src_pitch+2*(x+i)+i_y_offset] - p_oldpix[y*i_old_pitch+2*(x+i)+i_y_offset] ) + d; + } } - free( p_vout->p_sys->pp_areas ); - free( p_vout->p_sys ); + /** + * Get the areas where movement was detected + */ + p_sys->i_colors = FindShapes( p_buf2, p_buf, p_fmt->i_width, p_fmt->i_width, p_fmt->i_height, + p_sys->colors, p_sys->color_x_min, p_sys->color_x_max, p_sys->color_y_min, p_sys->color_y_max ); + + /** + * Count final number of shapes + * Draw rectangles (there can be more than 1 moving shape in 1 rectangle) + */ + Draw( p_filter, &p_outpic->p[Y_PLANE].p_pixels[i_y_offset], p_outpic->p[Y_PLANE].i_pitch, 2 ); + + /** + * We're done. Lets keep a copy of the picture + * TODO we may just picture_Release with a latency of 1 if the filters/vout + * handle it correctly */ + picture_Copy( p_sys->p_old, p_inpic ); + + picture_Release( p_inpic ); + return p_outpic; } + + /***************************************************************************** - * Render: displays previously rendered output + * Gaussian Convolution ***************************************************************************** - * This function send the currently rendered image to Distort image, waits - * until it is displayed and switch the two rendering buffers, preparing next - * frame. + * Gaussian convolution ( sigma == 1.4 ) + * + * | 2 4 5 4 2 | | 2 4 4 4 2 | + * | 4 9 12 9 4 | | 4 8 12 8 4 | + * | 5 12 15 12 5 | ~ | 4 12 16 12 4 | + * | 4 9 12 9 4 | | 4 8 12 8 4 | + * | 2 4 5 4 2 | | 2 4 4 4 2 | *****************************************************************************/ -static void Render( vout_thread_t *p_vout, picture_t *p_pic ) +static void GaussianConvolution( uint32_t *p_inpix, uint32_t *p_smooth, + int i_src_pitch, int i_num_lines, + int i_src_visible ) { - picture_t *p_outpic; + int x,y; - /* This is a new frame. Get a structure from the video_output. */ - while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) ) - == NULL ) + /* A bit overkill but ... simpler */ + memset( p_smooth, 0, sizeof(*p_smooth) * i_src_pitch * i_num_lines ); + + for( y = 2; y < i_num_lines - 2; y++ ) { - if( p_vout->b_die || p_vout->b_error ) + for( x = 2; x < i_src_visible - 2; x++ ) { - return; + p_smooth[y*i_src_visible+x] = (uint32_t)( + /* 2 rows up */ + ( p_inpix[(y-2)*i_src_pitch+x-2] ) + + ((p_inpix[(y-2)*i_src_pitch+x-1] + + p_inpix[(y-2)*i_src_pitch+x] + + p_inpix[(y-2)*i_src_pitch+x+1])<<1 ) + + ( p_inpix[(y-2)*i_src_pitch+x+2] ) + /* 1 row up */ + + ((p_inpix[(y-1)*i_src_pitch+x-2] + + ( p_inpix[(y-1)*i_src_pitch+x-1]<<1 ) + + ( p_inpix[(y-1)*i_src_pitch+x]*3 ) + + ( p_inpix[(y-1)*i_src_pitch+x+1]<<1 ) + + p_inpix[(y-1)*i_src_pitch+x+2] + /* */ + + p_inpix[y*i_src_pitch+x-2] + + ( p_inpix[y*i_src_pitch+x-1]*3 ) + + ( p_inpix[y*i_src_pitch+x]<<2 ) + + ( p_inpix[y*i_src_pitch+x+1]*3 ) + + p_inpix[y*i_src_pitch+x+2] + /* 1 row down */ + + p_inpix[(y+1)*i_src_pitch+x-2] + + ( p_inpix[(y+1)*i_src_pitch+x-1]<<1 ) + + ( p_inpix[(y+1)*i_src_pitch+x]*3 ) + + ( p_inpix[(y+1)*i_src_pitch+x+1]<<1 ) + + p_inpix[(y+1)*i_src_pitch+x+2] )<<1 ) + /* 2 rows down */ + + ( p_inpix[(y+2)*i_src_pitch+x-2] ) + + ((p_inpix[(y+2)*i_src_pitch+x-1] + + p_inpix[(y+2)*i_src_pitch+x] + + p_inpix[(y+2)*i_src_pitch+x+1])<<1 ) + + ( p_inpix[(y+2)*i_src_pitch+x+2] ) + ) >> 6 /* 115 */; } - msleep( VOUT_OUTMEM_SLEEP ); } - - vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date ); - - MotionDetect( p_vout, p_pic, p_outpic ); - - vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic ); } /***************************************************************************** - * MotionDetect: calculates new matches + * *****************************************************************************/ -static void MotionDetect( vout_thread_t *p_vout, picture_t *p_inpic, - picture_t *p_outpic ) +static int FindShapes( uint32_t *p_diff, uint32_t *p_smooth, + int i_pitch, int i_visible, int i_lines, + int *colors, + int *color_x_min, int *color_x_max, + int *color_y_min, int *color_y_max ) { -#define pp_curent_area p_vout->p_sys->pp_areas[i_area] - - int i_index, i_index_col, i_area; - for( i_index = 0 ; i_index < p_inpic->i_planes ; i_index++ ) + int last = 1; + int i, j; + + /** + * Apply some smoothing to remove noise + */ + GaussianConvolution( p_diff, p_smooth, i_pitch, i_lines, i_visible ); + + /** + * Label the shapes and build the labels dependencies list + */ + for( j = 0; j < i_pitch; j++ ) { - int i_line, i_num_lines, i_offset, i_size, i_diff; - uint8_t *p_in, *p_out, *p_last_in, *p_buffer; - - p_in = p_inpic->p[i_index].p_pixels; - p_out = p_outpic->p[i_index].p_pixels; - - i_num_lines = p_inpic->p[i_index].i_visible_lines; - i_size = p_inpic->p[i_index].i_lines * p_inpic->p[i_index].i_pitch; - - p_vout->p_vlc->pf_memcpy( p_out, p_in, i_size ); - switch( i_index ) + p_smooth[j] = 0; + p_smooth[(i_lines-1)*i_pitch+j] = 0; + } + for( i = 1; i < i_lines-1; i++ ) + { + p_smooth[i*i_pitch] = 0; + for( j = 1; j < i_pitch-1; j++ ) { - case Y_PLANE: - p_buffer = p_vout->p_sys->p_bufferY; - - if( p_buffer == NULL) + if( p_smooth[i*i_pitch+j] > 15 ) { - p_buffer = malloc( p_vout->p_sys->i_history * i_size); - memset( p_buffer, 0, p_vout->p_sys->i_history * i_size ); - - p_vout->p_sys->p_bufferY = p_buffer; - p_vout->p_sys->i_stack = 0; - } - - i_offset = i_size * p_vout->p_sys->i_stack; - p_last_in = p_buffer + i_offset; - for( i_area = 0 ; i_area < p_vout->p_sys->i_areas ; i_area++) - { - int i_tmp = 0, i_nb_pixels; - p_last_in = p_buffer + i_offset; - p_in = p_inpic->p[i_index].p_pixels; - p_out = p_outpic->p[i_index].p_pixels; - if( ( pp_curent_area->i_y1 > p_inpic->p[i_index].i_lines ) || - ( pp_curent_area->i_x1 > p_inpic->p[i_index].i_pitch ) ) - continue; - if( ( pp_curent_area->i_y2 > p_inpic->p[i_index].i_lines ) ) - pp_curent_area->i_y2 = p_inpic->p[i_index].i_lines; - if( ( pp_curent_area->i_x2 > p_inpic->p[i_index].i_pitch ) ) - pp_curent_area->i_x2 = p_inpic->p[i_index].i_pitch; - - for( i_line = pp_curent_area->i_y1 ; - i_line < pp_curent_area->i_y2 ; i_line++ ) + if( p_smooth[(i-1)*i_pitch+j-1] ) { - for( i_index_col = pp_curent_area->i_x1 ; - i_index_col < pp_curent_area->i_x2 ; i_index_col++ ) + p_smooth[i*i_pitch+j] = p_smooth[(i-1)*i_pitch+j-1]; + } + else if( p_smooth[(i-1)*i_pitch+j] ) + p_smooth[i*i_pitch+j] = p_smooth[(i-1)*i_pitch+j]; + else if( p_smooth[i*i_pitch+j-1] ) + p_smooth[i*i_pitch+j] = p_smooth[i*i_pitch+j-1]; + else + { + if( last < NUM_COLORS ) { - i_diff = (*(p_last_in + i_index_col + - i_line * p_inpic->p[i_index].i_pitch) - - *(p_in + i_index_col + - i_line * p_inpic->p[i_index].i_pitch)); - if( i_diff < 0) - i_diff = -i_diff; - - if( i_diff > pp_curent_area->i_level ) - i_tmp += pp_curent_area->i_upspeed; - - *( p_out + i_index_col + i_line * - p_inpic->p[i_index].i_pitch ) = - pp_curent_area->i_matches; + p_smooth[i*i_pitch+j] = last; + colors[last] = last; + last++; } } - i_nb_pixels = ( pp_curent_area->i_y2 - pp_curent_area->i_y1 ) * - ( pp_curent_area->i_x2 - pp_curent_area->i_x1 ); - pp_curent_area->i_matches += i_tmp / i_nb_pixels - - pp_curent_area->i_downspeed; - if( pp_curent_area->i_matches < 0) - pp_curent_area->i_matches = 0; - if( pp_curent_area->i_matches > 255) - { - playlist_item_t *p_item = playlist_ItemNew( p_vout, - (const char*)pp_curent_area->psz_mrl, - pp_curent_area->psz_mrl ); - msg_Dbg( p_vout, "Area(%d) matched, going to %s\n", i_area, - pp_curent_area->psz_mrl ); - playlist_Control( p_vout->p_sys->p_playlist, - PLAYLIST_ITEMPLAY, p_item ); - pp_curent_area->i_matches = 0; + #define CHECK( A ) \ + if( p_smooth[A] && p_smooth[A] != p_smooth[i*i_pitch+j] ) \ + { \ + if( p_smooth[A] < p_smooth[i*i_pitch+j] ) \ + colors[p_smooth[i*i_pitch+j]] = p_smooth[A]; \ + else \ + colors[p_smooth[A]] = p_smooth[i*i_pitch+j]; \ } + CHECK( i*i_pitch+j-1 ); + CHECK( (i-1)*i_pitch+j-1 ); + CHECK( (i-1)*i_pitch+j ); + CHECK( (i-1)*i_pitch+j+1 ); + #undef CHECK } - p_last_in = p_buffer + i_offset; - p_in = p_inpic->p[i_index].p_pixels; - p_out = p_outpic->p[i_index].p_pixels; + else + { + p_smooth[i*i_pitch+j] = 0; + } + } + p_smooth[i*i_pitch+j] = 0; + } - p_vout->p_vlc->pf_memcpy( p_last_in, p_in, i_size ); - break; - default: - break; + /** + * Initialise empty rectangle list + */ + for( i = 1; i < last; i++ ) + { + color_x_min[i] = -1; + color_x_max[i] = -1; + color_y_min[i] = -1; + color_y_max[i] = -1; + } + + /** + * Compute rectangle coordinates + */ + for( i = 0; i < i_pitch * i_lines; i++ ) + { + if( p_smooth[i] ) + { + while( colors[p_smooth[i]] != (int)p_smooth[i] ) + p_smooth[i] = colors[p_smooth[i]]; + if( color_x_min[p_smooth[i]] == -1 ) + { + color_x_min[p_smooth[i]] = + color_x_max[p_smooth[i]] = i % i_pitch; + color_y_min[p_smooth[i]] = + color_y_max[p_smooth[i]] = i / i_pitch; + } + else + { + int x = i % i_pitch, y = i / i_pitch; + if( x < color_x_min[p_smooth[i]] ) + color_x_min[p_smooth[i]] = x; + if( x > color_x_max[p_smooth[i]] ) + color_x_max[p_smooth[i]] = x; + if( y < color_y_min[p_smooth[i]] ) + color_y_min[p_smooth[i]] = y; + if( y > color_y_max[p_smooth[i]] ) + color_y_max[p_smooth[i]] = y; + } } } - p_vout->p_sys->i_stack++; - if( p_vout->p_sys->i_stack >= p_vout->p_sys->i_history ) - p_vout->p_sys->i_stack = 0; -#undef pp_curent_area -} -/***************************************************************************** - * SendEvents: forward mouse and keyboard events to the parent p_vout - *****************************************************************************/ -static int SendEvents( vlc_object_t *p_this, char const *psz_var, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) -{ - var_Set( (vlc_object_t *)p_data, psz_var, newval ); + /** + * Merge overlaping rectangles + */ + for( i = 1; i < last; i++ ) + { + if( colors[i] != i ) continue; + if( color_x_min[i] == -1 ) continue; + for( j = i+1; j < last; j++ ) + { + if( colors[j] != j ) continue; + if( color_x_min[j] == -1 ) continue; + if( __MAX( color_x_min[i], color_x_min[j] ) < __MIN( color_x_max[i], color_x_max[j] ) && + __MAX( color_y_min[i], color_y_min[j] ) < __MIN( color_y_max[i], color_y_max[j] ) ) + { + color_x_min[i] = __MIN( color_x_min[i], color_x_min[j] ); + color_x_max[i] = __MAX( color_x_max[i], color_x_max[j] ); + color_y_min[i] = __MIN( color_y_min[i], color_y_min[j] ); + color_y_max[i] = __MAX( color_y_max[i], color_y_max[j] ); + color_x_min[j] = -1; + j = 0; + } + } + } - return VLC_SUCCESS; + return last; } -/***************************************************************************** - * SendEventsToChild: forward events to the child/children vout - *****************************************************************************/ -static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var, - vlc_value_t oldval, vlc_value_t newval, void *p_data ) +static void Draw( filter_t *p_filter, uint8_t *p_pix, int i_pix_pitch, int i_pix_size ) { - vout_thread_t *p_vout = (vout_thread_t *)p_this; - var_Set( p_vout->p_sys->p_vout, psz_var, newval ); - return VLC_SUCCESS; + filter_sys_t *p_sys = p_filter->p_sys; + int i, j; + + for( i = 1, j = 0; i < p_sys->i_colors; i++ ) + { + int x, y; + + if( p_sys->colors[i] != i ) + continue; + + const int color_x_min = p_sys->color_x_min[i]; + const int color_x_max = p_sys->color_x_max[i]; + const int color_y_min = p_sys->color_y_min[i]; + const int color_y_max = p_sys->color_y_max[i]; + + if( color_x_min == -1 ) + continue; + if( ( color_y_max - color_y_min ) * ( color_x_max - color_x_min ) < 16 ) + continue; + + j++; + + y = color_y_min; + for( x = color_x_min; x <= color_x_max; x++ ) + p_pix[y*i_pix_pitch+x*i_pix_size] = 0xff; + + y = color_y_max; + for( x = color_x_min; x <= color_x_max; x++ ) + p_pix[y*i_pix_pitch+x*i_pix_size] = 0xff; + + x = color_x_min; + for( y = color_y_min; y <= color_y_max; y++ ) + p_pix[y*i_pix_pitch+x*i_pix_size] = 0xff; + + x = color_x_max; + for( y = color_y_min; y <= color_y_max; y++ ) + p_pix[y*i_pix_pitch+x*i_pix_size] = 0xff; + } + msg_Dbg( p_filter, "Counted %d moving shapes.", j ); }