mux:ts: convert vlc_tick_t to seconds explicitly using SEC_FROM_VLC_TICK()
[vlc.git] / modules / video_filter / vhs.c
blob13c97d05fad618e76064b6bf0a4a3157c249eb07
1 /*****************************************************************************
2 * vhs.c : VHS effect video filter
3 *****************************************************************************
4 * Copyright (C) 2013 Vianney Boyer
5 * $Id$
7 * Authors: Vianney Boyer <vlcvboyer -at- gmail -dot- com>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_filter.h>
35 #include <vlc_picture.h>
36 #include <vlc_rand.h>
37 #include <vlc_tick.h>
39 #include "filter_picture.h"
41 static inline int64_t MOD(int64_t a, int64_t b) {
42 return ( ( a % b ) + b ) % b; }
44 #define MAX_BLUE_RED_LINES 100
46 typedef struct {
47 int32_t i_offset;
48 uint16_t i_intensity;
49 bool b_blue_red;
50 vlc_tick_t i_stop_trigger;
51 } blue_red_line_t;
53 typedef struct
56 /* general data */
57 bool b_init;
58 int32_t i_planes;
59 int32_t *i_height; /* note: each plane may have different dimensions */
60 int32_t *i_width;
61 int32_t *i_visible_pitch;
62 vlc_tick_t i_start_time;
63 vlc_tick_t i_last_time;
64 vlc_tick_t i_cur_time;
66 /* sliding & offset effect */
67 int32_t i_phase_speed;
68 int32_t i_phase_ofs;
69 int32_t i_offset_ofs;
70 int32_t i_sliding_ofs;
71 int32_t i_sliding_speed;
72 vlc_tick_t i_offset_trigger;
73 vlc_tick_t i_sliding_trigger;
74 vlc_tick_t i_sliding_stop_trig;
75 bool i_sliding_type_duplicate;
77 /* blue red lines effect */
78 vlc_tick_t i_BR_line_trigger;
79 blue_red_line_t *p_BR_lines[MAX_BLUE_RED_LINES];
81 } filter_sys_t;
83 /*****************************************************************************
84 * Prototypes
85 *****************************************************************************/
87 static picture_t *Filter( filter_t *, picture_t * );
89 static int vhs_allocate_data( filter_t *, picture_t * );
90 static void vhs_free_allocated_data( filter_t * );
92 static int vhs_blue_red_line_effect( filter_t *, picture_t * );
93 static void vhs_blue_red_dots_effect( filter_t *, picture_t * );
94 static int vhs_sliding_effect( filter_t *, picture_t * );
96 static int vhs_sliding_effect_apply( filter_t *, picture_t * );
98 /*****************************************************************************
99 * Module descriptor
100 *****************************************************************************/
102 static int Open ( vlc_object_t * );
103 static void Close( vlc_object_t * );
105 vlc_module_begin()
106 set_description( N_("VHS movie effect video filter") )
107 set_shortname( N_("VHS movie" ) )
108 set_capability( "video filter", 0 )
109 set_category( CAT_VIDEO )
110 set_subcategory( SUBCAT_VIDEO_VFILTER )
112 set_callbacks( Open, Close )
113 vlc_module_end()
116 * Open the filter
118 static int Open( vlc_object_t *p_this )
120 filter_t *p_filter = (filter_t*)p_this;
121 filter_sys_t *p_sys;
123 /* Assert video in match with video out */
124 if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
125 msg_Err( p_filter, "Input and output format does not match" );
126 return VLC_EGENERIC;
129 /* Reject 0 bpp and unsupported chroma */
130 const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
131 const vlc_chroma_description_t *p_chroma =
132 vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
133 if( !p_chroma || p_chroma->pixel_size == 0
134 || p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
135 || !vlc_fourcc_IsYUV( fourcc ) )
137 msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
138 return VLC_EGENERIC;
141 /* Allocate structure */
142 p_filter->p_sys = p_sys = calloc(1, sizeof(*p_sys) );
143 if( unlikely( !p_sys ) )
144 return VLC_ENOMEM;
146 /* init data */
147 p_filter->pf_video_filter = Filter;
148 p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = vlc_tick_now();
150 return VLC_SUCCESS;
154 * Close the filter
156 static void Close( vlc_object_t *p_this ) {
157 filter_t *p_filter = (filter_t*)p_this;
158 filter_sys_t *p_sys = p_filter->p_sys;
160 /* Free allocated memory */
161 vhs_free_allocated_data( p_filter );
162 free( p_sys );
166 * Filter a picture
168 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
169 if( unlikely( !p_pic_in || !p_filter) )
170 return NULL;
172 filter_sys_t *p_sys = p_filter->p_sys;
174 picture_t *p_pic_out = filter_NewPicture( p_filter );
175 if( unlikely( !p_pic_out ) ) {
176 picture_Release( p_pic_in );
177 return NULL;
181 * manage time
183 p_sys->i_last_time = p_sys->i_cur_time;
184 p_sys->i_cur_time = vlc_tick_now();
187 * allocate data
189 if ( unlikely( !p_sys->b_init ) )
190 if ( unlikely( vhs_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS ) ) {
191 picture_Release( p_pic_in );
192 return NULL;
194 p_sys->b_init = true;
197 * preset output pic: raw copy src to dst
199 picture_CopyPixels(p_pic_out, p_pic_in);
202 * apply effects on picture
204 if ( unlikely( vhs_blue_red_line_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
205 return CopyInfoAndRelease( p_pic_out, p_pic_in );
207 if ( unlikely( vhs_sliding_effect(p_filter, p_pic_out ) != VLC_SUCCESS ) )
208 return CopyInfoAndRelease( p_pic_out, p_pic_in );
210 vhs_blue_red_dots_effect( p_filter, p_pic_out );
212 return CopyInfoAndRelease( p_pic_out, p_pic_in );
216 * Allocate data
218 static int vhs_allocate_data( filter_t *p_filter, picture_t *p_pic_in ) {
219 filter_sys_t *p_sys = p_filter->p_sys;
221 vhs_free_allocated_data( p_filter );
224 * take into account different characteristics for each plane
226 p_sys->i_planes = p_pic_in->i_planes;
227 p_sys->i_height = calloc( p_sys->i_planes, sizeof(int32_t) );
228 p_sys->i_width = calloc( p_sys->i_planes, sizeof(int32_t) );
229 p_sys->i_visible_pitch = calloc( p_sys->i_planes, sizeof(int32_t) );
231 if( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) ) {
232 vhs_free_allocated_data( p_filter );
233 return VLC_ENOMEM;
236 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++) {
237 p_sys->i_visible_pitch [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch;
238 p_sys->i_height[i_p] = (int) p_pic_in->p[i_p].i_visible_lines;
239 p_sys->i_width [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch / p_pic_in->p[i_p].i_pixel_pitch;
241 return VLC_SUCCESS;
245 * Free allocated data
247 static void vhs_free_allocated_data( filter_t *p_filter ) {
248 filter_sys_t *p_sys = p_filter->p_sys;
250 for ( uint32_t i_b = 0; i_b < MAX_BLUE_RED_LINES; i_b++ )
251 FREENULL( p_sys->p_BR_lines[i_b] );
253 p_sys->i_planes = 0;
254 FREENULL( p_sys->i_height );
255 FREENULL( p_sys->i_width );
256 FREENULL( p_sys->i_visible_pitch );
261 * Horizontal blue or red lines random management and effect
263 static int vhs_blue_red_line_effect( filter_t *p_filter, picture_t *p_pic_out ) {
264 filter_sys_t *p_sys = p_filter->p_sys;
266 #define BR_LINES_GENERATOR_PERIOD VLC_TICK_FROM_SEC(50)
267 #define BR_LINES_DURATION VLC_TICK_FROM_MS(20)
269 /* generate new blue or red lines */
270 if ( p_sys->i_BR_line_trigger <= p_sys->i_cur_time ) {
271 for ( uint32_t i_b = 0; i_b < MAX_BLUE_RED_LINES; i_b++ )
272 if (p_sys->p_BR_lines[i_b] == NULL) {
273 /* allocate data */
274 p_sys->p_BR_lines[i_b] = calloc( 1, sizeof(blue_red_line_t) );
275 if ( unlikely( !p_sys->p_BR_lines[i_b] ) )
276 return VLC_ENOMEM;
278 /* set random parameters */
279 p_sys->p_BR_lines[i_b]->i_offset = (unsigned)vlc_mrand48()
280 % __MAX( 1, p_sys->i_height[Y_PLANE] - 10 )
281 + 5;
283 p_sys->p_BR_lines[i_b]->b_blue_red = (unsigned)vlc_mrand48() & 0x01;
285 p_sys->p_BR_lines[i_b]->i_stop_trigger = p_sys->i_cur_time
286 + (uint64_t)vlc_mrand48() % BR_LINES_DURATION
287 + BR_LINES_DURATION / 2;
289 break;
291 p_sys->i_BR_line_trigger = p_sys->i_cur_time
292 + (uint64_t)vlc_mrand48() % BR_LINES_GENERATOR_PERIOD
293 + BR_LINES_GENERATOR_PERIOD / 2;
297 /* manage and apply current blue/red lines */
298 for ( uint8_t i_b = 0; i_b < MAX_BLUE_RED_LINES; i_b++ )
299 if ( p_sys->p_BR_lines[i_b] ) {
300 /* remove outdated ones */
301 if ( p_sys->p_BR_lines[i_b]->i_stop_trigger <= p_sys->i_cur_time ) {
302 FREENULL( p_sys->p_BR_lines[i_b] );
303 continue;
306 /* otherwise apply */
307 for ( int32_t i_p=0; i_p < p_sys->i_planes; i_p++ ) {
308 uint32_t i_pix_ofs = p_sys->p_BR_lines[i_b]->i_offset
309 * p_pic_out->p[i_p].i_visible_lines
310 / p_sys->i_height[Y_PLANE]
311 * p_pic_out->p[i_p].i_pitch;
313 switch ( i_p ) {
314 case Y_PLANE:
315 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs], 127,
316 p_pic_out->p[i_p].i_visible_pitch);
317 break;
318 case U_PLANE:
319 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs],
320 (p_sys->p_BR_lines[i_b]->b_blue_red?255:0),
321 p_pic_out->p[i_p].i_visible_pitch);
322 break;
323 case V_PLANE:
324 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs],
325 (p_sys->p_BR_lines[i_b]->b_blue_red?0:255),
326 p_pic_out->p[i_p].i_visible_pitch);
327 break;
332 return VLC_SUCCESS;
336 * insert randomly blue and red dots on the picture
338 static void vhs_blue_red_dots_effect( filter_t *p_filter, picture_t *p_pic_out ) {
339 #define BR_DOTS_RATIO 10000
341 filter_sys_t *p_sys = p_filter->p_sys;
343 for ( int32_t i_dots = 0;
344 i_dots < p_sys->i_width[Y_PLANE] * p_sys->i_height[Y_PLANE] / BR_DOTS_RATIO;
345 i_dots++) {
347 uint32_t i_length = (unsigned)vlc_mrand48()
348 % __MAX( 1, p_sys->i_width[Y_PLANE] / 30 ) + 1;
350 uint16_t i_x = (unsigned)vlc_mrand48()
351 % __MAX( 1, p_sys->i_width[Y_PLANE] - i_length );
352 uint16_t i_y = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
353 bool b_color = ( ( (unsigned)vlc_mrand48() % 2 ) == 0);
355 for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ ) {
356 uint32_t i_pix_ofs = i_y
357 * p_pic_out->p[i_p].i_visible_lines
358 / p_sys->i_height[Y_PLANE]
359 * p_pic_out->p[i_p].i_pitch
360 + i_x
361 * p_pic_out->p[i_p].i_pixel_pitch;
363 uint32_t i_length_in_plane = i_length
364 * p_pic_out->p[i_p].i_visible_pitch
365 / p_pic_out->p[Y_PLANE].i_visible_pitch;
367 switch ( i_p ) {
368 case Y_PLANE:
369 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs], 127,
370 i_length_in_plane );
371 break;
372 case U_PLANE:
373 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs],
374 (b_color?255:0),
375 i_length_in_plane );
376 break;
377 case V_PLANE:
378 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs],
379 (b_color?0:255),
380 i_length_in_plane );
381 break;
388 * sliding effects
390 static int vhs_sliding_effect( filter_t *p_filter, picture_t *p_pic_out ) {
391 filter_sys_t *p_sys = p_filter->p_sys;
394 * one shot offset section
397 #define OFFSET_AVERAGE_PERIOD VLC_TICK_FROM_SEC(10)
399 /* start trigger to be (re)initialized */
400 if ( p_sys->i_offset_trigger == 0
401 || p_sys->i_sliding_speed != 0 ) { /* do not mix sliding and offset */
403 /* random trigger for offset effect */
404 p_sys->i_offset_trigger = p_sys->i_cur_time
405 + ((uint64_t) vlc_mrand48() ) % OFFSET_AVERAGE_PERIOD
406 + OFFSET_AVERAGE_PERIOD / 2;
407 p_sys->i_offset_ofs = 0;
408 } else if (p_sys->i_offset_trigger <= p_sys->i_cur_time) {
409 /* trigger for offset effect occurs */
410 p_sys->i_offset_trigger = 0;
411 p_sys->i_offset_ofs = (uint32_t)vlc_mrand48()
412 % p_sys->i_height[Y_PLANE];
414 else
415 p_sys->i_offset_ofs = 0;
419 * phase section
422 #define MAX_PHASE_OFS (p_sys->i_height[Y_PLANE]*100/15)
424 p_sys->i_phase_speed += MOD( (int32_t)vlc_mrand48(), 3) - 1;
425 p_sys->i_phase_ofs += p_sys->i_phase_speed;
426 p_sys->i_phase_ofs = VLC_CLIP( p_sys->i_phase_ofs, -MAX_PHASE_OFS, +MAX_PHASE_OFS);
427 if ( abs( p_sys->i_phase_ofs ) >= MAX_PHASE_OFS )
428 p_sys->i_phase_speed = 0;
432 * sliding section
435 #define SLIDING_AVERAGE_PERIOD VLC_TICK_FROM_SEC(20)
436 #define SLIDING_AVERAGE_DURATION VLC_TICK_FROM_SEC(3)
438 /* start trigger to be (re)initialized */
439 if ( ( p_sys->i_sliding_stop_trig == 0 ) &&
440 ( p_sys->i_sliding_trigger == 0 ) &&
441 ( p_sys->i_sliding_speed == 0 ) ) {
443 /* random trigger which enable sliding effect */
444 p_sys->i_sliding_trigger = p_sys->i_cur_time
445 + (uint64_t)vlc_mrand48() % SLIDING_AVERAGE_PERIOD
446 + SLIDING_AVERAGE_PERIOD / 2;
449 /* start trigger just occurs */
450 else if ( ( p_sys->i_sliding_stop_trig == 0 ) &&
451 ( p_sys->i_sliding_trigger <= p_sys->i_cur_time ) &&
452 ( p_sys->i_sliding_speed == 0 ) ) {
454 /* init sliding parameters */
455 p_sys->i_sliding_trigger = 0;
456 p_sys->i_sliding_stop_trig = p_sys->i_cur_time
457 + (uint64_t)vlc_mrand48() % SLIDING_AVERAGE_DURATION
458 + SLIDING_AVERAGE_DURATION / 2;
459 p_sys->i_sliding_ofs = 0;
460 /* note: sliding speed unit = image per 100 s */
461 p_sys->i_sliding_speed = MOD( (int32_t)vlc_mrand48(), 1001 ) - 500;
462 p_sys->i_sliding_type_duplicate = (unsigned)vlc_mrand48() & 0x01;
465 /* stop trigger disabling sliding effect occurs */
466 else if ( ( p_sys->i_sliding_stop_trig <= p_sys->i_cur_time )
467 && ( p_sys->i_sliding_trigger == 0 ) ) {
469 /* first increase speed to ensure we will stop sliding on plain pict */
470 if ( abs( p_sys->i_sliding_speed ) < 5 )
471 p_sys->i_sliding_speed += 1;
473 /* check if offset is close to 0 and then ready to stop */
474 if ( abs( p_sys->i_sliding_ofs ) < abs( p_sys->i_sliding_speed
475 * p_sys->i_height[Y_PLANE]
476 * ( p_sys->i_cur_time - p_sys->i_last_time ) / CLOCK_FREQ )
477 || abs( p_sys->i_sliding_ofs ) < p_sys->i_height[Y_PLANE] * 100 / 20 ) {
479 /* reset sliding parameters */
480 p_sys->i_sliding_ofs = p_sys->i_sliding_speed = 0;
481 p_sys->i_sliding_trigger = p_sys->i_sliding_stop_trig = 0;
482 p_sys->i_sliding_type_duplicate = false;
486 /* update offset */
487 p_sys->i_sliding_ofs = MOD( p_sys->i_sliding_ofs
488 + p_sys->i_sliding_speed * p_sys->i_height[Y_PLANE]
489 * ( p_sys->i_cur_time - p_sys->i_last_time)
490 / CLOCK_FREQ,
491 p_sys->i_height[Y_PLANE] * 100 );
493 return vhs_sliding_effect_apply( p_filter, p_pic_out );
497 * apply both sliding and offset effect
499 static int vhs_sliding_effect_apply( filter_t *p_filter, picture_t *p_pic_out )
501 filter_sys_t *p_sys = p_filter->p_sys;
503 for ( uint8_t i_p = 0; i_p < p_pic_out->i_planes; i_p++ ) {
504 /* first allocate temporary buffer for swap operation */
505 uint8_t *p_temp_buf;
506 if ( !p_sys->i_sliding_type_duplicate ) {
507 p_temp_buf= calloc( p_pic_out->p[i_p].i_lines
508 * p_pic_out->p[i_p].i_pitch, sizeof(uint8_t) );
509 if ( unlikely( !p_temp_buf ) )
510 return VLC_ENOMEM;
511 memcpy( p_temp_buf, p_pic_out->p[i_p].p_pixels,
512 p_pic_out->p[i_p].i_lines * p_pic_out->p[i_p].i_pitch );
514 else
515 p_temp_buf = p_pic_out->p[i_p].p_pixels;
517 /* copy lines to output_pic */
518 for ( int32_t i_y = 0; i_y < p_pic_out->p[i_p].i_visible_lines; i_y++ )
520 int32_t i_ofs = p_sys->i_offset_ofs + p_sys->i_sliding_ofs;
522 if ( ( p_sys->i_sliding_speed == 0 ) || !p_sys->i_sliding_type_duplicate )
523 i_ofs += p_sys->i_phase_ofs;
525 i_ofs = MOD( i_ofs / 100, p_sys->i_height[Y_PLANE] );
526 i_ofs *= p_pic_out->p[i_p].i_visible_lines;
527 i_ofs /= p_sys->i_height[Y_PLANE];
529 memcpy( &p_pic_out->p[i_p].p_pixels[ i_y * p_pic_out->p[i_p].i_pitch ],
530 &p_temp_buf[ ( ( i_y + i_ofs ) % p_pic_out->p[i_p].i_visible_lines ) * p_pic_out->p[i_p].i_pitch ],
531 p_pic_out->p[i_p].i_visible_pitch );
533 if ( !p_sys->i_sliding_type_duplicate )
534 free(p_temp_buf);
537 return VLC_SUCCESS;