]> git.sesse.net Git - vlc/blob - modules/video_filter/freeze.c
decoder: fix data race in input_DecoderChangePause()
[vlc] / modules / video_filter / freeze.c
1 /*****************************************************************************
2  * freeze.c : Freezing video filter
3  *****************************************************************************
4  * Copyright (C) 2013      Vianney Boyer
5  * $Id$
6  *
7  * Authors: Vianney Boyer <vlcvboyer -at- gmail -dot- com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.h>
35
36 #include "filter_picture.h"
37
38 #ifndef MOD
39 #   define MOD(a, b) ((((a)%(b)) + (b))%(b))
40 #endif
41
42 struct filter_sys_t {
43     bool b_init;
44
45     int32_t i_planes;
46     int32_t *i_height;
47     int32_t *i_width;
48     int32_t *i_visible_pitch;
49     int8_t  ***pi_freezed_picture;   /* records freezed pixels */
50     int16_t **pi_freezing_countdown; /* freezed pixel delay    */
51     bool    **pb_update_cache;       /* update chache request  */
52
53 };
54
55 /*****************************************************************************
56  * Prototypes
57  *****************************************************************************/
58
59 static picture_t *Filter( filter_t *, picture_t * );
60
61 static int  freeze_mouse( filter_t *, vlc_mouse_t *,
62                    const vlc_mouse_t *, const vlc_mouse_t * );
63 static int  freeze_allocate_data( filter_t *, picture_t * );
64 static void freeze_free_allocated_data( filter_t * );
65
66
67 /*****************************************************************************
68  * Module descriptor
69  *****************************************************************************/
70
71 #define CFG_PREFIX "freeze-"
72
73 static int  Open ( vlc_object_t * );
74 static void Close( vlc_object_t * );
75
76 vlc_module_begin()
77     set_description( N_("Freezing interactive video filter") )
78     set_shortname(   N_("Freeze" ) )
79     set_capability(  "video filter2", 0 )
80     set_category(    CAT_VIDEO )
81     set_subcategory( SUBCAT_VIDEO_VFILTER )
82
83     set_callbacks( Open, Close )
84 vlc_module_end()
85
86 /*****************************************************************************
87  * Local prototypes
88  *****************************************************************************/
89
90 /**
91  * Open the filter
92  */
93 static int Open( vlc_object_t *p_this )
94 {
95     filter_t *p_filter = (filter_t *)p_this;
96     filter_sys_t *p_sys;
97
98     /* Assert video in match with video out */
99     if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
100         msg_Err( p_filter, "Input and output format does not match" );
101         return VLC_EGENERIC;
102     }
103
104     /* Reject 0 bpp and unsupported chroma */
105     const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
106     const vlc_chroma_description_t *p_chroma
107           = vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
108     if( !p_chroma || p_chroma->pixel_size == 0
109         || p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
110         || !vlc_fourcc_IsYUV( fourcc ) )
111     {
112         msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
113         return VLC_EGENERIC;
114     }
115
116     /* Allocate structure */
117     p_filter->p_sys = p_sys = calloc(1, sizeof( *p_sys ) );
118     if( unlikely(!p_sys) )
119         return VLC_ENOMEM;
120
121     /* init data */
122
123     p_filter->pf_video_filter = Filter;
124     p_filter->pf_video_mouse  = freeze_mouse;
125
126     return VLC_SUCCESS;
127 }
128
129 /**
130  * Close the filter
131  */
132 static void Close( vlc_object_t *p_this ) {
133     filter_t *p_filter  = (filter_t *)p_this;
134     filter_sys_t *p_sys = p_filter->p_sys;
135
136     /* Free allocated memory */
137     freeze_free_allocated_data( p_filter );
138     free( p_sys );
139 }
140
141 /**
142  * Filter a picture
143  */
144 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
145     if( !p_pic_in || !p_filter) return NULL;
146
147     filter_sys_t *p_sys = p_filter->p_sys;
148
149     picture_t *p_pic_out = filter_NewPicture( p_filter );
150     if( unlikely(!p_pic_out) ) {
151         picture_Release( p_pic_in );
152         return NULL;
153     }
154
155    /*
156     * allocate data
157     */
158     if ( unlikely(!p_sys->b_init) )
159         if (freeze_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS)
160         {
161             picture_Release( p_pic_in );
162             return NULL;
163         }
164     p_sys->b_init = true;
165
166    /*
167     * preset output pic: raw copy src to dst
168     */
169     picture_CopyPixels(p_pic_out, p_pic_in);
170
171    /*
172     * cache original pict pixels selected with mouse pointer
173     */
174     for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
175         for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
176             for ( int32_t i_c = 0; i_c < p_sys->i_width[i_p]; i_c++ )
177             {
178                 uint32_t i_Yr = i_r * p_sys->i_height[Y_PLANE]
179                               / p_sys->i_height[i_p];
180                 uint32_t i_Yc = i_c * p_sys->i_width[Y_PLANE]
181                               / p_sys->i_width[i_p];
182
183                 if ( p_sys->pb_update_cache[i_Yr][i_Yc] )
184                     p_sys->pi_freezed_picture[i_p][i_r][i_c]
185                         = p_pic_in->p[i_p].p_pixels[i_r*p_pic_out->p[i_p].i_pitch
186                         + i_c*p_pic_out->p[i_p].i_pixel_pitch];
187             }
188
189    /*
190     * countdown freezed pixel delay & reset pb_update_cache flag
191     */
192     for ( int32_t i_Yr = 0; i_Yr < p_sys->i_height[Y_PLANE]; i_Yr++)
193         for ( int32_t i_Yc = 0; i_Yc < p_sys->i_width[Y_PLANE]; i_Yc++)
194         {
195             if ( p_sys->pi_freezing_countdown[i_Yr][i_Yc] > 0 )
196                  p_sys->pi_freezing_countdown[i_Yr][i_Yc]--;
197             p_sys->pb_update_cache[i_Yr][i_Yc] = false;
198         }
199
200    /*
201     * apply filter: draw freezed pixels over current picture
202     */
203     for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
204         for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
205             for ( int32_t i_c = 0; i_c < p_sys->i_width[i_p]; i_c++ )
206             {
207                 uint32_t i_Yr = i_r * p_sys->i_height[Y_PLANE]
208                               / p_sys->i_height[i_p];
209                 uint32_t i_Yc = i_c * p_sys->i_width[Y_PLANE]
210                               / p_sys->i_width[i_p];
211
212                 if ( p_sys->pi_freezing_countdown[i_Yr][i_Yc] > 0 )
213                     p_pic_out->p[i_p].p_pixels[i_r * p_pic_out->p[i_p].i_pitch
214                         + i_c * p_pic_out->p[i_p].i_pixel_pitch]
215                         = p_sys->pi_freezed_picture[i_p][i_r][i_c];
216             }
217
218     return CopyInfoAndRelease( p_pic_out, p_pic_in );
219 }
220
221 /*
222  * mouse callback
223  **/
224 static int freeze_mouse( filter_t *p_filter, vlc_mouse_t *p_mouse,
225                   const vlc_mouse_t *p_old, const vlc_mouse_t *p_new )
226 {
227     filter_sys_t *p_sys = p_filter->p_sys;
228     const video_format_t  *p_fmt_in = &p_filter->fmt_in.video;
229
230     /* Only take events inside the video area */
231     if( p_new->i_x < 0 || p_new->i_x >= (int)p_fmt_in->i_width ||
232         p_new->i_y < 0 || p_new->i_y >= (int)p_fmt_in->i_height )
233         return VLC_EGENERIC;
234
235     if ( unlikely(!p_sys->b_init) )
236     {
237         *p_mouse = *p_new;
238         return VLC_SUCCESS;
239     }
240
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;
246
247     if( i_base_timeout > 0 )
248     {
249         /*
250          * find pixels selected by user to apply freezing filter
251          */
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++)
256             {
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);
262
263                 uint16_t i_timeout = __MAX(i_base_timeout - i_sq_dist, 0);
264
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;
268
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;
272             }
273     }
274
275     return VLC_EGENERIC;
276 }
277
278
279 /*
280  * Allocate data
281  */
282 static int freeze_allocate_data( filter_t *p_filter, picture_t *p_pic_in )
283 {
284     filter_sys_t *p_sys = p_filter->p_sys;
285
286     freeze_free_allocated_data( p_filter );
287
288    /*
289     * take into account different characteristics for each plane
290     */
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) );
295
296     if ( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) )
297     {
298         freeze_free_allocated_data( p_filter );
299         return VLC_ENOMEM;
300     }
301
302     /* init data */
303     for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ )
304     {
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;
309     }
310
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 ) )
315     {
316         freeze_free_allocated_data( p_filter );
317         return VLC_ENOMEM;
318     }
319
320     for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
321     {
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] ) )
325         {
326             freeze_free_allocated_data( p_filter );
327             return VLC_ENOMEM;
328         }
329     }
330
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 ) )
334     {
335         freeze_free_allocated_data( p_filter );
336         return VLC_ENOMEM;
337     }
338
339     for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++)
340     {
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]) )
344         {
345             freeze_free_allocated_data( p_filter );
346             return VLC_ENOMEM;
347         }
348         for ( int32_t i_r = 0; i_r < p_sys->i_height[i_p]; i_r++ )
349         {
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] ) )
353             {
354                 freeze_free_allocated_data( p_filter );
355                 return VLC_ENOMEM;
356             }
357         }
358     }
359
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 ) )
364     {
365         freeze_free_allocated_data( p_filter );
366         return VLC_ENOMEM;
367     }
368
369     for ( int32_t i_r = 0; i_r < p_sys->i_height[Y_PLANE]; i_r++ )
370     {
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] ) )
374         {
375             freeze_free_allocated_data( p_filter );
376             return VLC_ENOMEM;
377         }
378     }
379
380     return VLC_SUCCESS;
381 }
382
383 /**
384  * Free allocated data
385  */
386 static void freeze_free_allocated_data( filter_t *p_filter ) {
387     filter_sys_t *p_sys = p_filter->p_sys;
388
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 );
393
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 );
398
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] );
404             }
405     FREENULL( p_sys->pi_freezed_picture );
406
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 );
411 }