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 ¶m_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 *)¶ms_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