lib: media_player: fix libvlc_MediaPlayerMediaChanged event
[vlc.git] / modules / video_filter / freeze.c
blobbe5fde8e9d9369830ae6bc07280d470d41729f85
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 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
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"
38 #ifndef MOD
39 # define MOD(a, b) ((((a)%(b)) + (b))%(b))
40 #endif
42 typedef struct
44 bool b_init;
46 int32_t i_planes;
47 int32_t *i_height;
48 int32_t *i_width;
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 */
54 } filter_sys_t;
56 /*****************************************************************************
57 * Prototypes
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 /*****************************************************************************
69 * Module descriptor
70 *****************************************************************************/
72 #define CFG_PREFIX "freeze-"
74 static int Open ( vlc_object_t * );
75 static void Close( vlc_object_t * );
77 vlc_module_begin()
78 set_description( N_("Freezing interactive video filter") )
79 set_shortname( N_("Freeze" ) )
80 set_capability( "video filter", 0 )
81 set_category( CAT_VIDEO )
82 set_subcategory( SUBCAT_VIDEO_VFILTER )
84 set_callbacks( Open, Close )
85 vlc_module_end()
87 /*****************************************************************************
88 * Local prototypes
89 *****************************************************************************/
91 /**
92 * Open the filter
94 static int Open( vlc_object_t *p_this )
96 filter_t *p_filter = (filter_t *)p_this;
97 filter_sys_t *p_sys;
99 /* Assert video in match with video out */
100 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
101 msg_Err( p_filter, "Input and output format does not match" );
102 return VLC_EGENERIC;
105 /* Reject 0 bpp and unsupported chroma */
106 const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
107 const vlc_chroma_description_t *p_chroma
108 = vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
109 if( !p_chroma || p_chroma->pixel_size == 0
110 || p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
111 || !vlc_fourcc_IsYUV( fourcc ) )
113 msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
114 return VLC_EGENERIC;
117 /* Allocate structure */
118 p_filter->p_sys = p_sys = calloc(1, sizeof( *p_sys ) );
119 if( unlikely(!p_sys) )
120 return VLC_ENOMEM;
122 /* init data */
124 p_filter->pf_video_filter = Filter;
125 p_filter->pf_video_mouse = freeze_mouse;
127 return VLC_SUCCESS;
131 * Close the filter
133 static void Close( vlc_object_t *p_this ) {
134 filter_t *p_filter = (filter_t *)p_this;
135 filter_sys_t *p_sys = p_filter->p_sys;
137 /* Free allocated memory */
138 freeze_free_allocated_data( p_filter );
139 free( p_sys );
143 * Filter a picture
145 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
146 if( !p_pic_in || !p_filter) return NULL;
148 filter_sys_t *p_sys = p_filter->p_sys;
150 picture_t *p_pic_out = filter_NewPicture( p_filter );
151 if( unlikely(!p_pic_out) ) {
152 picture_Release( p_pic_in );
153 return NULL;
157 * allocate data
159 if ( unlikely(!p_sys->b_init) )
160 if (freeze_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS)
162 picture_Release( p_pic_in );
163 return NULL;
165 p_sys->b_init = true;
168 * preset output pic: raw copy src to dst
170 picture_CopyPixels(p_pic_out, p_pic_in);
173 * cache original pict pixels selected with mouse pointer
175 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
176 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
177 for ( int32_t i_c = 0; i_c < p_sys->i_width[i_p]; i_c++ )
179 uint32_t i_Yr = i_r * p_sys->i_height[Y_PLANE]
180 / p_sys->i_height[i_p];
181 uint32_t i_Yc = i_c * p_sys->i_width[Y_PLANE]
182 / p_sys->i_width[i_p];
184 if ( p_sys->pb_update_cache[i_Yr][i_Yc] )
185 p_sys->pi_freezed_picture[i_p][i_r][i_c]
186 = p_pic_in->p[i_p].p_pixels[i_r*p_pic_out->p[i_p].i_pitch
187 + i_c*p_pic_out->p[i_p].i_pixel_pitch];
191 * countdown freezed pixel delay & reset pb_update_cache flag
193 for ( int32_t i_Yr = 0; i_Yr < p_sys->i_height[Y_PLANE]; i_Yr++)
194 for ( int32_t i_Yc = 0; i_Yc < p_sys->i_width[Y_PLANE]; i_Yc++)
196 if ( p_sys->pi_freezing_countdown[i_Yr][i_Yc] > 0 )
197 p_sys->pi_freezing_countdown[i_Yr][i_Yc]--;
198 p_sys->pb_update_cache[i_Yr][i_Yc] = false;
202 * apply filter: draw freezed pixels over current picture
204 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
205 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
206 for ( int32_t i_c = 0; i_c < p_sys->i_width[i_p]; i_c++ )
208 uint32_t i_Yr = i_r * p_sys->i_height[Y_PLANE]
209 / p_sys->i_height[i_p];
210 uint32_t i_Yc = i_c * p_sys->i_width[Y_PLANE]
211 / p_sys->i_width[i_p];
213 if ( p_sys->pi_freezing_countdown[i_Yr][i_Yc] > 0 )
214 p_pic_out->p[i_p].p_pixels[i_r * p_pic_out->p[i_p].i_pitch
215 + i_c * p_pic_out->p[i_p].i_pixel_pitch]
216 = p_sys->pi_freezed_picture[i_p][i_r][i_c];
219 return CopyInfoAndRelease( p_pic_out, p_pic_in );
223 * mouse callback
225 static int freeze_mouse( filter_t *p_filter, vlc_mouse_t *p_new,
226 const vlc_mouse_t *p_old )
228 filter_sys_t *p_sys = p_filter->p_sys;
229 const video_format_t *p_fmt_in = &p_filter->fmt_in.video;
231 /* Only take events inside the video area */
232 if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt_in->i_width ||
233 p_new->i_y < 0 || p_new->i_y >= (int)p_fmt_in->i_height )
234 return VLC_EGENERIC;
236 if ( unlikely(!p_sys->b_init) )
238 return VLC_SUCCESS;
241 int32_t i_base_timeout = 0;
242 if( vlc_mouse_HasPressed( p_old, p_new, MOUSE_BUTTON_LEFT ) )
243 i_base_timeout = 100;
244 else if( vlc_mouse_IsLeftPressed( p_new ) )
245 i_base_timeout = 50;
247 if( i_base_timeout > 0 )
250 * find pixels selected by user to apply freezing filter
252 int32_t i_min_sq_radius = (p_sys->i_width[Y_PLANE] / 15)
253 * (p_sys->i_width[Y_PLANE] / 15);
254 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++)
255 for ( int32_t i_c = 0; i_c < p_sys->i_width[Y_PLANE]; i_c++)
257 int32_t i_sq_dist = ( p_new->i_x - i_c )
258 * ( p_new->i_x - i_c )
259 + ( p_new->i_y - i_r )
260 * ( p_new->i_y - i_r );
261 i_sq_dist = __MAX(0, i_sq_dist - i_min_sq_radius);
263 uint16_t i_timeout = __MAX(i_base_timeout - i_sq_dist, 0);
265 /* ask to update chache for pixel to be freezed just now */
266 if ( p_sys->pi_freezing_countdown[i_r][i_c] == 0 && i_timeout > 0)
267 p_sys->pb_update_cache[i_r][i_c] = true;
269 /* set freezing delay */
270 if ( p_sys->pi_freezing_countdown[i_r][i_c] < i_timeout )
271 p_sys->pi_freezing_countdown[i_r][i_c] = i_timeout;
275 return VLC_EGENERIC;
280 * Allocate data
282 static int freeze_allocate_data( filter_t *p_filter, picture_t *p_pic_in )
284 filter_sys_t *p_sys = p_filter->p_sys;
286 freeze_free_allocated_data( p_filter );
289 * take into account different characteristics for each plane
291 p_sys->i_planes = p_pic_in->i_planes;
292 p_sys->i_height = calloc( p_sys->i_planes, sizeof(int32_t) );
293 p_sys->i_width = calloc( p_sys->i_planes, sizeof(int32_t) );
294 p_sys->i_visible_pitch = calloc( p_sys->i_planes, sizeof(int32_t) );
296 if ( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) )
298 freeze_free_allocated_data( p_filter );
299 return VLC_ENOMEM;
302 /* init data */
303 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
305 p_sys->i_visible_pitch [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch;
306 p_sys->i_height[i_p] = (int) p_pic_in->p[i_p].i_visible_lines;
307 p_sys->i_width[i_p] = (int) p_pic_in->p[i_p].i_visible_pitch
308 / p_pic_in->p[i_p].i_pixel_pitch;
311 /* buffer used to countdown freezing delay */
312 p_sys->pi_freezing_countdown
313 = calloc( p_sys->i_height[Y_PLANE], sizeof(int16_t*) );
314 if ( unlikely( !p_sys->pi_freezing_countdown ) )
316 freeze_free_allocated_data( p_filter );
317 return VLC_ENOMEM;
320 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
322 p_sys->pi_freezing_countdown[i_r]
323 = calloc( p_sys->i_width[Y_PLANE], sizeof(int16_t) );
324 if ( unlikely( !p_sys->pi_freezing_countdown[i_r] ) )
326 freeze_free_allocated_data( p_filter );
327 return VLC_ENOMEM;
331 /* buffer used to cache freezed pixels colors */
332 p_sys->pi_freezed_picture = calloc( p_sys->i_planes, sizeof(int8_t**) );
333 if( unlikely( !p_sys->pi_freezed_picture ) )
335 freeze_free_allocated_data( p_filter );
336 return VLC_ENOMEM;
339 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++)
341 p_sys->pi_freezed_picture[i_p]
342 = calloc( p_sys->i_height[i_p], sizeof(int8_t*) );
343 if ( unlikely(!p_sys->pi_freezed_picture[i_p]) )
345 freeze_free_allocated_data( p_filter );
346 return VLC_ENOMEM;
348 for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
350 p_sys->pi_freezed_picture[i_p][i_r]
351 = calloc( p_sys->i_width[i_p], sizeof(int8_t) );
352 if ( unlikely( !p_sys->pi_freezed_picture[i_p][i_r] ) )
354 freeze_free_allocated_data( p_filter );
355 return VLC_ENOMEM;
360 /* flag used to manage freezed pixels cache update */
361 p_sys->pb_update_cache
362 = calloc( p_sys->i_height[Y_PLANE], sizeof(bool*) );
363 if( unlikely( !p_sys->pb_update_cache ) )
365 freeze_free_allocated_data( p_filter );
366 return VLC_ENOMEM;
369 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
371 p_sys->pb_update_cache[i_r]
372 = calloc( p_sys->i_width[Y_PLANE], sizeof(bool) );
373 if ( unlikely( !p_sys->pb_update_cache[i_r] ) )
375 freeze_free_allocated_data( p_filter );
376 return VLC_ENOMEM;
380 return VLC_SUCCESS;
384 * Free allocated data
386 static void freeze_free_allocated_data( filter_t *p_filter ) {
387 filter_sys_t *p_sys = p_filter->p_sys;
389 if (p_sys->pi_freezing_countdown)
390 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
391 free( p_sys->pi_freezing_countdown[i_r] );
392 FREENULL( p_sys->pi_freezing_countdown );
394 if ( p_sys->pb_update_cache )
395 for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
396 free( p_sys->pb_update_cache[i_r] );
397 FREENULL( p_sys->pb_update_cache );
399 if ( p_sys->pi_freezed_picture )
400 for ( int32_t i_p=0; i_p < p_sys->i_planes; i_p++ ) {
401 for ( int32_t i_r=0; i_r < p_sys->i_height[i_p]; i_r++ )
402 free( p_sys->pi_freezed_picture[i_p][i_r] );
403 free( p_sys->pi_freezed_picture[i_p] );
405 FREENULL( p_sys->pi_freezed_picture );
407 p_sys->i_planes = 0;
408 FREENULL( p_sys->i_height );
409 FREENULL( p_sys->i_width );
410 FREENULL( p_sys->i_visible_pitch );