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 ( vlc_object_t
* );
75 static void Close( vlc_object_t
* );
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
)
87 /*****************************************************************************
89 *****************************************************************************/
94 static int Open( vlc_object_t
*p_this
)
96 filter_t
*p_filter
= (filter_t
*)p_this
;
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" );
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
);
117 /* Allocate structure */
118 p_filter
->p_sys
= p_sys
= calloc(1, sizeof( *p_sys
) );
119 if( unlikely(!p_sys
) )
124 p_filter
->pf_video_filter
= Filter
;
125 p_filter
->pf_video_mouse
= freeze_mouse
;
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
);
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
);
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
);
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
);
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
)
236 if ( unlikely(!p_sys
->b_init
) )
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
) )
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
;
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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
);
408 FREENULL( p_sys
->i_height
);
409 FREENULL( p_sys
->i_width
);
410 FREENULL( p_sys
->i_visible_pitch
);