diff --git a/configure.ac b/configure.ac index b9bc30e..e7ed887 100644 --- a/configure.ac +++ b/configure.ac @@ -1129,6 +1129,8 @@ VLC_ADD_PLUGIN([ripple]) VLC_ADD_PLUGIN([psychedelic]) VLC_ADD_PLUGIN([gradient]) VLC_ADD_PLUGIN([motionblur]) +VLC_ADD_PLUGIN([staticdetect]) VLC_ADD_PLUGIN([rv32]) VLC_ADD_PLUGIN([rotate]) VLC_ADD_PLUGIN([noise]) diff --git a/modules/codec/avcodec/avcodec.c b/modules/codec/avcodec/avcodec.c index 3fa80a5..30dde7e 100644 --- a/modules/codec/avcodec/avcodec.c +++ b/modules/codec/avcodec/avcodec.c @@ -185,7 +185,9 @@ vlc_module_begin(); ENC_LUMA_ELIM_TEXT, ENC_LUMA_ELIM_LONGTEXT, true ); add_integer( ENC_CFG_PREFIX "chroma-elim-threshold", 0, NULL, ENC_CHROMA_ELIM_TEXT, ENC_CHROMA_ELIM_LONGTEXT, true ); - +//MOD FOR JPEG Q FACTOR + add_float_with_range( ENC_CFG_PREFIX "qcompress", 0.5, 0.0, 1.0, NULL, + ENC_QCOMPRESS_TEXT, NULL, true ); #if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4) /* Audio AAC encoder profile */ add_string( ENC_CFG_PREFIX "aac-profile", "main", NULL, diff --git a/modules/codec/avcodec/avcodec.h b/modules/codec/avcodec/avcodec.h index 591d125..0c57840 100644 --- a/modules/codec/avcodec/avcodec.h +++ b/modules/codec/avcodec/avcodec.h @@ -224,6 +224,9 @@ void EndAudioDec( decoder_t *p_dec ); "the PSNR isn't much changed (default: 0.0). The H264 specification " \ "recommends 7." ) +//MOD FOR JPEG Q FACTOR +#define ENC_QCOMPRESS_TEXT N_("Q Compress Value") + #if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4) #define ENC_PROFILE_TEXT N_( "Specify AAC audio profile to use" ) #define ENC_PROFILE_LONGTEXT N_( "Specify the AAC audio profile to use " \ diff --git a/modules/codec/avcodec/encoder.c b/modules/codec/avcodec/encoder.c index 95eef57..b314e75 100644 --- a/modules/codec/avcodec/encoder.c +++ b/modules/codec/avcodec/encoder.c @@ -143,6 +143,7 @@ struct encoder_sys_t int i_quality; /* for VBR */ float f_lumi_masking, f_dark_masking, f_p_masking, f_border_masking; int i_luma_elim, i_chroma_elim; + float f_qcompress ; /* MOD for JPEG QAULITY FACOR */ #if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4) int i_aac_profile; /* AAC profile to use.*/ #endif @@ -161,6 +162,7 @@ static const char *const ppsz_enc_options[] = { #if LIBAVCODEC_VERSION_INT >= ((51<<16)+(40<<8)+4) "aac-profile", #endif + "qcompress", /*MOD for JPEG Q*/ NULL }; @@ -423,7 +425,12 @@ int OpenEncoder( vlc_object_t *p_this ) /* Defaults from ffmpeg.c */ p_context->qblur = 0.5; - p_context->qcompress = 0.5; + //MOD for JPEG Q was 0.5 + var_Get( p_enc, ENC_CFG_PREFIX "qcompress", &val ); + if( val.f_float < 0.01 || val.f_float > 1.00 ) val.f_float = 0.5; + p_context->qcompress = val.f_float ; + msg_Dbg( p_enc, "JPEG Q Compress Value= %.2f", p_context->qcompress ); + p_context->b_quant_offset = 1.25; p_context->b_quant_factor = 1.25; p_context->i_quant_offset = 0.0; diff --git a/modules/video_filter/Modules.am b/modules/video_filter/Modules.am index 2268d9a..fb52578 100644 --- a/modules/video_filter/Modules.am +++ b/modules/video_filter/Modules.am @@ -44,4 +44,6 @@ SOURCES_chain = chain.c SOURCES_postproc = postproc.c SOURCES_swscale = swscale.c ../codec/avcodec/chroma.h SOURCES_imgresample = imgresample.c ../codec/avcodec/chroma.h +SOURCES_staticdetect = static_detect.c beep.c noinst_HEADERS = filter_common.h filter_picture.h diff --git a/modules/video_filter/beep.c b/modules/video_filter/beep.c new file mode 100644 index 0000000..fd469a4 --- /dev/null +++ b/modules/video_filter/beep.c @@ -0,0 +1,141 @@ +/* A Beep facility implemented on windows and linux */ +#include "beep.h" + +#ifdef __linux__ +/* beep - just what it sounds like, makes the console beep - but with + * precision control. See the man page for details. + * + * + * This code is copyright (C) Johnathan Nightingale, 2000. + * + * This code may distributed only under the terms of the GNU Public License + * which can be found at http://www.gnu.org/copyleft or in the file COPYING + * supplied with this code. + * + * This code is not distributed with warranties of any kind, including implied + * warranties of merchantability or fitness for a particular use or ability to + * breed pandas in captivity, it just can't be done. + * + * Bug me, I like it: http://johnath.com/ or johnath@johnath.com + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* I don't know where this number comes from, I admit that freely. A + wonderful human named Raine M. Ekman used it in a program that played + a tune at the console, and apparently, it's how the kernel likes its + sound requests to be phrased. If you see Raine, thank him for me. + + June 28, email from Peter Tirsek (peter at tirsek dot com): + + This number represents the fixed frequency of the original PC XT's + timer chip (the 8254 AFAIR), which is approximately 1.193 MHz. This + number is divided with the desired frequency to obtain a counter value, + that is subsequently fed into the timer chip, tied to the PC speaker. + The chip decreases this counter at every tick (1.193 MHz) and when it + reaches zero, it toggles the state of the speaker (on/off, or in/out), + resets the counter to the original value, and starts over. The end + result of this is a tone at approximately the desired frequency. :) +*/ +#ifndef CLOCK_TICK_RATE +#define CLOCK_TICK_RATE 1193180 +#endif + +#define VERSION_STRING "beep-1.2.2" + +/* Meaningful Defaults */ + +#define DEFAULT_REPS 1 +#define DEFAULT_DELAY 100 /* milliseconds */ + + +typedef struct beep_parms_t { + float freq; /* tone frequency (Hz) */ + int length; /* tone length (ms) */ + int reps; /* # of repetitions */ + int delay; /* delay between reps (ms) */ +} beep_parms_t; + +int play_beep(float freq, int length) { + + int i; /* loop counter */ + int console_fd; /* file descriptor */ + beep_parms_t parms; + + /* try to snag the console */ + if((console_fd = open("/dev/console", O_WRONLY)) == -1) { + //fprintf(stderr, "Could not open /dev/console for writing.\n"); + printf("\a"); /* Output the only beep we can, in an effort to fall back on usefulness */ + return -1; + } + + parms.freq = ( freq ? freq : DEFAULT_FREQ ); + parms.length = ( length ? length : DEFAULT_LENGTH ); + parms.reps = DEFAULT_REPS; + parms.delay = DEFAULT_DELAY; + + /* Beep */ + for (i = 0; i < parms.reps; i++) { /* start beep */ + if(ioctl(console_fd, KIOCSOUND, (int)(CLOCK_TICK_RATE/parms.freq)) < 0) { + printf("\a"); /* Output the only beep we can, in an effort to fall back on usefulness */ + return -2; + } + /* Look ma, I'm not ansi C compatible! */ + usleep(1000*parms.length); /* wait... */ + ioctl(console_fd, KIOCSOUND, 0); /* stop beep */ + if(i+1 < parms.reps) + usleep(1000*parms.delay); /* wait... */ + } + close(console_fd); + return 0; +} + +/* If we get interrupted, it would be nice to not leave the speaker beeping in + perpetuity. */ +/*void handle_signal(int signum) { + switch(signum) { + case SIGINT: + if(console_fd >= 0) { + // Kill the sound, quit gracefully + ioctl(console_fd, KIOCSOUND, 0); + close(console_fd); + exit(signum); + } else { + // Just quit gracefully + exit(signum); + } + } +}*/ + + +#elif defined WIN32 + +#include +int play_beep( float freq, int length) +{ + int freqi = (int) freq; + if ( !freq ) freq = DEFAULT_FREQ; + if ( !length ) length = DEFAULT_LENGTH; + if ( freqi > 32767 || freqi < 37 ) + return -10; + if ( Beep( freqi, length ) ) // error occured + return -11; + return 0; +} + +#else +/* Other Platforms should be quiet for now... */ +int play_beep( float freq, int length) +{ + return -40; +} +#endif diff --git a/modules/video_filter/beep.h b/modules/video_filter/beep.h new file mode 100644 index 0000000..e26cae5 --- /dev/null +++ b/modules/video_filter/beep.h @@ -0,0 +1,15 @@ +/* A Beep facility implemented on windows and linux */ +/* Prototype : we export a windows/linux beep function + arg1 frequency in Hz (notice that in windows the range is 37 to 32767 Hz) + arg2 beep duration in miliseconds + Return Value : 0 = success + < 0 = error + Also note that in linux if we get interrupted by signal and the program exits + the console may still beeping + + moded by basOS 2008 +*/ +int play_beep(float freq, int length); + +#define DEFAULT_FREQ 440.0 /* Middle A */ +#define DEFAULT_LENGTH 200 /* milliseconds */ diff --git a/modules/video_filter/static_detect.c b/modules/video_filter/static_detect.c new file mode 100644 index 0000000..30c0054 --- /dev/null +++ b/modules/video_filter/static_detect.c @@ -0,0 +1,827 @@ +/***************************************************************************** + * motion_blur.c : static detect filter for VLC + ***************************************************************************** + * Copyright (C) 2008 the VideoLAN team + * $Id$ + * + * Authors: basOS G + * based on the work of Antoine Cellerier for motion detect VLC VFilter + * + * 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 + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + *****************************************************************************/ + +/***************************************************************************** + * Preamble + *****************************************************************************/ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include "filter_picture.h" +#include "beep.h" +#include +//#include +//#include + + +/***************************************************************************** + * Help Data Types + *****************************************************************************/ +#undef NOIZ /* Should we compile a noise reduction filter ? Leave it out for now */ +/*difference frame data type: 16 bit for now to account for overflow (8 bit per channel + x 3 channels: max diff is 256x3 = 768 : we need more than 8 bits */ +typedef uint16_t diff_t; +typedef void ( *findDiffs_t )( diff_t*, picture_t*, picture_t*, filter_t * ); +typedef void ( *motionMeter_t )( float , float , picture_t* , filter_t* ); +//typedef void ( *markPicture_t )( diff_t*, picture_t*, diff_t , filter_t * p_filter ); + +/***************************************************************************** + * Local protypes + *****************************************************************************/ +static int Create ( vlc_object_t * ); +static void Destroy ( vlc_object_t * ); +static picture_t *Filter ( filter_t *, picture_t * ); +/***************************************************************************** + ** Some Predefined Protos + *****************************************************************************/ +void findDiffs (diff_t* p_diff, picture_t* p_new_pic, picture_t* p_old_pic, filter_t * p_filter ); +void findDiffsPacked (diff_t* p_diff, picture_t* p_new_pic, picture_t* p_old_pic, filter_t * p_filter); +#ifdef NOIZ +static void GaussianConvolution( diff_t *p_inpix, picture_t* p_ref_pic ); +#endif +float countMovedPixelsPercent( diff_t* p_diff, picture_t* p_ref_pic, diff_t i_motion_threshold ); +void motionMeter( float f_motion, float f_threshold, picture_t* p_out_pic, filter_t* p_filter ); +void motionMeterPacked( float f_motion, float f_threshold, picture_t* p_out_pic, filter_t* p_filter ); + +/*void markPicture( diff_t* p_diff, picture_t* p_out_pic, diff_t , filter_t * p_filter ); +void markPicturePacked( diff_t* p_diff, picture_t* p_out_pic, diff_t , filter_t * p_filter ); */ +//TODO :static int StaticDetectCallback( vlc_object_t *, char const *, +// vlc_value_t, vlc_value_t, void * ); + + +/***************************************************************************** + * Module descriptor + *****************************************************************************/ +#define MTHRESHOLD_TEXT N_("Motion Tolerance Percent") +#define MTHRESHOLD_LONGTEXT N_("Percentage of image pixels that have to be changed for motion to be detected.") +#define TTHRESHOLD_TEXT N_("Static Time needed for snapshot (mseconds)") +#define TTHRESHOLD_LONGTEXT N_("Amount of time in mseconds that video should remain unchanged for a snapshot to occur.") +#define DTHRESHOLD_TEXT N_("Motion Sensivity") +#define DTHRESHOLD_LONGTEXT N_("Amount of chroma and luminance difference for two pixels to be considered changed. You should mess with Motion Tolerance first if you want to tune motion detection system sensivity.") +#define FREEZE_TEXT N_("Snapshot Freeze Time (mseconds)") +#define FREEZE_LONGTEXT N_("How long should the snapped image stay freezed on the video. Use 0 to disable") +#define MOTIONM_TEXT N_("Display Motion meter") + +#define FILTER_PREFIX "staticdetect-" +#define PAR_MTHRESHOLD "tolerance" +#define PAR_TTHRESHOLD "snaptime" +#define PAR_DTHRESHOLD "sensivity" +#define PAR_FREEZETIME "freezetime" +#define PAR_MMETER "motionmeter" + +/* default difference threshold */ +#define DEFAULT_DIFF_THRESHOLD 9 +/* default time threshold */ +#define DEFAULT_TIME_THRESHOLD 700 +/* default pix count percent threshold */ +#define DEFAULT_PIX_COUNT_THRESHOLD 1.1 +/*default motion meter stat */ +#define DEFAULT_MMETER true +/* time the station mark should be presented (in msecs)*/ +#define DEFAULT_FREEZE_TIME 1200 + + +vlc_module_begin(); + set_shortname( N_("Static Image Detector") ); + set_description( N_("Detects stationary images from a video and takes snapshots of them") ); + set_capability( "video filter2", 0 ); + set_category( CAT_VIDEO ); + set_subcategory( SUBCAT_VIDEO_VFILTER ); + + add_float_with_range( FILTER_PREFIX PAR_MTHRESHOLD, DEFAULT_PIX_COUNT_THRESHOLD, 1, 100, NULL, + MTHRESHOLD_TEXT, MTHRESHOLD_LONGTEXT, false ); + add_integer ( FILTER_PREFIX PAR_TTHRESHOLD, DEFAULT_TIME_THRESHOLD, NULL, + TTHRESHOLD_TEXT, TTHRESHOLD_LONGTEXT, false ); + add_integer ( FILTER_PREFIX PAR_FREEZETIME, DEFAULT_FREEZE_TIME, NULL, + FREEZE_TEXT, FREEZE_LONGTEXT, false ); + add_integer_with_range ( FILTER_PREFIX PAR_DTHRESHOLD, DEFAULT_DIFF_THRESHOLD, 0, 768, NULL, + DTHRESHOLD_TEXT, DTHRESHOLD_LONGTEXT, false ); + add_bool ( FILTER_PREFIX PAR_MMETER, DEFAULT_MMETER, NULL, + MOTIONM_TEXT, NULL, false ); + add_shortcut( "static" ); + + set_callbacks( Create, Destroy ); +vlc_module_end(); + +//TOCHECK : WHAT is this for?? It parses command line args without it +static const char *const psz_filter_options[] = { + PAR_MTHRESHOLD, + PAR_TTHRESHOLD, + PAR_DTHRESHOLD, + PAR_FREEZETIME, + PAR_MMETER, + NULL +}; + + +/***************************************************************************** + * filter_sys_t + *****************************************************************************/ +struct filter_sys_t +{ + float f_pix_count_thres_percent ; /* motion threshold % pixels of an image */ + int i_frame_thres ; /* time threshold of stationary image at msecs */ + diff_t i_pix_diff_thres ;/* Difference of 2 pixels to mark it as changed */ + + bool b_is_snaped ; /* Is the picture snaped */ + int i_static_frames ; /* # of static frames */ + findDiffs_t pf_findDiffs ; /* findPixelDiffs: are we on a packed YCbCr data format? see www.fourcc.org */ + //markPicture_t pf_markPicture; /* mark Pixel Diffs */ + motionMeter_t pf_motionMeter; /* Draw a nice motion meter */ + picture_t* p_old_pic; /*previous frame */ + bool b_has_old_pic; + diff_t* p_diff; /*difference frame */ + picture_t* p_freeze_pic; /*difference frame for marking video output */ + int i_freeze_frames; /* # of frames used for marking */ + int i_freeze_counter; /* # of frames left to dispay the current differencies*/ + /* various motion stats */ + float f_motion_min; + float f_motion_max; + unsigned int i_motion_n; + float f_motion_avg; +}; + +/* Motion Statistics Handling */ +void resetStats( filter_t* p_filter ) +{ + p_filter->p_sys->i_motion_n = 0; + p_filter->p_sys->f_motion_max = 0 ; + p_filter->p_sys->f_motion_min = 100 ; + p_filter->p_sys->f_motion_avg = 0 ; +} +void echoStats( filter_t* p_filter ) +{ + msg_Dbg( p_filter, "Motion Stats:\n" + "[static]\tMinimum: %.4f\t Maximum: %.4f\tAverage: %.4f\tFrames:%d", + p_filter->p_sys->f_motion_min, p_filter->p_sys->f_motion_max, + p_filter->p_sys->f_motion_avg, p_filter->p_sys->i_motion_n ); +} + +/***************************************************************************** + ** Create + *****************************************************************************/ +static int Create( vlc_object_t *p_this ) +{ + filter_t *p_filter = (filter_t *)p_this; + const video_format_t *p_fmt = &p_filter->fmt_in.video; + findDiffs_t pf_findDiffs; + //markPicture_t pf_markPicture; + motionMeter_t pf_motionMeter; + filter_sys_t *p_sys; + + switch( p_fmt->i_chroma ) + { + CASE_PLANAR_YUV + pf_findDiffs = findDiffs; + //pf_markPicture = markPicture; + pf_motionMeter = motionMeter; + msg_Dbg( p_filter, "Using YUV *Planar* Chroma") ; + break; + + CASE_PACKED_YUV_422 + pf_findDiffs = findDiffsPacked ; + //pf_markPicture = markPicturePacked ; + pf_motionMeter = motionMeterPacked ; + msg_Dbg( p_filter, "Using YUV *Packed* Chroma") ; + break; + + default: + msg_Err( p_filter, "Unsupported input chroma (%4s)", + (char*)&(p_fmt->i_chroma) ); + return VLC_EGENERIC; + } + + /* Allocate structure */ + p_filter->p_sys = p_sys = malloc( sizeof( filter_sys_t ) ); + if( p_sys == NULL ) + return VLC_ENOMEM; + + + + /* Parse config */ + config_ChainParse( p_filter, FILTER_PREFIX, psz_filter_options, + p_filter->p_cfg ); + /* Initialize parameters */ + p_sys->f_pix_count_thres_percent = + var_CreateGetFloatCommand( p_filter, FILTER_PREFIX PAR_MTHRESHOLD ); + int i_time_threshold = + var_CreateGetIntegerCommand( p_filter, FILTER_PREFIX PAR_TTHRESHOLD ); + float f_frame_rate ; + switch ( p_fmt->i_frame_rate_base ) + { /* NASTY hack to deal with nasty frame rate values in various formats ... */ + case 0 : + f_frame_rate = 25 ; + break; + case 1 : + f_frame_rate = ( (float) p_fmt->i_frame_rate / ( p_fmt->i_frame_rate > 1000 ? 1000 : 1 ) ) ; + break; + default : + f_frame_rate = ( (float) p_fmt->i_frame_rate / p_fmt->i_frame_rate_base ) ; + } + p_sys->i_frame_thres = ((float) i_time_threshold / 1000) * ( f_frame_rate ) ; + int i_freeze_time = + var_CreateGetIntegerCommand( p_filter, FILTER_PREFIX PAR_FREEZETIME ); + msg_Dbg( p_filter, "frame rate=%d base=%d:\n", p_fmt->i_frame_rate , p_fmt->i_frame_rate_base ); + p_sys->i_freeze_frames = ((float) i_freeze_time / 1000) * ( f_frame_rate ) ; + p_filter->p_sys->i_pix_diff_thres = + var_CreateGetIntegerCommand( p_filter, FILTER_PREFIX PAR_DTHRESHOLD ); + bool b_meter = var_CreateGetBoolCommand( p_filter, FILTER_PREFIX PAR_MMETER ); + if ( !b_meter) pf_motionMeter = NULL; + + + //TODO : Check for callback meaning. Do we need it ? + /*var_AddCallback( p_filter, FILTER_PREFIX "factor", + MotionBlurCallback, p_filter->p_sys ); + */ + msg_Dbg( p_filter, "Static Detect Parameters:\n" + "\t[static] Pixel Count Threshold= %.2f\n" + "\t[static] Frame Count Threshold= %d\n" + "\t[static] Pixel Difference Threshold= %d\n" + "\t[static] Motion Meter %s\n", + p_filter->p_sys->f_pix_count_thres_percent, + p_filter->p_sys->i_frame_thres, p_filter->p_sys->i_pix_diff_thres, + ( b_meter ? "on" : "off" ) ); + + p_sys->p_old_pic = picture_New( p_fmt->i_chroma, + p_fmt->i_width, p_fmt->i_height, 0 ); + p_sys->p_freeze_pic = picture_New( p_fmt->i_chroma, + p_fmt->i_width, p_fmt->i_height, 0 ); + + p_sys->p_diff = calloc( p_fmt->i_width * p_fmt->i_height, sizeof(*p_sys->p_diff) ); + + if( !p_sys->p_old_pic || !p_sys->p_diff || !p_sys->p_freeze_pic ) + { + free( p_sys->p_diff ); + if( p_sys->p_old_pic ) + picture_Release( p_sys->p_old_pic ); + if( p_sys->p_freeze_pic ) + picture_Release( p_sys->p_freeze_pic ); + return VLC_ENOMEM; + } + + p_sys->b_has_old_pic = false; + p_sys->i_freeze_counter = 0; + + resetStats( p_filter ); + + /* libvlc event manager */ + /*libvlc_exception_t e; + libvlc_exception_init( &e ); + p_filter->p_event_manager = libvlc_event_manager_new( p_filter, p_filter->p_libvlc_instance, &e ); + libvlc_event_manager_register_event_type( p_fitler->p_event_manager, libvlc_snapshotTaken, &e );' + */ + + /* Set Video Filter Proccessor */ + p_filter->pf_video_filter = Filter; + p_sys->pf_findDiffs = pf_findDiffs ; + //p_sys->pf_markPicture = pf_markPicture ; + p_sys->pf_motionMeter = pf_motionMeter ; + + return VLC_SUCCESS; +} + +/***************************************************************************** + ** Destroy + *****************************************************************************/ +static void Destroy( vlc_object_t *p_this ) +{ + filter_t *p_filter = (filter_t *)p_this; + + echoStats( p_filter ); + free( p_filter->p_sys->p_diff ); + picture_Release( p_filter->p_sys->p_freeze_pic ); + picture_Release( p_filter->p_sys->p_old_pic ); + free( p_filter->p_sys ); +} + + +/***************************************************************************** + * Filter + *****************************************************************************/ +static picture_t *Filter( filter_t *p_filter, picture_t *p_in_pic ) +{ + filter_sys_t *p_sys = p_filter->p_sys; + picture_t *p_out_pic; + diff_t *p_diff = p_sys->p_diff; + + if( !p_in_pic ) /* we are called with empty pic */ + return NULL; + + + if( !p_sys->b_has_old_pic ) + { /* It is the first time we are called */ + picture_Copy( p_sys->p_old_pic, p_in_pic ); + p_sys->i_static_frames = 0; + p_sys->b_is_snaped = false ; + p_sys->b_has_old_pic = true; + return p_in_pic; + } + p_out_pic = filter_NewPicture( p_filter ); + + if( !p_out_pic ) + { + picture_Release( p_in_pic ); + return NULL; + } + +#if 1 + /* Find Differences */ + (p_sys->pf_findDiffs) ( p_diff, p_in_pic, p_sys->p_old_pic, p_filter ); + +#ifdef NOIZ + /* Clean Diff Image from noise */ + GaussianConvolution( p_diff, p_in_pic ); +#endif + + /* Count moved pixels */ + float f_motion ; + if ( (f_motion = countMovedPixelsPercent (p_diff, p_in_pic, p_sys->i_pix_diff_thres) ) + > p_sys->f_pix_count_thres_percent ) + { /* we have motion: reset static state */ + //msg_Dbg( p_filter, "Motion Detected. Reseting state" ); + p_sys->i_static_frames = 0; + p_sys->b_is_snaped = false; + //(p_sys->pf_markPicture) ( p_sys->p_diff, p_out_pic, p_sys->i_pix_diff_thres, p_filter ); + } else /* we have static */ + { + p_sys->i_static_frames++; + if ( p_sys->i_static_frames >= p_sys->i_frame_thres ) + { /* we are too long stationary : snap it if not alredy */ + if ( !p_sys->b_is_snaped ) { + int ec; + msg_Dbg( p_filter, "Static Detected. Snapping NOW" ); + if ( ( p_sys->i_freeze_counter = p_sys->i_freeze_frames ) ) + picture_Copy( p_sys->p_freeze_pic, p_in_pic ); + vout_Snapshot( (vout_thread_t*) p_filter->p_owner, p_in_pic ); + //vout_Control( (vout_thread_t*) p_filter->p_owner, VOUT_SNAPSHOT ); + if ( (ec = play_beep ( 0, 0)) ) //play a default beep sound on the speaker + msg_Warn( p_filter, "Error [%d] while Beeping",ec ); + p_sys->b_is_snaped = true; + } + #if 0 + if ( p_sys->i_static_frames % 30 == 0 ) + msg_Dbg( p_filter, "Static Detected [%d]. %s", p_sys->i_static_frames, + ( p_sys->b_is_snaped ? "Snapped" : "Not snapped" ) ); +#endif + } + } + if ( p_sys->i_motion_n % ( p_sys->i_frame_thres * 8 ) == 0 ) + { /* echo stats every 8 times the static framecount */ + echoStats( p_filter ); + resetStats( p_filter ); + } + /*update motion stats */ + p_sys->f_motion_max = __MAX( p_sys->f_motion_max, f_motion ); + p_sys->f_motion_min = __MIN( p_sys->f_motion_min, f_motion ); + p_sys->f_motion_avg = ( p_sys->f_motion_avg * p_sys->i_motion_n + f_motion ) / ( ++p_sys->i_motion_n ); + + if ( p_sys->i_freeze_counter > 0 ) + { + picture_Copy( p_out_pic, p_sys->p_freeze_pic ); /* Display the snapshot image for a while */ + p_sys->i_freeze_counter-- ; + } + else + picture_Copy( p_out_pic, p_in_pic ); /* Just Copy the image data for display */ + + if (p_sys->pf_motionMeter) + ( p_sys->pf_motionMeter)( f_motion, p_sys->f_pix_count_thres_percent, p_out_pic, p_filter ); +#endif + /** + * 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_pic, p_in_pic ); + + picture_Release( p_in_pic ); + return p_out_pic; +} + +void motionMeter( float f_motion, float f_threshold, picture_t* p_out_pic, filter_t* p_filter ) +{ + /* Create a horizontial bar at the bottom of the picture showing the motion level */ + const video_frame_format_t *p_fmt = &p_out_pic->format; + /* NOTE: We take format type from picture (not from filter ) */ + const unsigned int i_height = p_fmt->i_height; + const unsigned int i_width = p_fmt->i_width; + + uint8_t* p_y_pix = p_out_pic->p[Y_PLANE].p_pixels; + const int i_y_pitch = p_out_pic->p[Y_PLANE].i_pitch; + + unsigned int x,y; + + #define HMARGIN 0.020 /* bar margin from left/right */ + #define VMARGIN 0.010 /* bar margin from bottom */ + #define BHEIGHT 0.025 /* bar height */ + unsigned int i_bar_x1 = (float) i_width * HMARGIN ; + unsigned int i_bar_width = (float) i_width * ( 1 - 2 * HMARGIN ) ; + unsigned int i_bar_x2 = i_bar_x1 + i_bar_width ; + unsigned int i_bar_height = (float) i_height * ( BHEIGHT ); + unsigned int i_bar_y1 = i_height - ( (float) i_height * VMARGIN + i_bar_height ) ; + unsigned int i_bar_y2 = i_height - ( (float) i_height * VMARGIN ) ; + unsigned int i_bar_value = ( f_motion / 100 ) * i_bar_width + i_bar_x1 ; + unsigned int i_bar_threshold = ( f_threshold / 100 ) * i_bar_width + i_bar_x1 ; + unsigned int i_bar_threshold_x1 = (float) i_bar_threshold * 0.98 ; /*thicken the threshold a little */ + unsigned int i_bar_threshold_x2 = (float) i_bar_threshold * 1.02 ; + #undef HMARGIN + #undef VMARGIN + #undef BHEIGHT + /*fprintf( stderr, "static motionMeter value:%d thres:%d [%d-%d]\n", i_bar_value, i_bar_threshold, + i_bar_threshold_x1, i_bar_threshold_x2); + */ + if ( !p_out_pic) return; + + for (x = i_bar_x1; x <= i_bar_x2 ; x++) + for (y = i_bar_y1; y <= i_bar_y2; y++ ) + { + if ( x <= i_bar_value ) // Draw Value + p_y_pix[y*i_y_pitch+x] = 0xaf; + else if ( y == i_bar_y1 || y == i_bar_y2 || x == i_bar_x1 || x == i_bar_x2 ) // Draw surrounting rectangle + p_y_pix[y*i_y_pitch+x] = 0xef; + if ( x >= i_bar_threshold_x1 && x <= i_bar_threshold_x2 ) // Draw Limit + p_y_pix[y*i_y_pitch+x] = 0xff; + } +} + +void motionMeterPacked( float f_motion, float f_threshold, picture_t* p_out_pic, filter_t* p_filter ) +{ + /* Create a horizontial bar at the bottom of the picture showing the motion level */ + const video_frame_format_t *p_fmt = &p_out_pic->format; + /* NOTE: We take format type from picture (not from filter ) */ + const unsigned int i_height = p_fmt->i_height; + const unsigned int i_width = p_fmt->i_width; + + uint8_t* p_y_pix = p_out_pic->p[Y_PLANE].p_pixels; + const int i_y_pitch = p_out_pic->p[Y_PLANE].i_pitch; + + unsigned int x,y; + + if ( !p_out_pic) return; + + #define HMARGIN 0.020 /* bar margin from left/right */ + #define VMARGIN 0.010 /* bar margin from bottom */ + #define BHEIGHT 0.025 /* bar height */ + unsigned int i_bar_x1 = (float) i_width * HMARGIN ; + unsigned int i_bar_width = (float) i_width * ( 1 - 2 * HMARGIN ) ; + unsigned int i_bar_x2 = i_bar_x1 + i_bar_width ; + unsigned int i_bar_height = (float) i_height * ( BHEIGHT ); + unsigned int i_bar_y1 = i_height - ( (float) i_height * VMARGIN + i_bar_height ) ; + unsigned int i_bar_y2 = i_height - ( (float) i_height * VMARGIN ) ; + unsigned int i_bar_value = ( f_motion / 100 ) * i_bar_width + i_bar_x1 ; + unsigned int i_bar_threshold = ( f_threshold / 100 ) * i_bar_width + i_bar_x1 ; + unsigned int i_bar_threshold_x1 = (float) i_bar_threshold * 0.98 ; /*thicken the threshold a little */ + unsigned int i_bar_threshold_x2 = (float) i_bar_threshold * 1.02 ; + #undef HMARGIN + #undef VMARGIN + #undef BHEIGHT + /*fprintf( stderr, "static motionMeter value:%d thres:%d [%d-%d]\n", i_bar_value, i_bar_threshold, + i_bar_threshold_x1, i_bar_threshold_x2); + */ + int i_y_offset, i_u_offset, i_v_offset; + if( GetPackedYuvOffsets( p_fmt->i_chroma, + &i_y_offset, &i_u_offset, &i_v_offset ) != VLC_SUCCESS ) + { + msg_Warn( p_filter, "Unsupported input chroma (%4s)", + (char*)&p_fmt->i_chroma ); + return ; + } + + for( x = i_bar_x1; x <= i_bar_x2; x+=2 ) + { + for( y = i_bar_y1; y < i_bar_y2; y++ ) + // 1 macropixel = 4 bytes = 2 image pixels. For we proccess 2 image pixels per itteration + { + int i; + for ( i = 0; i < 2; i++) + { + if ( x <= i_bar_value ) // Draw Value + p_y_pix[y*i_y_pitch+2*(x+i)+i_y_offset] = 0xaf; + else if ( y == i_bar_y1 || y == i_bar_y2 || x == i_bar_x1 || x == i_bar_x2 ) + p_y_pix[y*i_y_pitch+2*(x+i)+i_y_offset] = 0xef; // Draw surrounting rectangle + if ( x >= i_bar_threshold_x1 && x <= i_bar_threshold_x2 ) + p_y_pix[y*i_y_pitch+2*(x+i)+i_y_offset] = 0xff; // Draw Limit + } + } + } +} + +/* +void markPicture( diff_t* p_diff, picture_t* p_out_pic, diff_t i_motion_thres, filter_t * p_filter ) +{ + const video_frame_format_t *p_fmt = &p_out_pic->format; + // NOTE: We take format type from picture (not from filter ) + const unsigned int i_height = p_fmt->i_height; + const unsigned int i_width = p_fmt->i_width; + + uint8_t* p_y_pix = p_out_pic->p[Y_PLANE].p_pixels; + const int i_y_pitch = p_out_pic->p[Y_PLANE].i_pitch; + + unsigned int x,y; + //msg_Dbg( p_filter, "In markPicture" ); + if ( !p_out_pic) return; + + for (y = 0; y < i_height; y++ ) + for (x = 0; x < i_width ; x++) + { + if ( p_diff[y*i_width+x] > i_motion_thres ) + p_y_pix[y*i_y_pitch+x] = 0xff; + } +} + +void markPicturePacked( diff_t* p_diff, picture_t* p_out_pic,diff_t i_motion_thres, filter_t * p_filter ) +{ + const video_frame_format_t *p_fmt = &p_out_pic->format; + // NOTE: We take format type from picture (not from filter ) + const unsigned int i_height = p_fmt->i_height; + const unsigned int i_width = p_fmt->i_width; + + uint8_t* p_y_pix = p_out_pic->p[Y_PLANE].p_pixels; + const int i_y_pitch = p_out_pic->p[Y_PLANE].i_pitch; + + unsigned int x,y; + int i_y_offset, i_u_offset, i_v_offset; + //msg_Dbg( p_filter, "In markPicturePacked" ); + + if( GetPackedYuvOffsets( p_fmt->i_chroma, + &i_y_offset, &i_u_offset, &i_v_offset ) != VLC_SUCCESS ) + { + msg_Warn( p_filter, "Unsupported input chroma (%4s)", + (char*)&p_fmt->i_chroma ); + + return ; + } + + for( y = 0; y < i_height; y++ ) + { + for( x = 0; x < i_width; x+=2 ) + // 1 macropixel = 4 bytes = 2 image pixels. For we proccess 2 image pixels per itteration + { + int i; + for ( i = 0; i < 2; i++) + if ( p_diff[y*i_width+x] > i_motion_thres ) + p_y_pix[y*i_y_pitch+2*(x+i)+i_y_offset] = 0xff ; + } + } + +} +*/ + +float countMovedPixelsPercent( diff_t* p_diff, picture_t* p_ref_pic, diff_t i_motion_threshold ) +{ + const video_frame_format_t *p_fmt = &p_ref_pic->format; + /* NOTE: We take format type from picture (not from filter ) */ + const unsigned int i_height = p_fmt->i_height; + const unsigned int i_width = p_fmt->i_width; + + + unsigned int x,y,i_pix_count = 0; + + for (y = 0; y < i_height; y++ ) + for (x = 0; x < i_width ; x++) + if ( p_diff[y*i_width+x] > i_motion_threshold ) + i_pix_count++; + /*fprintf( stderr, "static detect: countMovedPixels: %d / %d [%f%%]\n", i_pix_count, + ( i_height * i_width ), (float) i_pix_count / ( i_height * i_width ) * 100 ); + */ + /* Compute percentage of total pixel count */ + return (float) ( i_pix_count ) / (float) ( i_height * i_width ) * 100 ; +} + +void findDiffs (diff_t* p_diff, picture_t* p_new_pic, picture_t* p_old_pic, filter_t * p_filter ) +{ + const video_frame_format_t *p_fmt = &p_new_pic->format; + /* NOTE: We take format type from picture (not from filter ) */ + const unsigned int i_height = p_fmt->i_height; + const unsigned int i_width = p_fmt->i_width; + + const uint8_t* p_new_pix = p_new_pic->p[Y_PLANE].p_pixels; + const int i_new_pitch = p_new_pic->p[Y_PLANE].i_pitch; + + const uint8_t *p_old_pix = p_old_pic->p[Y_PLANE].p_pixels; + const int i_old_pitch = p_old_pic->p[Y_PLANE].i_pitch; + //msg_Dbg( p_filter, "In findDiffs" ); + /** + * Substract Y planes + */ + unsigned x, y; + for( y = 0; y < i_height; y++ ) + { + for( x = 0; x < i_width; x++ ) + p_diff[y*i_width+x] = abs( p_new_pix[y*i_new_pitch+x] - p_old_pix[y*i_old_pitch+x] ); + } + + int i_chroma_dx; + int i_chroma_dy; + switch( p_new_pic->format.i_chroma ) + /* NOTE: We check here with picture->format.i_chrome and we initialized with p_filter->fmt_in.video.i_chroma */ + { + case VLC_FOURCC('I','4','2','0'): + case VLC_FOURCC('I','Y','U','V'): + case VLC_FOURCC('J','4','2','0'): + case VLC_FOURCC('Y','V','1','2'): + i_chroma_dx = 2; + i_chroma_dy = 2; + break; + + case VLC_FOURCC('I','4','2','2'): + case VLC_FOURCC('J','4','2','2'): + i_chroma_dx = 2; + i_chroma_dy = 1; + break; + + default: + //FIX: Is it ok to hang to the p_new_pic ?? + msg_Warn( p_filter, "Not taking chroma into account" ); + i_chroma_dx = 0; + i_chroma_dy = 0; + break; + } + /* Subtrack Cb (U) and Cr (V) planes */ + + + if( i_chroma_dx != 0 && i_chroma_dy != 0 ) + { + const uint8_t *p_new_pix_u = p_new_pic->p[U_PLANE].p_pixels; + const uint8_t *p_new_pix_v = p_new_pic->p[V_PLANE].p_pixels; + const int i_new_pitch_u = p_new_pic->p[U_PLANE].i_pitch; + const int i_new_pitch_v = p_new_pic->p[V_PLANE].i_pitch; + + const uint8_t *p_old_pix_u = p_old_pic->p[U_PLANE].p_pixels; + const uint8_t *p_old_pix_v = p_old_pic->p[V_PLANE].p_pixels; + const int i_old_pitch_u = p_old_pic->p[U_PLANE].i_pitch; + const int i_old_pitch_v = p_old_pic->p[V_PLANE].i_pitch; + + for( y = 0; y < i_height/i_chroma_dy; y++ ) + { + for( x = 0; x < i_width/i_chroma_dx; x ++ ) + { + const diff_t d = abs( p_new_pix_u[y*i_new_pitch_u+x] - p_old_pix_u[y*i_old_pitch_u+x] ) + + abs( p_new_pix_v[y*i_new_pitch_v+x] - p_old_pix_v[y*i_old_pitch_v+x] ); + int i, j; + + for( j = 0; j < i_chroma_dy; j++ ) + { + for( i = 0; i < i_chroma_dx; i++ ) + p_diff[i_chroma_dy*i_width*j + i_chroma_dx*i] += d; + } + } + } + } +} + +void findDiffsPacked (diff_t* p_diff, picture_t* p_new_pic, picture_t* p_old_pic, filter_t * p_filter ) +{ + const video_frame_format_t *p_fmt = &p_new_pic->format; + /* NOTE: We take format type from picture (not from filter ) */ + const unsigned int i_height = p_fmt->i_height; + const unsigned int i_width = p_fmt->i_width; + + const uint8_t* p_new_pix = p_new_pic->p[Y_PLANE].p_pixels; + const int i_new_pitch = p_new_pic->p[Y_PLANE].i_pitch; + + const uint8_t *p_old_pix = p_old_pic->p[Y_PLANE].p_pixels; + const int i_old_pitch = p_old_pic->p[Y_PLANE].i_pitch; + + int i_y_offset, i_u_offset, i_v_offset; + + unsigned x, y; + //msg_Dbg( p_filter, "In findDiffsPacked" ); + if( GetPackedYuvOffsets( p_fmt->i_chroma, + &i_y_offset, &i_u_offset, &i_v_offset ) != VLC_SUCCESS ) + { + //FIX: Is it ok to hang to the p_new_pic ?? + msg_Warn( p_filter, "Unsupported input chroma (%4s)", + (char*)&p_fmt->i_chroma ); + return ; + } + + /* Subtract all planes at once */ + + for( y = 0; y < i_height; y++ ) + { + for( x = 0; x < i_width; x+=2 ) + /* 1 macropixel = 4 bytes = 2 image pixels. For we proccess 2 image pixels per itteration */ + { + int i; + diff_t d; + + d = abs( p_new_pix[y*i_new_pitch+2*x+i_u_offset] - p_old_pix[y*i_old_pitch+2*x+i_u_offset] ) + /* U */ + abs( p_new_pix[y*i_new_pitch+2*x+i_v_offset] - p_old_pix[y*i_old_pitch+2*x+i_v_offset] ); /* V */ + + for( i = 0; i < 2; i++ ) + p_diff[y*i_width+x+i] = + abs( p_new_pix[y*i_new_pitch+2*(x+i)+i_y_offset] - p_old_pix[y*i_old_pitch+2*(x+i)+i_y_offset] ) + d; + } + } + +} + +#ifdef NOIZ +/***************************************************************************** + * Gaussian Convolution + ***************************************************************************** + * 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 GaussianConvolution( diff_t *p_inpix, + picture_t* p_ref_pic ) +{ + const video_frame_format_t *p_fmt = &p_ref_pic->format; + /* NOTE: We take format type from picture (not from filter ) */ + const int i_num_lines = p_fmt->i_height; + const int i_src_pitch = p_fmt->i_width; + const int i_src_visible = p_fmt->i_visible_width ; + + int x,y; + diff_t* p_smooth = calloc( i_src_pitch * i_num_lines, sizeof( *p_smooth ) ); + if ( !p_smooth ) + return; + + for( y = 2; y < i_num_lines - 2; y++ ) + { + for( x = 2; x < i_src_visible - 2; x++ ) + { + 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 */; + } + } + memcpy( p_inpix, p_smooth, i_src_pitch * i_num_lines * sizeof( *p_inpix ) ); + free( p_smooth ); + +} +#endif + +//TODO: On The fly parameter Change +/*static int MotionBlurCallback( vlc_object_t *p_this, char const *psz_var, + vlc_value_t oldval, vlc_value_t newval, + void *p_data ) +{ + VLC_UNUSED(p_this); VLC_UNUSED(oldval); + filter_sys_t *p_sys = (filter_sys_t *)p_data; + if( !strcmp( psz_var, FILTER_PREFIX "factor" ) ) + p_sys->i_factor = __MIN( 127, __MAX( 1, newval.i_int ) ); + return VLC_SUCCESS; +}*/