upnp: remove m_server_list_lock, p_server_list is already protected by s_lock
[vlc.git] / modules / video_filter / vhs.c
1 /*****************************************************************************
2  * vhs.c : VHS effect 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 #include <vlc_rand.h>
36 #include <vlc_mtime.h>
37
38 #include "filter_picture.h"
39
40 static inline int64_t MOD(int64_t a, int64_t b) {
41     return ( ( a % b ) + b ) % b; }
42
43 #define MAX_BLUE_RED_LINES 100
44
45 typedef struct {
46     int32_t  i_offset;
47     uint16_t i_intensity;
48     bool     b_blue_red;
49     mtime_t  i_stop_trigger;
50 } blue_red_line_t;
51
52 struct filter_sys_t {
53
54     /* general data */
55     bool b_init;
56     int32_t  i_planes;
57     int32_t *i_height; /* note: each plane may have different dimensions */
58     int32_t *i_width;
59     int32_t *i_visible_pitch;
60     mtime_t  i_start_time;
61     mtime_t  i_last_time;
62     mtime_t  i_cur_time;
63
64     /* sliding & offset effect */
65     int32_t  i_phase_speed;
66     int32_t  i_phase_ofs;
67     int32_t  i_offset_ofs;
68     int32_t  i_sliding_ofs;
69     int32_t  i_sliding_speed;
70     mtime_t  i_offset_trigger;
71     mtime_t  i_sliding_trigger;
72     mtime_t  i_sliding_stop_trig;
73     bool     i_sliding_type_duplicate;
74
75     /* blue red lines effect */
76     mtime_t  i_BR_line_trigger;
77     blue_red_line_t *p_BR_lines[MAX_BLUE_RED_LINES];
78
79 };
80
81 /*****************************************************************************
82  * Prototypes
83  *****************************************************************************/
84
85 static picture_t *Filter( filter_t *, picture_t * );
86
87 static int  vhs_allocate_data( filter_t *, picture_t * );
88 static void vhs_free_allocated_data( filter_t * );
89
90 static int  vhs_blue_red_line_effect( filter_t *, picture_t * );
91 static void vhs_blue_red_dots_effect( filter_t *, picture_t * );
92 static int  vhs_sliding_effect( filter_t *, picture_t * );
93
94 static int  vhs_sliding_effect_apply( filter_t *, picture_t * );
95
96 /*****************************************************************************
97  * Module descriptor
98  *****************************************************************************/
99
100 static int  Open ( vlc_object_t * );
101 static void Close( vlc_object_t * );
102
103 vlc_module_begin()
104     set_description( N_("VHS movie effect video filter") )
105     set_shortname(   N_("VHS movie" ) )
106     set_capability( "video filter2", 0 )
107     set_category( CAT_VIDEO )
108     set_subcategory( SUBCAT_VIDEO_VFILTER )
109
110     set_callbacks( Open, Close )
111 vlc_module_end()
112
113 /**
114  * Open the filter
115  */
116 static int Open( vlc_object_t *p_this )
117 {
118     filter_t *p_filter = (filter_t*)p_this;
119     filter_sys_t *p_sys;
120
121     /* Assert video in match with video out */
122     if( !es_format_IsSimilar( &p_filter->fmt_in, &p_filter->fmt_out ) ) {
123         msg_Err( p_filter, "Input and output format does not match" );
124         return VLC_EGENERIC;
125     }
126
127     /* Reject 0 bpp and unsupported chroma */
128     const vlc_fourcc_t fourcc = p_filter->fmt_in.video.i_chroma;
129     const vlc_chroma_description_t *p_chroma =
130         vlc_fourcc_GetChromaDescription( p_filter->fmt_in.video.i_chroma );
131     if( !p_chroma || p_chroma->pixel_size == 0
132         || p_chroma->plane_count < 3 || p_chroma->pixel_size > 1
133         || !vlc_fourcc_IsYUV( fourcc ) )
134     {
135         msg_Err( p_filter, "Unsupported chroma (%4.4s)", (char*)&fourcc );
136         return VLC_EGENERIC;
137     }
138
139     /* Allocate structure */
140     p_filter->p_sys = p_sys = calloc(1, sizeof(*p_sys) );
141     if( unlikely( !p_sys ) )
142         return VLC_ENOMEM;
143
144     /* init data */
145     p_filter->pf_video_filter = Filter;
146     p_sys->i_start_time = p_sys->i_cur_time = p_sys->i_last_time = mdate();
147
148     return VLC_SUCCESS;
149 }
150
151 /**
152  * Close the filter
153  */
154 static void Close( vlc_object_t *p_this ) {
155     filter_t *p_filter = (filter_t*)p_this;
156     filter_sys_t *p_sys = p_filter->p_sys;
157
158     /* Free allocated memory */
159     vhs_free_allocated_data( p_filter );
160     free( p_sys );
161 }
162
163 /**
164  * Filter a picture
165  */
166 static picture_t *Filter( filter_t *p_filter, picture_t *p_pic_in ) {
167     if( unlikely( !p_pic_in || !p_filter) )
168         return NULL;
169
170     filter_sys_t *p_sys = p_filter->p_sys;
171
172     picture_t *p_pic_out = filter_NewPicture( p_filter );
173     if( unlikely( !p_pic_out ) ) {
174         picture_Release( p_pic_in );
175         return NULL;
176     }
177
178    /*
179     * manage time
180     */
181     p_sys->i_last_time = p_sys->i_cur_time;
182     p_sys->i_cur_time = mdate();
183
184    /*
185     * allocate data
186     */
187     if ( unlikely( !p_sys->b_init ) )
188         if ( unlikely( vhs_allocate_data( p_filter, p_pic_in ) != VLC_SUCCESS ) ) {
189             picture_Release( p_pic_in );
190             return NULL;
191         }
192     p_sys->b_init = true;
193
194    /*
195     * preset output pic: raw copy src to dst
196     */
197     picture_CopyPixels(p_pic_out, p_pic_in);
198
199    /*
200     * apply effects on picture
201     */
202     if ( unlikely( vhs_blue_red_line_effect( p_filter, p_pic_out ) != VLC_SUCCESS ) )
203         return CopyInfoAndRelease( p_pic_out, p_pic_in );
204
205     if ( unlikely( vhs_sliding_effect(p_filter, p_pic_out ) != VLC_SUCCESS ) )
206         return CopyInfoAndRelease( p_pic_out, p_pic_in );
207
208     vhs_blue_red_dots_effect( p_filter, p_pic_out );
209
210     return CopyInfoAndRelease( p_pic_out, p_pic_in );
211 }
212
213 /*
214  * Allocate data
215  */
216 static int vhs_allocate_data( filter_t *p_filter, picture_t *p_pic_in ) {
217     filter_sys_t *p_sys = p_filter->p_sys;
218
219     vhs_free_allocated_data( p_filter );
220
221    /*
222     * take into account different characteristics for each plane
223     */
224     p_sys->i_planes = p_pic_in->i_planes;
225     p_sys->i_height = calloc( p_sys->i_planes, sizeof(int32_t) );
226     p_sys->i_width  = calloc( p_sys->i_planes, sizeof(int32_t) );
227     p_sys->i_visible_pitch = calloc( p_sys->i_planes, sizeof(int32_t) );
228
229     if( unlikely( !p_sys->i_height || !p_sys->i_width || !p_sys->i_visible_pitch ) ) {
230         vhs_free_allocated_data( p_filter );
231         return VLC_ENOMEM;
232     }
233
234     for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++) {
235         p_sys->i_visible_pitch [i_p] = (int) p_pic_in->p[i_p].i_visible_pitch;
236         p_sys->i_height[i_p] = (int) p_pic_in->p[i_p].i_visible_lines;
237         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;
238     }
239     return VLC_SUCCESS;
240 }
241
242 /**
243  * Free allocated data
244  */
245 static void vhs_free_allocated_data( filter_t *p_filter ) {
246     filter_sys_t *p_sys = p_filter->p_sys;
247
248     for ( uint32_t i_b = 0; i_b < MAX_BLUE_RED_LINES; i_b++ )
249         FREENULL( p_sys->p_BR_lines[i_b] );
250
251     p_sys->i_planes = 0;
252     FREENULL( p_sys->i_height );
253     FREENULL( p_sys->i_width );
254     FREENULL( p_sys->i_visible_pitch );
255 }
256
257
258 /**
259  * Horizontal blue or red lines random management and effect
260  */
261 static int vhs_blue_red_line_effect( filter_t *p_filter, picture_t *p_pic_out ) {
262     filter_sys_t *p_sys = p_filter->p_sys;
263
264 #define BR_LINES_GENERATOR_PERIOD ( CLOCK_FREQ * 50 )
265 #define BR_LINES_DURATION         ( CLOCK_FREQ * 1/50 )
266
267     /* generate new blue or red lines */
268     if ( p_sys->i_BR_line_trigger <= p_sys->i_cur_time ) {
269         for ( uint32_t i_b = 0; i_b < MAX_BLUE_RED_LINES; i_b++ )
270             if (p_sys->p_BR_lines[i_b] == NULL) {
271                 /* allocate data */
272                 p_sys->p_BR_lines[i_b] = calloc( 1, sizeof(blue_red_line_t) );
273                 if ( unlikely( !p_sys->p_BR_lines[i_b] ) )
274                     return VLC_ENOMEM;
275
276                 /* set random parameters */
277                 p_sys->p_BR_lines[i_b]->i_offset = (unsigned)vlc_mrand48()
278                                                  % __MAX( 1, p_sys->i_height[Y_PLANE] - 10 )
279                                                  + 5;
280
281                 p_sys->p_BR_lines[i_b]->b_blue_red = (unsigned)vlc_mrand48() & 0x01;
282
283                 p_sys->p_BR_lines[i_b]->i_stop_trigger = p_sys->i_cur_time
284                                                        + (uint64_t)vlc_mrand48() % BR_LINES_DURATION
285                                                        + BR_LINES_DURATION / 2;
286
287                 break;
288             }
289         p_sys->i_BR_line_trigger = p_sys->i_cur_time
290                                  + (uint64_t)vlc_mrand48() % BR_LINES_GENERATOR_PERIOD
291                                  + BR_LINES_GENERATOR_PERIOD / 2;
292     }
293
294
295     /* manage and apply current blue/red lines */
296     for ( uint8_t i_b = 0; i_b < MAX_BLUE_RED_LINES; i_b++ )
297         if ( p_sys->p_BR_lines[i_b] ) {
298             /* remove outdated ones */
299             if ( p_sys->p_BR_lines[i_b]->i_stop_trigger <= p_sys->i_cur_time ) {
300                 FREENULL( p_sys->p_BR_lines[i_b] );
301                 continue;
302             }
303
304             /* otherwise apply */
305             for ( int32_t i_p=0; i_p < p_sys->i_planes; i_p++ ) {
306                 uint32_t i_pix_ofs = p_sys->p_BR_lines[i_b]->i_offset
307                                    * p_pic_out->p[i_p].i_visible_lines
308                                    / p_sys->i_height[Y_PLANE]
309                                    * p_pic_out->p[i_p].i_pitch;
310
311                 switch ( i_p ) {
312                   case Y_PLANE:
313                     memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs], 127,
314                             p_pic_out->p[i_p].i_visible_pitch);
315                     break;
316                   case U_PLANE:
317                     memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs],
318                             (p_sys->p_BR_lines[i_b]->b_blue_red?255:0),
319                             p_pic_out->p[i_p].i_visible_pitch);
320                     break;
321                   case V_PLANE:
322                     memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs],
323                             (p_sys->p_BR_lines[i_b]->b_blue_red?0:255),
324                             p_pic_out->p[i_p].i_visible_pitch);
325                     break;
326                 }
327
328             }
329         }
330     return VLC_SUCCESS;
331 }
332
333 /**
334  * insert randomly blue and red dots on the picture
335  */
336 static void vhs_blue_red_dots_effect( filter_t *p_filter, picture_t *p_pic_out ) {
337 #define BR_DOTS_RATIO 10000
338
339     filter_sys_t *p_sys = p_filter->p_sys;
340
341     for ( int32_t i_dots = 0;
342           i_dots < p_sys->i_width[Y_PLANE] * p_sys->i_height[Y_PLANE] / BR_DOTS_RATIO;
343           i_dots++) {
344
345         uint32_t i_length = (unsigned)vlc_mrand48()
346                           % __MAX( 1, p_sys->i_width[Y_PLANE] / 30 ) + 1;
347
348         uint16_t i_x = (unsigned)vlc_mrand48()
349                      % __MAX( 1, p_sys->i_width[Y_PLANE] - i_length );
350         uint16_t i_y = (unsigned)vlc_mrand48() % p_sys->i_height[Y_PLANE];
351         bool b_color = ( ( (unsigned)vlc_mrand48() % 2 ) == 0);
352
353         for ( int32_t i_p = 0; i_p < p_sys->i_planes; i_p++ ) {
354             uint32_t i_pix_ofs = i_y
355                                * p_pic_out->p[i_p].i_visible_lines
356                                / p_sys->i_height[Y_PLANE]
357                                * p_pic_out->p[i_p].i_pitch
358                                + i_x
359                                * p_pic_out->p[i_p].i_pixel_pitch;
360
361             uint32_t i_length_in_plane = i_length
362                                        * p_pic_out->p[i_p].i_visible_pitch
363                                        / p_pic_out->p[Y_PLANE].i_visible_pitch;
364
365             switch ( i_p ) {
366               case Y_PLANE:
367                 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs], 127,
368                         i_length_in_plane );
369                 break;
370               case U_PLANE:
371                 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs],
372                         (b_color?255:0),
373                         i_length_in_plane );
374                 break;
375               case V_PLANE:
376                 memset( &p_pic_out->p[i_p].p_pixels[i_pix_ofs],
377                         (b_color?0:255),
378                         i_length_in_plane );
379                 break;
380             }
381         }
382     }
383 }
384
385 /**
386 * sliding effects
387 */
388 static int vhs_sliding_effect( filter_t *p_filter, picture_t *p_pic_out ) {
389     filter_sys_t *p_sys = p_filter->p_sys;
390
391     /**
392     * one shot offset section
393     */
394
395 #define OFFSET_AVERAGE_PERIOD   (10 * CLOCK_FREQ)
396
397     /* start trigger to be (re)initialized */
398     if ( p_sys->i_offset_trigger == 0
399          || p_sys->i_sliding_speed != 0 ) { /* do not mix sliding and offset */
400
401         /* random trigger for offset effect */
402         p_sys->i_offset_trigger = p_sys->i_cur_time
403                                 + ((uint64_t) vlc_mrand48() ) % OFFSET_AVERAGE_PERIOD
404                                 + OFFSET_AVERAGE_PERIOD / 2;
405         p_sys->i_offset_ofs = 0;
406     } else if (p_sys->i_offset_trigger <= p_sys->i_cur_time) {
407         /* trigger for offset effect occurs */
408         p_sys->i_offset_trigger = 0;
409         p_sys->i_offset_ofs = (uint32_t)vlc_mrand48()
410                             % p_sys->i_height[Y_PLANE];
411     }
412     else
413         p_sys->i_offset_ofs = 0;
414
415
416     /**
417     * phase section
418     */
419
420 #define MAX_PHASE_OFS (p_sys->i_height[Y_PLANE]*100/15)
421
422     p_sys->i_phase_speed += MOD( (int32_t)vlc_mrand48(), 3) - 1;
423     p_sys->i_phase_ofs   += p_sys->i_phase_speed;
424     p_sys->i_phase_ofs    = VLC_CLIP( p_sys->i_phase_ofs, -MAX_PHASE_OFS, +MAX_PHASE_OFS);
425     if ( abs( p_sys->i_phase_ofs ) >= MAX_PHASE_OFS )
426         p_sys->i_phase_speed = 0;
427
428
429     /**
430     * sliding section
431     */
432
433 #define SLIDING_AVERAGE_PERIOD   (20 * CLOCK_FREQ)
434 #define SLIDING_AVERAGE_DURATION ( 3 * CLOCK_FREQ)
435
436     /* start trigger to be (re)initialized */
437     if ( ( p_sys->i_sliding_stop_trig  == 0 ) &&
438          ( p_sys->i_sliding_trigger    == 0 ) &&
439          ( p_sys->i_sliding_speed      == 0 ) ) {
440
441         /* random trigger which enable sliding effect */
442         p_sys->i_sliding_trigger = p_sys->i_cur_time
443                                  + (uint64_t)vlc_mrand48() % SLIDING_AVERAGE_PERIOD
444                                  + SLIDING_AVERAGE_PERIOD / 2;
445     }
446
447     /* start trigger just occurs */
448     else if ( ( p_sys->i_sliding_stop_trig  == 0 ) &&
449               ( p_sys->i_sliding_trigger    <= p_sys->i_cur_time ) &&
450               ( p_sys->i_sliding_speed      == 0 ) ) {
451
452         /* init sliding parameters */
453         p_sys->i_sliding_trigger = 0;
454         p_sys->i_sliding_stop_trig = p_sys->i_cur_time
455                                    + (uint64_t)vlc_mrand48() % SLIDING_AVERAGE_DURATION
456                                    + SLIDING_AVERAGE_DURATION / 2;
457         p_sys->i_sliding_ofs = 0;
458         /* note: sliding speed unit = image per 100 s */
459         p_sys->i_sliding_speed = MOD( (int32_t)vlc_mrand48(), 1001 ) - 500;
460         p_sys->i_sliding_type_duplicate = (unsigned)vlc_mrand48() & 0x01;
461     }
462
463     /* stop trigger disabling sliding effect occurs */
464     else if ( ( p_sys->i_sliding_stop_trig  <= p_sys->i_cur_time )
465               && ( p_sys->i_sliding_trigger == 0 ) ) {
466
467         /* first increase speed to ensure we will stop sliding on plain pict */
468         if ( abs( p_sys->i_sliding_speed ) < 5 )
469             p_sys->i_sliding_speed += 1;
470
471         /* check if offset is close to 0 and then ready to stop */
472         if ( abs( p_sys->i_sliding_ofs ) < abs( p_sys->i_sliding_speed
473              * p_sys->i_height[Y_PLANE]
474              * ( p_sys->i_cur_time - p_sys->i_last_time ) / CLOCK_FREQ )
475              || abs( p_sys->i_sliding_ofs ) < p_sys->i_height[Y_PLANE] * 100 / 20 ) {
476
477             /* reset sliding parameters */
478             p_sys->i_sliding_ofs = p_sys->i_sliding_speed = 0;
479             p_sys->i_sliding_trigger = p_sys->i_sliding_stop_trig = 0;
480             p_sys->i_sliding_type_duplicate = false;
481         }
482     }
483
484     /* update offset */
485     p_sys->i_sliding_ofs = MOD( p_sys->i_sliding_ofs
486                                 + p_sys->i_sliding_speed * p_sys->i_height[Y_PLANE]
487                                 * ( p_sys->i_cur_time - p_sys->i_last_time)
488                                 / CLOCK_FREQ,
489                                 p_sys->i_height[Y_PLANE] * 100 );
490
491     return vhs_sliding_effect_apply( p_filter, p_pic_out );
492 }
493
494 /**
495 * apply both sliding and offset effect
496 */
497 static int vhs_sliding_effect_apply( filter_t *p_filter, picture_t *p_pic_out )
498 {
499     filter_sys_t *p_sys = p_filter->p_sys;
500
501     for ( uint8_t i_p = 0; i_p < p_pic_out->i_planes; i_p++ ) {
502         /* first allocate temporary buffer for swap operation */
503         uint8_t *p_temp_buf;
504         if ( !p_sys->i_sliding_type_duplicate ) {
505             p_temp_buf= calloc( p_pic_out->p[i_p].i_lines
506                                 * p_pic_out->p[i_p].i_pitch, sizeof(uint8_t) );
507             if ( unlikely( !p_temp_buf ) )
508                 return VLC_ENOMEM;
509             memcpy( p_temp_buf, p_pic_out->p[i_p].p_pixels,
510                     p_pic_out->p[i_p].i_lines * p_pic_out->p[i_p].i_pitch );
511         }
512         else
513             p_temp_buf = p_pic_out->p[i_p].p_pixels;
514
515         /* copy lines to output_pic */
516         for ( int32_t i_y = 0; i_y < p_pic_out->p[i_p].i_visible_lines; i_y++ )
517         {
518             int32_t i_ofs = p_sys->i_offset_ofs + p_sys->i_sliding_ofs;
519
520             if ( ( p_sys->i_sliding_speed == 0 ) || !p_sys->i_sliding_type_duplicate )
521                 i_ofs += p_sys->i_phase_ofs;
522
523             i_ofs  = MOD( i_ofs / 100, p_sys->i_height[Y_PLANE] );
524             i_ofs *= p_pic_out->p[i_p].i_visible_lines;
525             i_ofs /= p_sys->i_height[Y_PLANE];
526
527             memcpy( &p_pic_out->p[i_p].p_pixels[ i_y * p_pic_out->p[i_p].i_pitch ],
528                     &p_temp_buf[ ( ( i_y + i_ofs ) % p_pic_out->p[i_p].i_visible_lines ) * p_pic_out->p[i_p].i_pitch ],
529                     p_pic_out->p[i_p].i_visible_pitch );
530         }
531         if ( !p_sys->i_sliding_type_duplicate )
532             free(p_temp_buf);
533     }
534
535     return VLC_SUCCESS;
536 }