1 /*
2  * Copyright (C) 2000-2018 the xine project
3  *
4  * This file is part of xine, a free video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * mplayer's noise filter, ported by Jason Tackaberry.  Original filter
21  * is copyright 2002 Michael Niedermayer <michaelni@gmx.at>
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include "planar.h"
29 
30 #include <xine/xine_internal.h>
31 #include <xine/post.h>
32 #include <xine/xineutils.h>
33 #include <math.h>
34 #include <pthread.h>
35 
36 #ifdef ARCH_X86
37 #include "x86/noise.h"
38 #endif
39 
40 #define MAX_NOISE 4096
41 #define MAX_SHIFT 1024
42 #define MAX_RES (MAX_NOISE-MAX_SHIFT)
43 
44 typedef struct noise_param_t {
45     void (*lineNoise)(uint8_t *dst, const uint8_t *src, const int8_t *noise, int len, int shift);
46     void (*lineNoiseAvg)(uint8_t *dst, const uint8_t *src, int len, int8_t **shift);
47 
48     int strength,
49         uniform,
50         temporal,
51         quality,
52         averaged,
53         pattern,
54         shiftptr;
55     int8_t *noise,
56            *prev_shift[MAX_RES][3];
57 } noise_param_t;
58 
59 static int nonTempRandShift[MAX_RES]= {-1};
60 
61 static const int patt[4] = {
62     -1,0,1,0
63 };
64 
65 #define RAND_N(range) ((int) ((double)range*rand()/(RAND_MAX+1.0)))
initNoise(noise_param_t * fp)66 static int8_t *initNoise(noise_param_t *fp){
67     int strength= fp->strength;
68     int uniform= fp->uniform;
69     int averaged= fp->averaged;
70     int pattern= fp->pattern;
71     int8_t *noise;
72     int i, j;
73 
74     noise = xine_mallocz_aligned(MAX_NOISE*sizeof(int8_t));
75     srand(123457);
76 
77     for(i=0,j=0; i<MAX_NOISE; i++,j++)
78     {
79         if(uniform) {
80             if (averaged) {
81                     if (pattern) {
82                     noise[i]= (RAND_N(strength) - strength/2)/6
83                         +patt[j%4]*strength*0.25/3;
84                 } else {
85                     noise[i]= (RAND_N(strength) - strength/2)/3;
86                     }
87             } else {
88                     if (pattern) {
89                     noise[i]= (RAND_N(strength) - strength/2)/2
90                         + patt[j%4]*strength*0.25;
91                 } else {
92                     noise[i]= RAND_N(strength) - strength/2;
93                     }
94             }
95             } else {
96             double x1, x2, w, y1;
97             do {
98                 x1 = 2.0 * rand()/(float)RAND_MAX - 1.0;
99                 x2 = 2.0 * rand()/(float)RAND_MAX - 1.0;
100                 w = x1 * x1 + x2 * x2;
101             } while ( w >= 1.0 );
102 
103             w = sqrt( (-2.0 * log( w ) ) / w );
104             y1= x1 * w;
105             y1*= strength / sqrt(3.0);
106             if (pattern) {
107                 y1 /= 2;
108                 y1 += patt[j%4]*strength*0.35;
109             }
110             if     (y1<-128) y1=-128;
111             else if(y1> 127) y1= 127;
112             if (averaged) y1 /= 3.0;
113             noise[i]= (int)y1;
114         }
115         if (RAND_N(6) == 0) j--;
116     }
117 
118 
119     for (i = 0; i < MAX_RES; i++)
120         for (j = 0; j < 3; j++)
121         fp->prev_shift[i][j] = noise + (rand()&(MAX_SHIFT-1));
122 
123     if(nonTempRandShift[0]==-1){
124         for(i=0; i<MAX_RES; i++){
125             nonTempRandShift[i]= rand()&(MAX_SHIFT-1);
126         }
127     }
128 
129     fp->noise= noise;
130     fp->shiftptr= 0;
131     return noise;
132 }
133 
lineNoise_C(uint8_t * dst,const uint8_t * src,const int8_t * noise,int len,int shift)134 static inline void lineNoise_C(uint8_t *dst, const uint8_t *src, const int8_t *noise, int len, int shift)
135 {
136     int i;
137     noise+= shift;
138     for(i=0; i<len; i++)
139     {
140         int v= src[i]+ noise[i];
141         if(v>255)   dst[i]=255; //FIXME optimize
142         else if(v<0)    dst[i]=0;
143         else        dst[i]=v;
144     }
145 }
146 
147 /***************************************************************************/
148 
lineNoiseAvg_C(uint8_t * dst,const uint8_t * src,int len,int8_t ** shift)149 static inline void lineNoiseAvg_C(uint8_t *dst, const uint8_t *src, int len, int8_t **shift)
150 {
151     int i;
152     const int8_t *src2= (const int8_t*)src;
153 
154     for(i=0; i<len; i++)
155     {
156         const int n= shift[0][i] + shift[1][i] + shift[2][i];
157         dst[i]= src2[i]+((n*src2[i])>>7);
158     }
159 }
160 
161 /***************************************************************************/
162 
noise(uint8_t * dst,const uint8_t * src,int dstStride,int srcStride,int width,int height,noise_param_t * fp)163 static void noise(uint8_t *dst, const uint8_t *src, int dstStride, int srcStride, int width, int height, noise_param_t *fp)
164 {
165     int8_t *noise= fp->noise;
166     int y;
167     int shift=0;
168 
169     if(!noise)
170     {
171         if(src==dst) return;
172 
173         if(dstStride==srcStride) memcpy(dst, src, srcStride*height);
174         else
175         {
176             for(y=0; y<height; y++)
177             {
178                 memcpy(dst, src, width);
179                 dst+= dstStride;
180                 src+= srcStride;
181             }
182         }
183         return;
184     }
185 
186     for(y=0; y<height; y++)
187     {
188         if(fp->temporal)    shift=  rand()&(MAX_SHIFT  -1);
189         else                shift= nonTempRandShift[y];
190 
191         if(fp->quality==0) shift&= ~7;
192         if (fp->averaged) {
193             fp->lineNoiseAvg(dst, src, width, fp->prev_shift[y]);
194             fp->prev_shift[y][fp->shiftptr] = noise + shift;
195         } else {
196             fp->lineNoise(dst, src, noise, width, shift);
197         }
198         dst+= dstStride;
199         src+= srcStride;
200     }
201     fp->shiftptr++;
202     if (fp->shiftptr == 3) fp->shiftptr = 0;
203 }
204 
205 
206 typedef struct post_plugin_noise_s post_plugin_noise_t;
207 
208 /*
209  * this is the struct used by "parameters api"
210  */
211 typedef struct noise_parameters_s {
212     int luma_strength, chroma_strength,
213         type, quality, pattern;
214 } noise_parameters_t;
215 
216 static const char *const enum_types[] = {"uniform", "gaussian", NULL};
217 static const char *const enum_quality[] = {"fixed", "temporal", "averaged temporal", NULL};
218 
219 /*
220  * description of params struct
221  */
222 START_PARAM_DESCR( noise_parameters_t )
223 PARAM_ITEM( POST_PARAM_TYPE_INT, luma_strength, NULL, 0, 100, 0,
224     "Amount of noise to add to luma channel" )
225 PARAM_ITEM( POST_PARAM_TYPE_INT, chroma_strength, NULL, 0, 100, 0,
226     "Amount of noise to add to chroma channel" )
227 PARAM_ITEM( POST_PARAM_TYPE_INT, quality, (char **)enum_quality, 0, 0, 0,
228     "Quality level of noise" )
229 PARAM_ITEM( POST_PARAM_TYPE_INT, type, (char **)enum_types, 0, 0, 0,
230     "Type of noise" )
231 PARAM_ITEM( POST_PARAM_TYPE_BOOL, pattern, NULL, 0, 1, 0,
232     "Mix random noise with a (semi)regular pattern" )
233 END_PARAM_DESCR( param_descr )
234 
235 
236 /* plugin structure */
237 struct post_plugin_noise_s {
238   post_plugin_t post;
239 
240   /* private data */
241   noise_param_t params[2]; // luma and chroma
242 
243   pthread_mutex_t    lock;
244 };
245 
246 
set_parameters(xine_post_t * this_gen,const void * param_gen)247 static int set_parameters (xine_post_t *this_gen, const void *param_gen)
248 {
249     post_plugin_noise_t *this = (post_plugin_noise_t *)this_gen;
250     const noise_parameters_t *param = (const noise_parameters_t *)param_gen;
251     int i;
252 
253     pthread_mutex_lock (&this->lock);
254     for (i = 0; i < 2; i++) {
255         this->params[i].uniform = (param->type == 0);
256         this->params[i].temporal = (param->quality >= 1);
257         this->params[i].averaged = (param->quality == 2);
258         this->params[i].quality = 1;
259         this->params[i].pattern = param->pattern;
260     }
261     this->params[0].strength = param->luma_strength;
262     this->params[1].strength = param->chroma_strength;
263     pthread_mutex_unlock (&this->lock);
264     initNoise(&this->params[0]);
265     initNoise(&this->params[1]);
266     return 1;
267 }
268 
get_parameters(xine_post_t * this_gen,void * param_gen)269 static int get_parameters (xine_post_t *this_gen, void *param_gen)
270 {
271     post_plugin_noise_t *this = (post_plugin_noise_t *)this_gen;
272     noise_parameters_t *param = (noise_parameters_t *)param_gen;
273 
274     pthread_mutex_lock (&this->lock);
275     param->type = (this->params[0].uniform == 0);
276     if (this->params[0].averaged)
277         param->quality = 2;
278     else if (this->params[0].temporal)
279         param->quality = 1;
280     else
281         param->quality = 0;
282     param->pattern = this->params[0].pattern;
283     param->luma_strength = this->params[0].strength;
284     param->chroma_strength = this->params[1].strength;
285     pthread_mutex_unlock (&this->lock);
286     return 1;
287 }
288 
get_param_descr(void)289 static xine_post_api_descr_t *get_param_descr (void) {
290     return &param_descr;
291 }
292 
get_help(void)293 static char *get_help (void) {
294   return _("Adds random noise to the video.\n"
295            "\n"
296            "Parameters:\n"
297            "  luma_strength: strength of noise added to luma channel "
298            "(0-100, default: 8)\n"
299            "  chroma_strength: strength of noise added to chroma channel "
300            "(0-100, default: 5)\n"
301            "  quality: quality level of the noise.  fixed: constant noise "
302            "pattern; temporal: noise pattern changes between frames; "
303            "averaged temporal: smoother noise pattern that changes between "
304            "frames.  (default: averaged temporal)\n"
305            "  type: Type of noise: uniform or gaussian.  (default: "
306            "gaussian)\n"
307            "  pattern: Mix random noise with a (semi)regular pattern. "
308            "(default: False)\n"
309            "\n"
310            "* mplayer's noise (C) Michael Niedermayer\n"
311            );
312 }
313 
314 
noise_dispose(post_plugin_t * this_gen)315 static void noise_dispose(post_plugin_t *this_gen)
316 {
317     post_plugin_noise_t *this = (post_plugin_noise_t *)this_gen;
318 
319     if (_x_post_dispose(this_gen)) {
320         pthread_mutex_destroy(&this->lock);
321         xine_freep_aligned(&this->params[0].noise);
322         xine_freep_aligned(&this->params[1].noise);
323         free(this);
324     }
325 }
326 
327 
noise_intercept_frame(post_video_port_t * port,vo_frame_t * frame)328 static int noise_intercept_frame(post_video_port_t *port, vo_frame_t *frame)
329 {
330     (void)port;
331     return (frame->format == XINE_IMGFMT_YV12 || frame->format == XINE_IMGFMT_YUY2);
332 }
333 
334 
noise_draw(vo_frame_t * frame,xine_stream_t * stream)335 static int noise_draw(vo_frame_t *frame, xine_stream_t *stream)
336 {
337     post_video_port_t *port = (post_video_port_t *)frame->port;
338     post_plugin_noise_t *this = (post_plugin_noise_t *)port->post;
339     vo_frame_t *out_frame;
340     int skip;
341 
342     if (frame->bad_frame ||
343         (this->params[0].strength == 0 && this->params[1].strength == 0)) {
344         _x_post_frame_copy_down(frame, frame->next);
345         skip = frame->next->draw(frame->next, stream);
346         _x_post_frame_copy_up(frame, frame->next);
347         return skip;
348     }
349 
350     frame->lock(frame);
351     out_frame = port->original_port->get_frame(port->original_port,
352         frame->width, frame->height, frame->ratio, frame->format,
353         frame->flags | VO_BOTH_FIELDS);
354 
355     _x_post_frame_copy_down(frame, out_frame);
356     pthread_mutex_lock (&this->lock);
357 
358     if (frame->format == XINE_IMGFMT_YV12) {
359         noise(out_frame->base[0], frame->base[0],
360               out_frame->pitches[0], frame->pitches[0],
361               frame->width, frame->height, &this->params[0]);
362         noise(out_frame->base[1], frame->base[1],
363               out_frame->pitches[1], frame->pitches[1],
364               frame->width/2, frame->height/2, &this->params[1]);
365         noise(out_frame->base[2], frame->base[2],
366               out_frame->pitches[2], frame->pitches[2],
367               frame->width/2, frame->height/2, &this->params[1]);
368     } else {
369         // Chroma strength is ignored for YUY2.
370         noise(out_frame->base[0], frame->base[0],
371               out_frame->pitches[0], frame->pitches[0],
372               frame->width * 2, frame->height, &this->params[0]);
373     }
374 
375 #ifdef ARCH_X86
376     if (xine_mm_accel() & MM_ACCEL_X86_MMX)
377         __asm__ __volatile__ ("emms\n\t");
378     if (xine_mm_accel() & MM_ACCEL_X86_MMXEXT)
379         __asm__ __volatile__ ("sfence\n\t");
380 #endif
381 
382     pthread_mutex_unlock (&this->lock);
383     skip = out_frame->draw(out_frame, stream);
384     _x_post_frame_copy_up(frame, out_frame);
385 
386     out_frame->free(out_frame);
387     frame->free(frame);
388     return skip;
389 }
390 
noise_open_plugin(post_class_t * class_gen,int inputs,xine_audio_port_t ** audio_target,xine_video_port_t ** video_target)391 static post_plugin_t *noise_open_plugin(post_class_t *class_gen, int inputs,
392                      xine_audio_port_t **audio_target,
393                      xine_video_port_t **video_target)
394 {
395     post_plugin_noise_t *this = calloc(1, sizeof(post_plugin_noise_t));
396     post_in_t         *input;
397     post_out_t        *output;
398     post_video_port_t *port;
399 
400     static const xine_post_api_t post_api = {
401       .set_parameters  = set_parameters,
402       .get_parameters  = get_parameters,
403       .get_param_descr = get_param_descr,
404       .get_help        = get_help,
405     };
406     static const xine_post_in_t params_input = {
407       .name = "parameters",
408       .type = XINE_POST_DATA_PARAMETERS,
409       .data = (void *)&post_api,
410     };
411     static const noise_parameters_t init_params = {
412       .luma_strength = 8,
413       .chroma_strength = 5,
414       .type = 1,
415       .quality = 2,
416     };
417 
418     if (!this || !video_target || !video_target[0]) {
419       free(this);
420       return NULL;
421     }
422 
423     (void)class_gen;
424     (void)inputs;
425     (void)audio_target;
426 
427     _x_post_init(&this->post, 0, 1);
428 
429     pthread_mutex_init(&this->lock, NULL);
430 
431     port = _x_post_intercept_video_port(&this->post, video_target[0], &input, &output);
432     port->intercept_frame       = noise_intercept_frame;
433     port->new_frame->draw       = noise_draw;
434 
435     xine_list_push_back(this->post.input, (void *)&params_input);
436 
437     input->xine_in.name     = "video";
438     output->xine_out.name   = "filtered video";
439 
440     this->post.xine_post.video_input[0] = &port->new_port;
441 
442     this->post.dispose = noise_dispose;
443 
444     set_parameters ((xine_post_t *)this, &init_params);
445 
446     /* */
447 
448     this->params[0].lineNoise = lineNoise_C;
449     this->params[0].lineNoiseAvg = lineNoiseAvg_C;
450 #ifdef ARCH_X86
451     if (xine_mm_accel() & MM_ACCEL_X86_MMX) {
452         this->params[0].lineNoise = lineNoise_MMX;
453         this->params[0].lineNoiseAvg = lineNoiseAvg_MMX;
454     }
455     if (xine_mm_accel() & MM_ACCEL_X86_MMXEXT) {
456         this->params[0].lineNoise = lineNoise_MMX2;
457     }
458 #endif
459     this->params[1].lineNoise    = this->params[0].lineNoise;
460     this->params[1].lineNoiseAvg = this->params[0].lineNoiseAvg;
461 
462     return &this->post;
463 }
464 
noise_init_plugin(xine_t * xine,const void * data)465 void *noise_init_plugin(xine_t *xine, const void *data)
466 {
467     static const post_class_t post_noise_class = {
468         .open_plugin     = noise_open_plugin,
469         .identifier      = "noise",
470         .description     = N_("Adds noise"),
471         .dispose         = NULL,
472     };
473 
474     (void)xine;
475     (void)data;
476 
477     return (void *)&post_noise_class;
478 }
479