1 /*****************************************************************************
2 * freeze.c : Freezing video filter
3 *****************************************************************************
4 * Copyright (C) 2013 Vianney Boyer
6 * Authors: Vianney Boyer <vlcvboyer -at- gmail -dot- com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_filter.h>
34 #include <vlc_mouse.h>
35 #include <vlc_picture.h>
36 #include "filter_picture.h"
39 # define MOD(a, b) ((((a)%(b)) + (b))%(b))
49 int32_t *i_visible_pitch;
50 int8_t ***pi_freezed_picture; /* records freezed pixels */
51 int16_t **pi_freezing_countdown; /* freezed pixel delay */
52 bool **pb_update_cache; /* update chache request */
56 /*****************************************************************************
58 *****************************************************************************/
60 static picture_t *Filter( filter_t *, picture_t * );
62 static int freeze_mouse( filter_t *, vlc_mouse_t *,
63 const vlc_mouse_t * );
64 static int freeze_allocate_data( filter_t *, picture_t * );
65 static void freeze_free_allocated_data( filter_t * );
68 /*****************************************************************************
70 *****************************************************************************/
72 #define CFG_PREFIX "freeze-"
74 static int Open ( filter_t * );
75 static void Close( filter_t * );
78 set_description( N_("Freezing interactive video filter") )
79 set_shortname( N_("Freeze" ) )
80 set_category( CAT_VIDEO )
81 set_subcategory( SUBCAT_VIDEO_VFILTER )
83 set_callback_video_filter( Open )
86 /*****************************************************************************
88 *****************************************************************************/
90 static const struct vlc_filter_operations filter_ops =
92 .filter_video = Filter,
93 .video_mouse = freeze_mouse,
100 static int Open( filter_t *p_filter )
104 /* Assert video in match with video out */
105 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
106 msg_Err( p_filter, "Input and output format does not match" );
110 /* Reject 0 bpp and unsupported chroma */
111 const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
112 const vlc_chroma_description_t *p_chroma
113 = vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
114 if( !p_chroma || p_chroma->pixel_size == 0
115 || p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
116 || !vlc_fourcc_IsYUV( fourcc ) )
118 msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
122 /* Allocate structure */
123 p_filter->p_sys = p_sys = calloc(1, sizeof( *p_sys ) );
124 if( unlikely(!p_sys) )
129 p_filter->ops = &filter_ops;
137 static void Close( filter_t *p_filter ) {
138 filter_sys_t *p_sys = p_filter->p_sys;
140 /* Free allocated memory */
141 freeze_free_allocated_data( p_filter );
148 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
149 if( !p_pic_in || !p_filter) return NULL;
151 filter_sys_t *p_sys = p_filter->p_sys;
153 picture_t *p_pic_out = filter_NewPicture( p_filter );
154 if( unlikely(!p_pic_out) ) {
155 picture_Release( p_pic_in );
162 if ( unlikely(!p_sys->b_init) )
163 if (freeze_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS)
165 picture_Release( p_pic_in );
168 p_sys->b_init = true;
171 * preset output pic: raw copy src to dst
173 picture_CopyPixels(p_pic_out, p_pic_in);
176 * cache original pict pixels selected with mouse pointer
178 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
179 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
180 for ( int32_t i_c = 0; i_c < p_sys->i_width[i_p]; i_c++ )
182 uint32_t i_Yr = i_r * p_sys->i_height[Y_PLANE]
183 / p_sys->i_height[i_p];
184 uint32_t i_Yc = i_c * p_sys->i_width[Y_PLANE]
185 / p_sys->i_width[i_p];
187 if ( p_sys->pb_update_cache[i_Yr][i_Yc] )
188 p_sys->pi_freezed_picture[i_p][i_r][i_c]
189 = p_pic_in->p[i_p].p_pixels[i_r*p_pic_out->p[i_p].i_pitch
190 + i_c*p_pic_out->p[i_p].i_pixel_pitch];
194 * countdown freezed pixel delay & reset pb_update_cache flag
196 for ( int32_t i_Yr = 0; i_Yr < p_sys->i_height[Y_PLANE]; i_Yr++)
197 for ( int32_t i_Yc = 0; i_Yc < p_sys->i_width[Y_PLANE]; i_Yc++)
199 if ( p_sys->pi_freezing_countdown[i_Yr][i_Yc] > 0 )
200 p_sys->pi_freezing_countdown[i_Yr][i_Yc]--;
201 p_sys->pb_update_cache[i_Yr][i_Yc] = false;
205 * apply filter: draw freezed pixels over current picture
207 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
208 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
209 for ( int32_t i_c = 0; i_c < p_sys->i_width[i_p]; i_c++ )
211 uint32_t i_Yr = i_r * p_sys->i_height[Y_PLANE]
212 / p_sys->i_height[i_p];
213 uint32_t i_Yc = i_c * p_sys->i_width[Y_PLANE]
214 / p_sys->i_width[i_p];
216 if ( p_sys->pi_freezing_countdown[i_Yr][i_Yc] > 0 )
217 p_pic_out->p[i_p].p_pixels[i_r * p_pic_out->p[i_p].i_pitch
218 + i_c * p_pic_out->p[i_p].i_pixel_pitch]
219 = p_sys->pi_freezed_picture[i_p][i_r][i_c];
222 return CopyInfoAndRelease( p_pic_out, p_pic_in );
228 static int freeze_mouse( filter_t *p_filter, vlc_mouse_t *p_new,
229 const vlc_mouse_t *p_old )
231 filter_sys_t *p_sys = p_filter->p_sys;
232 const video_format_t *p_fmt_in = &p_filter->fmt_in.video;
234 /* Only take events inside the video area */
235 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt_in->i_width ||
236 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt_in->i_height )
239 if ( unlikely(!p_sys->b_init) )
244 int32_t i_base_timeout = 0;
245 if( vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT ) )
246 i_base_timeout = 100;
247 else if( vlc_mouse_IsLeftPressed( p_new ) )
250 if( i_base_timeout > 0 )
253 * find pixels selected by user to apply freezing filter
255 int32_t i_min_sq_radius = (p_sys->i_width[Y_PLANE] / 15)
256 * (p_sys->i_width[Y_PLANE] / 15);
257 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++)
258 for ( int32_t i_c = 0; i_c < p_sys->i_width[Y_PLANE]; i_c++)
260 int32_t i_sq_dist = ( p_new->i_x - i_c )
261 * ( p_new->i_x - i_c )
262 + ( p_new->i_y - i_r )
263 * ( p_new->i_y - i_r );
264 i_sq_dist = __MAX(0, i_sq_dist - i_min_sq_radius);
266 uint16_t i_timeout = __MAX(i_base_timeout - i_sq_dist, 0);
268 /* ask to update chache for pixel to be freezed just now */
269 if ( p_sys->pi_freezing_countdown[i_r][i_c] == 0 && i_timeout > 0)
270 p_sys->pb_update_cache[i_r][i_c] = true;
272 /* set freezing delay */
273 if ( p_sys->pi_freezing_countdown[i_r][i_c] < i_timeout )
274 p_sys->pi_freezing_countdown[i_r][i_c] = i_timeout;
285 static int freeze_allocate_data( filter_t *p_filter, picture_t *p_pic_in )
287 filter_sys_t *p_sys = p_filter->p_sys;
289 freeze_free_allocated_data( p_filter );
292 * take into account different characteristics for each plane
294 p_sys->i_planes = p_pic_in->i_planes;
295 p_sys->i_height = calloc( p_sys->i_planes, sizeof(int32_t) );
296 p_sys->i_width = calloc( p_sys->i_planes, sizeof(int32_t) );
297 p_sys->i_visible_pitch = calloc( p_sys->i_planes, sizeof(int32_t) );
299 if ( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) )
301 freeze_free_allocated_data( p_filter );
306 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
308 p_sys->i_visible_pitch [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch;
309 p_sys->i_height[i_p] = (int) p_pic_in->p[i_p].i_visible_lines;
310 p_sys->i_width[i_p] = (int) p_pic_in->p[i_p].i_visible_pitch
311 / p_pic_in->p[i_p].i_pixel_pitch;
314 /* buffer used to countdown freezing delay */
315 p_sys->pi_freezing_countdown
316 = calloc( p_sys->i_height[Y_PLANE], sizeof(int16_t*) );
317 if ( unlikely( !p_sys->pi_freezing_countdown ) )
319 freeze_free_allocated_data( p_filter );
323 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
325 p_sys->pi_freezing_countdown[i_r]
326 = calloc( p_sys->i_width[Y_PLANE], sizeof(int16_t) );
327 if ( unlikely( !p_sys->pi_freezing_countdown[i_r] ) )
329 freeze_free_allocated_data( p_filter );
334 /* buffer used to cache freezed pixels colors */
335 p_sys->pi_freezed_picture = calloc( p_sys->i_planes, sizeof(int8_t**) );
336 if( unlikely( !p_sys->pi_freezed_picture ) )
338 freeze_free_allocated_data( p_filter );
342 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++)
344 p_sys->pi_freezed_picture[i_p]
345 = calloc( p_sys->i_height[i_p], sizeof(int8_t*) );
346 if ( unlikely(!p_sys->pi_freezed_picture[i_p]) )
348 freeze_free_allocated_data( p_filter );
351 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
353 p_sys->pi_freezed_picture[i_p][i_r]
354 = calloc( p_sys->i_width[i_p], sizeof(int8_t) );
355 if ( unlikely( !p_sys->pi_freezed_picture[i_p][i_r] ) )
357 freeze_free_allocated_data( p_filter );
363 /* flag used to manage freezed pixels cache update */
364 p_sys->pb_update_cache
365 = calloc( p_sys->i_height[Y_PLANE], sizeof(bool*) );
366 if( unlikely( !p_sys->pb_update_cache ) )
368 freeze_free_allocated_data( p_filter );
372 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
374 p_sys->pb_update_cache[i_r]
375 = calloc( p_sys->i_width[Y_PLANE], sizeof(bool) );
376 if ( unlikely( !p_sys->pb_update_cache[i_r] ) )
378 freeze_free_allocated_data( p_filter );
387 * Free allocated data
389 static void freeze_free_allocated_data( filter_t *p_filter ) {
390 filter_sys_t *p_sys = p_filter->p_sys;
392 if (p_sys->pi_freezing_countdown)
393 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
394 free( p_sys->pi_freezing_countdown[i_r] );
395 FREENULL( p_sys->pi_freezing_countdown );
397 if ( p_sys->pb_update_cache )
398 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
399 free( p_sys->pb_update_cache[i_r] );
400 FREENULL( p_sys->pb_update_cache );
402 if ( p_sys->pi_freezed_picture )
403 for ( int32_t i_p=0; i_p < p_sys->i_planes; i_p++ ) {
404 for ( int32_t i_r=0; i_r < p_sys->i_height[i_p]; i_r++ )
405 free( p_sys->pi_freezed_picture[i_p][i_r] );
406 free( p_sys->pi_freezed_picture[i_p] );
408 FREENULL( p_sys->pi_freezed_picture );
411 FREENULL( p_sys->i_height );
412 FREENULL( p_sys->i_width );
413 FREENULL( p_sys->i_visible_pitch );