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 eq (soft video equalizer)
21  * Copyright (C) Richard Felker
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 <pthread.h>
34 
35 
36 #if defined(ARCH_X86)
37 
38 #if defined(ARCH_X86_64)
39 #  define MEM1(reg) "(%"reg")"
40 #  define MEM2(offs,reg)      offs"(%"reg")"
41 #  define BUMPPTR(offs,reg) "\n\tleaq\t"offs"(%"reg"), %"reg
42 #elif defined(ARCH_X86_X32)
43 #  define MEM1(reg) "(%q"reg")"
44 #  define MEM2(offs,reg)      offs"(%q"reg")"
45 #  define BUMPPTR(offs,reg) "\n\tleaq\t"offs"(%q"reg"), %q"reg
46 #else
47 #  define MEM1(reg) "(%"reg")"
48 #  define MEM2(offs,reg)      offs"(%"reg")"
49 #  define BUMPPTR(offs,reg) "\n\tleal\t"offs"(%"reg"), %"reg
50 #endif
51 
process_MMX(unsigned char * dest,int dstride,unsigned char * src,int sstride,int w,int h,int brightness,int contrast)52 static void process_MMX(unsigned char *dest, int dstride, unsigned char *src, int sstride,
53 		    int w, int h, int brightness, int contrast)
54 {
55   int i;
56   int pel;
57   int dstep = dstride - w;
58   int sstep = sstride - w;
59   short cvec[8];
60 
61   contrast = ((contrast + 100) * 256 * 16) / 100;
62   brightness = ((brightness + 100) * 511) / 200 - 128 - contrast / 32;
63 
64   cvec[0] = cvec[1] = cvec[2] = cvec[3] = brightness;
65   cvec[4] = cvec[5] = cvec[6] = cvec[7] = contrast;
66 
67   __asm__ __volatile__ (
68     "\n\tmovq\t"MEM1(     "0")", %%mm3"
69     "\n\tmovq\t"MEM2("8", "0")", %%mm4"
70     "\n\tpxor\t%%mm0, %%mm0"
71     :
72     : "r" (cvec)
73   );
74 
75   while (h--) {
76     __asm__ __volatile__ (
77       "\n\tmovl\t%4, %%eax"
78       "\n\t"ASMALIGN(4)
79       "\n1:"
80       "\n\tmovq\t"MEM1("0")", %%mm1"
81       "\n\tmovq\t"MEM1("0")", %%mm2"
82       "\n\tpunpcklbw\t%%mm0, %%mm1"
83       "\n\tpunpckhbw\t%%mm0, %%mm2"
84       "\n\tpsllw\t$4, %%mm1"
85       "\n\tpsllw\t$4, %%mm2"
86       "\n\tpmulhw\t%%mm4, %%mm1"
87       "\n\tpmulhw\t%%mm4, %%mm2"
88       "\n\tpaddw\t%%mm3, %%mm1"
89       "\n\tpaddw\t%%mm3, %%mm2"
90       "\n\tpackuswb\t%%mm2, %%mm1"
91       BUMPPTR("8","0")
92       "\n\tmovq\t%%mm1, "MEM1("1")
93       BUMPPTR("8","1")
94       "\n\tdecl\t%%eax"
95       "\n\tjnz\t1b"
96       : "=r" (src), "=r" (dest)
97       : "0"  (src), "1"  (dest), "r" (w>>3)
98       : "%eax"
99     );
100 
101     for (i = w & 7; i; i--) {
102       pel = ((*src++ * contrast) >> 12) + brightness;
103       if (pel & 768)
104         pel = (-pel) >> 31;
105       *dest++ = pel;
106     }
107 
108     src += sstep;
109     dest += dstep;
110   }
111   __asm__ __volatile__ ( "emms \n\t" ::: "memory" );
112 }
113 #endif
114 
process_C(unsigned char * dest,int dstride,unsigned char * src,int sstride,int w,int h,int brightness,int contrast)115 static void process_C(unsigned char *dest, int dstride, unsigned char *src, int sstride,
116 		    int w, int h, int brightness, int contrast)
117 {
118 	int i;
119 	int pel;
120 	int dstep = dstride-w;
121 	int sstep = sstride-w;
122 
123 	contrast = ((contrast+100)*256*256)/100;
124 	brightness = ((brightness+100)*511)/200-128 - contrast/512;
125 
126 	while (h--) {
127 		for (i = w; i; i--)
128 		{
129 			pel = ((*src++* contrast)>>16) + brightness;
130 			if(pel&768) pel = (-pel)>>31;
131 			*dest++ = pel;
132 		}
133 		src += sstep;
134 		dest += dstep;
135 	}
136 }
137 
138 static void (*process)(unsigned char *dest, int dstride, unsigned char *src, int sstride,
139 		       int w, int h, int brightness, int contrast);
140 
141 
142 typedef struct post_plugin_eq_s post_plugin_eq_t;
143 
144 /*
145  * this is the struct used by "parameters api"
146  */
147 typedef struct eq_parameters_s {
148 
149   int brightness;
150   int contrast;
151 
152 } eq_parameters_t;
153 
154 /*
155  * description of params struct
156  */
157 START_PARAM_DESCR( eq_parameters_t )
158 PARAM_ITEM( POST_PARAM_TYPE_INT, brightness, NULL, -100, 100, 0,
159             "brightness" )
160 PARAM_ITEM( POST_PARAM_TYPE_INT, contrast, NULL, -100, 100, 0,
161             "contrast" )
162 END_PARAM_DESCR( param_descr )
163 
164 
165 /* plugin structure */
166 struct post_plugin_eq_s {
167   post_plugin_t post;
168 
169   /* private data */
170   eq_parameters_t    params;
171 
172   pthread_mutex_t    lock;
173 };
174 
175 
set_parameters(xine_post_t * this_gen,const void * param_gen)176 static int set_parameters (xine_post_t *this_gen, const void *param_gen) {
177   post_plugin_eq_t *this = (post_plugin_eq_t *)this_gen;
178   const eq_parameters_t *param = (const eq_parameters_t *)param_gen;
179 
180   pthread_mutex_lock (&this->lock);
181 
182   memcpy( &this->params, param, sizeof(eq_parameters_t) );
183 
184   pthread_mutex_unlock (&this->lock);
185 
186   return 1;
187 }
188 
get_parameters(xine_post_t * this_gen,void * param_gen)189 static int get_parameters (xine_post_t *this_gen, void *param_gen) {
190   post_plugin_eq_t *this = (post_plugin_eq_t *)this_gen;
191   eq_parameters_t *param = (eq_parameters_t *)param_gen;
192 
193 
194   memcpy( param, &this->params, sizeof(eq_parameters_t) );
195 
196   return 1;
197 }
198 
get_param_descr(void)199 static xine_post_api_descr_t * get_param_descr (void) {
200   return &param_descr;
201 }
202 
get_help(void)203 static char * get_help (void) {
204   return _("Software equalizer with interactive controls just like the hardware "
205            "equalizer, for cards/drivers that do not support brightness and "
206            "contrast controls in hardware.\n"
207            "\n"
208            "Parameters\n"
209            "  brightness\n"
210            "  contrast\n"
211            "\n"
212            "Note: It is possible to use frontend's control window to set "
213            "these parameters.\n"
214            "\n"
215            "* mplayer's eq (C) Richard Felker\n"
216            );
217 }
218 
219 
eq_dispose(post_plugin_t * this_gen)220 static void eq_dispose(post_plugin_t *this_gen)
221 {
222   post_plugin_eq_t *this = (post_plugin_eq_t *)this_gen;
223 
224   if (_x_post_dispose(this_gen)) {
225     pthread_mutex_destroy(&this->lock);
226     free(this);
227   }
228 }
229 
230 
eq_get_property(xine_video_port_t * port_gen,int property)231 static int eq_get_property(xine_video_port_t *port_gen, int property) {
232   post_video_port_t *port = (post_video_port_t *)port_gen;
233   post_plugin_eq_t *this = (post_plugin_eq_t *)port->post;
234   if( property == XINE_PARAM_VO_BRIGHTNESS )
235     return 65535 * (this->params.brightness + 100) / 200;
236   else if( property == XINE_PARAM_VO_CONTRAST )
237     return 65535 * (this->params.contrast + 100) / 200;
238   else
239     return port->original_port->get_property(port->original_port, property);
240 }
241 
eq_set_property(xine_video_port_t * port_gen,int property,int value)242 static int eq_set_property(xine_video_port_t *port_gen, int property, int value) {
243   post_video_port_t *port = (post_video_port_t *)port_gen;
244   post_plugin_eq_t *this = (post_plugin_eq_t *)port->post;
245   if( property == XINE_PARAM_VO_BRIGHTNESS ) {
246     pthread_mutex_lock (&this->lock);
247     this->params.brightness = (200 * value / 65535) - 100;
248     pthread_mutex_unlock (&this->lock);
249     return value;
250   } else if( property == XINE_PARAM_VO_CONTRAST ) {
251     pthread_mutex_lock (&this->lock);
252     this->params.contrast = (200 * value / 65535) - 100;
253     pthread_mutex_unlock (&this->lock);
254     return value;
255   } else
256     return port->original_port->set_property(port->original_port, property, value);
257 }
258 
259 
eq_intercept_frame(post_video_port_t * port,vo_frame_t * frame)260 static int eq_intercept_frame(post_video_port_t *port, vo_frame_t *frame)
261 {
262   (void)port;
263   return (frame->format == XINE_IMGFMT_YV12 || frame->format == XINE_IMGFMT_YUY2);
264 }
265 
266 
eq_draw(vo_frame_t * frame,xine_stream_t * stream)267 static int eq_draw(vo_frame_t *frame, xine_stream_t *stream)
268 {
269   post_video_port_t *port = (post_video_port_t *)frame->port;
270   post_plugin_eq_t *this = (post_plugin_eq_t *)port->post;
271   vo_frame_t *out_frame;
272   vo_frame_t *yv12_frame;
273   int skip;
274 
275   if( !frame->bad_frame &&
276       ((this->params.brightness != 0) || (this->params.contrast != 0)) ) {
277 
278     /* convert to YV12 if needed */
279     if( frame->format != XINE_IMGFMT_YV12 ) {
280 
281       yv12_frame = port->original_port->get_frame(port->original_port,
282         frame->width, frame->height, frame->ratio, XINE_IMGFMT_YV12, frame->flags | VO_BOTH_FIELDS);
283 
284       _x_post_frame_copy_down(frame, yv12_frame);
285 
286       yuy2_to_yv12(frame->base[0], frame->pitches[0],
287                    yv12_frame->base[0], yv12_frame->pitches[0],
288                    yv12_frame->base[1], yv12_frame->pitches[1],
289                    yv12_frame->base[2], yv12_frame->pitches[2],
290                    frame->width, frame->height);
291 
292     } else {
293       yv12_frame = frame;
294       yv12_frame->lock(yv12_frame);
295     }
296 
297 
298     out_frame = port->original_port->get_frame(port->original_port,
299       frame->width, frame->height, frame->ratio, XINE_IMGFMT_YV12, frame->flags | VO_BOTH_FIELDS);
300 
301     _x_post_frame_copy_down(frame, out_frame);
302 
303     pthread_mutex_lock (&this->lock);
304 
305     process(out_frame->base[0], out_frame->pitches[0],
306             yv12_frame->base[0], yv12_frame->pitches[0],
307             frame->width, frame->height,
308             this->params.brightness, this->params.contrast);
309     xine_fast_memcpy(out_frame->base[1],yv12_frame->base[1],
310                      yv12_frame->pitches[1] * frame->height/2);
311     xine_fast_memcpy(out_frame->base[2],yv12_frame->base[2],
312                      yv12_frame->pitches[2] * frame->height/2);
313 
314     pthread_mutex_unlock (&this->lock);
315 
316     skip = out_frame->draw(out_frame, stream);
317 
318     _x_post_frame_copy_up(frame, out_frame);
319 
320     out_frame->free(out_frame);
321     yv12_frame->free(yv12_frame);
322 
323   } else {
324     _x_post_frame_copy_down(frame, frame->next);
325     skip = frame->next->draw(frame->next, stream);
326     _x_post_frame_copy_up(frame, frame->next);
327   }
328 
329   return skip;
330 }
331 
eq_open_plugin(post_class_t * class_gen,int inputs,xine_audio_port_t ** audio_target,xine_video_port_t ** video_target)332 static post_plugin_t *eq_open_plugin(post_class_t *class_gen, int inputs,
333                                      xine_audio_port_t **audio_target,
334                                      xine_video_port_t **video_target)
335 {
336   post_plugin_eq_t  *this = calloc(1, sizeof(post_plugin_eq_t));
337   post_in_t         *input;
338   post_out_t        *output;
339   post_video_port_t *port;
340 
341   static const xine_post_api_t post_api = {
342     .set_parameters  = set_parameters,
343     .get_parameters  = get_parameters,
344     .get_param_descr = get_param_descr,
345     .get_help        = get_help,
346   };
347   static const xine_post_in_t params_input = {
348     .name = "parameters",
349     .type = XINE_POST_DATA_PARAMETERS,
350     .data  = (void *)&post_api,
351   };
352 
353   if (!this || !video_target || !video_target[0]) {
354     free(this);
355     return NULL;
356   }
357 
358   (void)class_gen;
359   (void)inputs;
360   (void)audio_target;
361 
362   process = process_C;
363 #if defined(ARCH_X86)
364   if( xine_mm_accel() & MM_ACCEL_X86_MMX )
365     process = process_MMX;
366 #endif
367 
368   _x_post_init(&this->post, 0, 1);
369 
370   this->params.brightness = 0;
371   this->params.contrast = 0;
372 
373   pthread_mutex_init (&this->lock, NULL);
374 
375   port = _x_post_intercept_video_port(&this->post, video_target[0], &input, &output);
376   port->new_port.get_property = eq_get_property;
377   port->new_port.set_property = eq_set_property;
378   port->intercept_frame       = eq_intercept_frame;
379   port->new_frame->draw       = eq_draw;
380 
381   xine_list_push_back(this->post.input, (void *)&params_input);
382 
383   input->xine_in.name     = "video";
384   output->xine_out.name   = "eqd video";
385 
386   this->post.xine_post.video_input[0] = &port->new_port;
387 
388   this->post.dispose = eq_dispose;
389 
390   return &this->post;
391 }
392 
eq_init_plugin(xine_t * xine,const void * data)393 void *eq_init_plugin(xine_t *xine, const void *data)
394 {
395   static const post_class_t post_eq_class = {
396     .open_plugin     = eq_open_plugin,
397     .identifier      = "eq",
398     .description     = N_("soft video equalizer"),
399     .dispose         = NULL,
400   };
401 
402   (void)xine;
403   (void)data;
404 
405   return (void *)&post_eq_class;
406 }
407