1 /*
2  * Copyright (C) 2000-2020 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  * video_out_xv.c, X11 video extension interface for xine
21  *
22  * based on mpeg2dec code from
23  * Aaron Holtzman <aholtzma@ess.engr.uvic.ca>
24  *
25  * Xv image support by Gerd Knorr <kraxel@goldbach.in-berlin.de>
26  *
27  * xine-specific code by Guenter Bartsch <bartscgr@studbox.uni-stuttgart.de>
28  *
29  * overlay support by James Courtier-Dutton <James@superbug.demon.co.uk> - July 2001
30  * X11 unscaled overlay support by Miguel Freitas - Nov 2003
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <math.h>
42 #include <pthread.h>
43 
44 #include <sys/types.h>
45 #if defined(__FreeBSD__)
46 #include <machine/param.h>
47 #endif
48 #include <sys/ipc.h>
49 #include <sys/shm.h>
50 #include <sys/time.h>
51 
52 #include <X11/Xlib.h>
53 #include <X11/Xutil.h>
54 #include <X11/extensions/XShm.h>
55 #include <X11/extensions/Xv.h>
56 #include <X11/extensions/Xvlib.h>
57 #include <time.h>
58 
59 #define LOG_MODULE "video_out_xv"
60 #define LOG_VERBOSE
61 /*
62 #define LOG
63 */
64 
65 #define DEBUG_EMU
66 
67 #include "xine.h"
68 #include <xine/video_out.h>
69 #include <xine/xine_internal.h>
70 /* #include "overlay.h" */
71 #include <xine/xineutils.h>
72 #include <xine/vo_scale.h>
73 #include "x11osd.h"
74 #define XV_PROPS
75 #include "xv_common.h"
76 
77 #define LOCK_DISPLAY(this) this->lock_display (this->ld_user_data)
78 #define UNLOCK_DISPLAY(this) this->unlock_display (this->ud_user_data);
79 
80 typedef struct xv_driver_s xv_driver_t;
81 
82 typedef struct {
83   int                initial_value;
84   int                value;
85   int                min;
86   int                max;
87   Atom               atom;
88 
89   int                defer;
90 
91   const char        *name;
92   cfg_entry_t       *entry;
93   xv_driver_t       *this;
94 } xv_property_t;
95 
96 typedef struct {
97   /* public part */
98   vo_frame_t         vo_frame;
99   /* change detection */
100   double             ratio;
101   int                req_width, req_height, format;
102   /* xv image */
103   int                width, height, xvformat;
104   XvImage           *image;
105   XShmSegmentInfo    shminfo;
106   /* yv12 backend for yuy2 emulation */
107   uint8_t           *base[3];
108   int                pitches[3];
109 } xv_frame_t;
110 
111 
112 struct xv_driver_s {
113 
114   vo_driver_t        vo_driver;
115 
116   /* X11 / Xv related stuff */
117   Display           *display;
118   int                screen;
119   Drawable           drawable;
120   unsigned int       xv_format_yv12;
121   unsigned int       xv_format_yuy2;
122   XVisualInfo        vinfo;
123   GC                 gc;
124   XvPortID           xv_port;
125   XColor             black;
126 
127   int                use_shm;
128   int                use_pitch_alignment;
129   xv_property_t      props[XV_NUM_PROPERTIES];
130   uint32_t           capabilities;
131 
132   int                ovl_changed;
133   xv_frame_t        *recent_frames[VO_NUM_RECENT_FRAMES];
134   xv_frame_t        *cur_frame;
135   x11osd            *xoverlay;
136 
137   /* all scaling information goes here */
138   vo_scale_t         sc;
139 
140   int              (*x11_old_error_handler)  (Display *, XErrorEvent *);
141 
142   xine_t            *xine;
143 
144   alphablend_t       alphablend_extra_data;
145 
146   void             (*lock_display) (void *);
147   void              *ld_user_data;
148 
149   void             (*unlock_display) (void *);
150   void              *ud_user_data;
151 
152   int                emu_yuy2;
153 
154   /* color matrix switching */
155   uint8_t            cm_lut[32];
156   int                cm_active, cm_state;
157   int                fullrange_mode;
158 };
159 
160 typedef struct {
161   video_driver_class_t driver_class;
162 
163   xine_t              *xine;
164 } xv_class_t;
165 
166 /* import common color matrix stuff */
167 #define CM_LUT
168 #define CM_DRIVER_T xv_driver_t
169 #include "color_matrix.c"
170 
171 static int gX11Fail;
172 
173 VIDEO_DEVICE_XV_DECL_BICUBIC_TYPES
174 VIDEO_DEVICE_XV_DECL_PREFER_TYPES
175 
xv_get_capabilities(vo_driver_t * this_gen)176 static uint32_t xv_get_capabilities (vo_driver_t *this_gen) {
177   xv_driver_t *this = (xv_driver_t *) this_gen;
178 
179   return this->capabilities;
180 }
181 
xv_frame_field(vo_frame_t * vo_img,int which_field)182 static void xv_frame_field (vo_frame_t *vo_img, int which_field) {
183   /* not needed for Xv */
184   (void)vo_img;
185   (void)which_field;
186 }
187 
188 static void xv_rem_yuy2_emu (xv_frame_t *f);
189 
xv_frame_dispose(vo_frame_t * vo_img)190 static void xv_frame_dispose (vo_frame_t *vo_img) {
191   xv_frame_t  *frame = (xv_frame_t *) vo_img ;
192   xv_driver_t *this  = (xv_driver_t *) vo_img->driver;
193 
194   xv_rem_yuy2_emu (frame);
195 
196   if (frame->image) {
197 
198     if (frame->shminfo.shmaddr) {
199       LOCK_DISPLAY(this);
200       XShmDetach (this->display, &frame->shminfo);
201       XFree (frame->image);
202       UNLOCK_DISPLAY(this);
203 
204       shmdt (frame->shminfo.shmaddr);
205       shmctl (frame->shminfo.shmid, IPC_RMID, NULL);
206     }
207     else {
208       LOCK_DISPLAY(this);
209       xine_free_aligned (frame->image->data);
210       XFree (frame->image);
211       UNLOCK_DISPLAY(this);
212     }
213   }
214 
215   pthread_mutex_destroy (&frame->vo_frame.mutex);
216   free (frame);
217 }
218 
xv_alloc_frame(vo_driver_t * this_gen)219 static vo_frame_t *xv_alloc_frame (vo_driver_t *this_gen) {
220   /* xv_driver_t  *this = (xv_driver_t *) this_gen; */
221   xv_frame_t   *frame ;
222 
223   frame = (xv_frame_t *) calloc(1, sizeof(xv_frame_t));
224   if (!frame)
225     return NULL;
226 
227   frame->req_width  = 0;
228   frame->req_height = 0;
229   frame->format     = 0;
230   frame->width      = 0;
231   frame->height     = 0;
232   frame->xvformat   = 0;
233   frame->image      = NULL;
234   frame->base[0]    = NULL;
235   frame->vo_frame.proc_slice = NULL;
236   frame->vo_frame.proc_frame = NULL;
237 
238   pthread_mutex_init (&frame->vo_frame.mutex, NULL);
239 
240   /*
241    * supply required functions
242    */
243   frame->vo_frame.field      = xv_frame_field;
244   frame->vo_frame.dispose    = xv_frame_dispose;
245   frame->vo_frame.driver     = this_gen;
246 
247   return &frame->vo_frame;
248 }
249 
HandleXError(Display * display,XErrorEvent * xevent)250 static int HandleXError (Display *display, XErrorEvent *xevent) {
251   char str [1024];
252 
253   XGetErrorText (display, xevent->error_code, str, 1024);
254   printf ("received X error event: %s\n", str);
255   gX11Fail = 1;
256 
257   return 0;
258 }
259 
260 /* called xlocked */
x11_InstallXErrorHandler(xv_driver_t * this)261 static void x11_InstallXErrorHandler (xv_driver_t *this) {
262   this->x11_old_error_handler = XSetErrorHandler (HandleXError);
263   XSync(this->display, False);
264 }
265 
266 /* called xlocked */
x11_DeInstallXErrorHandler(xv_driver_t * this)267 static void x11_DeInstallXErrorHandler (xv_driver_t *this) {
268   XSetErrorHandler (this->x11_old_error_handler);
269   XSync(this->display, False);
270   this->x11_old_error_handler = NULL;
271 }
272 
273 /* called xlocked */
create_ximage(xv_driver_t * this,XShmSegmentInfo * shminfo,int width,int height,int format)274 static XvImage *create_ximage (xv_driver_t *this, XShmSegmentInfo *shminfo,
275 			       int width, int height, int format) {
276   unsigned int  xv_format;
277   XvImage      *image = NULL;
278 
279   if (this->use_pitch_alignment) {
280     lprintf ("use_pitch_alignment old width=%d",width);
281     width = (width + 7) & ~0x7;
282     lprintf ("use_pitch_alignment new width=%d",width);
283   }
284 
285   switch (format) {
286   case XINE_IMGFMT_YV12:
287     xv_format = this->xv_format_yv12;
288     break;
289   case XINE_IMGFMT_YUY2:
290     xv_format = this->xv_format_yuy2;
291     break;
292   default:
293     xprintf (this->xine, XINE_VERBOSITY_DEBUG, "create_ximage: unknown format %08x\n",format);
294     return NULL;
295   }
296 
297   if (xv_format == 0) {
298     xprintf (this->xine, XINE_VERBOSITY_DEBUG, "create_ximage: unsupported format %08x\n",format);
299     return NULL;
300   }
301 
302   if (this->use_shm) {
303 
304     /*
305      * try shm
306      */
307 
308     gX11Fail = 0;
309     x11_InstallXErrorHandler (this);
310 
311     lprintf( "XvShmCreateImage format=0x%x, width=%d, height=%d\n", xv_format, width, height );
312     image = XvShmCreateImage(this->display, this->xv_port, xv_format, 0,
313 			     width, height, shminfo);
314 
315     if (image == NULL )  {
316       xprintf(this->xine, XINE_VERBOSITY_LOG,
317 	      _("%s: XvShmCreateImage failed\n"), LOG_MODULE);
318       xprintf(this->xine, XINE_VERBOSITY_LOG,
319 	      _("%s: => not using MIT Shared Memory extension.\n"), LOG_MODULE);
320       this->use_shm = 0;
321       goto finishShmTesting;
322     }
323 
324     {
325     int q;
326 
327     lprintf( "XvImage id %d\n", image->id );
328     lprintf( "XvImage width %d\n", image->width );
329     lprintf( "XvImage height %d\n", image->height );
330     lprintf( "XvImage data_size %d\n", image->data_size );
331     lprintf( "XvImage num_planes %d\n", image->num_planes );
332 
333         for( q=0; q < image->num_planes; q++)
334         {
335             lprintf( "XvImage pitches[%d] %d\n",  q, image->pitches[q] );
336             lprintf( "XvImage offsets[%d] %d\n",  q, image->offsets[q] );
337         }
338     }
339 
340     shminfo->shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777);
341 
342     if (image->data_size==0) {
343       xprintf(this->xine, XINE_VERBOSITY_LOG,
344 	      _("%s: XvShmCreateImage returned a zero size\n"), LOG_MODULE);
345       xprintf(this->xine, XINE_VERBOSITY_LOG,
346 	      _("%s: => not using MIT Shared Memory extension.\n"), LOG_MODULE);
347       this->use_shm = 0;
348       goto finishShmTesting;
349     }
350 
351     if (shminfo->shmid < 0 ) {
352       xprintf(this->xine, XINE_VERBOSITY_LOG,
353 	      _("%s: shared memory error in shmget: %s\n"), LOG_MODULE, strerror(errno));
354       xprintf(this->xine, XINE_VERBOSITY_LOG,
355 	      _("%s: => not using MIT Shared Memory extension.\n"), LOG_MODULE);
356       this->use_shm = 0;
357       goto finishShmTesting;
358     }
359 
360     shminfo->shmaddr  = (char *) shmat(shminfo->shmid, 0, 0);
361 
362     if (shminfo->shmaddr == NULL) {
363       xprintf(this->xine, XINE_VERBOSITY_DEBUG,
364 	      LOG_MODULE ": shared memory error (address error NULL)\n");
365       this->use_shm = 0;
366       goto finishShmTesting;
367     }
368 
369     if (shminfo->shmaddr == ((char *) -1)) {
370       xprintf(this->xine, XINE_VERBOSITY_DEBUG,
371 	      LOG_MODULE ": shared memory error (address error)\n");
372       this->use_shm = 0;
373       goto finishShmTesting;
374     }
375 
376     shminfo->readOnly = False;
377     image->data       = shminfo->shmaddr;
378 
379     XShmAttach(this->display, shminfo);
380 
381     XSync(this->display, False);
382     shmctl(shminfo->shmid, IPC_RMID, 0);
383 
384     if (gX11Fail) {
385       shmdt (shminfo->shmaddr);
386       shmctl (shminfo->shmid, IPC_RMID, 0);
387       shminfo->shmid = -1;
388       xprintf(this->xine, XINE_VERBOSITY_LOG,
389 	      _("%s: x11 error during shared memory XImage creation\n"), LOG_MODULE);
390       xprintf(this->xine, XINE_VERBOSITY_LOG,
391 	      _("%s: => not using MIT Shared Memory extension.\n"), LOG_MODULE);
392       this->use_shm  = 0;
393       goto finishShmTesting;
394     }
395 
396     /*
397      * Now that the Xserver has learned about and attached to the
398      * shared memory segment,  delete it.  It's actually deleted by
399      * the kernel when all users of that segment have detached from
400      * it.  Gives an automatic shared memory cleanup in case we crash.
401      */
402     shmctl (shminfo->shmid, IPC_RMID, 0);
403     shminfo->shmid = -1;
404 
405   finishShmTesting:
406     XSync(this->display, False);
407     x11_DeInstallXErrorHandler(this);
408   }
409 
410 
411   /*
412    * fall back to plain Xv if necessary
413    */
414 
415   if (!this->use_shm) {
416     image = XvCreateImage (this->display, this->xv_port, xv_format, NULL, width, height);
417     if (image) {
418       image->data = xine_malloc_aligned (image->data_size);
419       if (!image->data) {
420         XFree (image);
421         image = NULL;
422       }
423     }
424     shminfo->shmaddr = 0;
425   }
426 
427   return image;
428 }
429 
430 /* called xlocked */
dispose_ximage(xv_driver_t * this,XShmSegmentInfo * shminfo,XvImage * myimage)431 static void dispose_ximage (xv_driver_t *this,
432 			    XShmSegmentInfo *shminfo,
433 			    XvImage *myimage) {
434 
435   if (shminfo->shmaddr) {
436 
437     XShmDetach (this->display, shminfo);
438     XFree (myimage);
439     shmdt (shminfo->shmaddr);
440     if (shminfo->shmid >= 0) {
441       shmctl (shminfo->shmid, IPC_RMID, 0);
442       shminfo->shmid = -1;
443     }
444 
445   }
446   else {
447     xine_free_aligned (myimage->data);
448 
449     XFree (myimage);
450   }
451 }
452 
xv_slice_yuy2_emu(vo_frame_t * vo_img,uint8_t ** src)453 static void xv_slice_yuy2_emu (vo_frame_t *vo_img, uint8_t **src) {
454   xv_frame_t  *frame = (xv_frame_t *)vo_img;
455   int y = (src[0] - frame->vo_frame.base[0]) / frame->vo_frame.pitches[0];
456   int h;
457 
458   if ((y < 0) || (y >= frame->height))
459     return;
460 
461   if (!vo_img->proc_called)
462     vo_img->proc_called = 1;
463 
464   h = frame->height - y;
465   h = h > 16 ? 16 : h;
466   yuy2_to_yv12 (src[0], frame->vo_frame.pitches[0],
467     frame->base[0] + y * frame->pitches[0], frame->pitches[0],
468     frame->base[1] + (y >> 1) * frame->pitches[1], frame->pitches[1],
469     frame->base[2] + (y >> 1) * frame->pitches[2], frame->pitches[2],
470     frame->width, h);
471 }
472 
xv_add_yuy2_emu(xv_frame_t * f)473 static int xv_add_yuy2_emu (xv_frame_t *f) {
474   uint8_t *base;
475   int pitch;
476   if (f->base[0])
477     return 0;
478   pitch = f->image->pitches[0] * 2;
479   base = xine_malloc_aligned (pitch * f->image->height);
480   if (!base)
481     return 1;
482   /* save backend */
483   f->base[0] = f->vo_frame.base[0];
484   f->base[1] = f->vo_frame.base[1];
485   f->base[2] = f->vo_frame.base[2];
486   f->pitches[0] = f->vo_frame.pitches[0];
487   f->pitches[1] = f->vo_frame.pitches[1];
488   f->pitches[2] = f->vo_frame.pitches[2];
489   /* new frontend */
490   f->vo_frame.base[0] = base;
491   f->vo_frame.base[1] =
492   f->vo_frame.base[2] = NULL;
493   f->vo_frame.pitches[0] = pitch;
494   f->vo_frame.pitches[1] =
495   f->vo_frame.pitches[2] = 0;
496   f->format = XINE_IMGFMT_YUY2;
497   f->vo_frame.proc_slice = xv_slice_yuy2_emu;
498   {
499     const union {uint8_t bytes[4]; uint32_t word;} black = {{0, 128, 0, 128}};
500     uint32_t *q = (uint32_t *)base;
501     int i;
502     for (i = pitch * f->image->height / 4; i > 0; i--)
503       *q++ = black.word;
504   }
505   return 0;
506 }
507 
xv_rem_yuy2_emu(xv_frame_t * f)508 static void xv_rem_yuy2_emu (xv_frame_t *f) {
509   if (!f->base[0])
510     return;
511   xine_free_aligned (f->vo_frame.base[0]);
512   f->vo_frame.base[0] = f->base[0];
513   f->vo_frame.base[1] = f->base[1];
514   f->vo_frame.base[2] = f->base[2];
515   f->vo_frame.pitches[0] = f->pitches[0];
516   f->vo_frame.pitches[1] = f->pitches[1];
517   f->vo_frame.pitches[2] = f->pitches[2];
518   f->base[0] = NULL;
519   f->format = XINE_IMGFMT_YV12;
520   f->vo_frame.proc_slice = NULL;
521 }
522 
xv_update_frame_format(vo_driver_t * this_gen,vo_frame_t * frame_gen,uint32_t width,uint32_t height,double ratio,int format,int flags)523 static void xv_update_frame_format (vo_driver_t *this_gen,
524 				    vo_frame_t *frame_gen,
525 				    uint32_t width, uint32_t height,
526 				    double ratio, int format, int flags) {
527   xv_driver_t  *this  = (xv_driver_t *) this_gen;
528   xv_frame_t   *frame = (xv_frame_t *) frame_gen;
529   int resize;
530 
531   (void)flags;
532   if (this->use_pitch_alignment) {
533     width = (width + 7) & ~0x7;
534   }
535   resize = (frame->req_width != (int)width) || (frame->req_height != (int)height);
536 
537   if (resize || (frame->format != format)) {
538     int fmt = format;
539     if ((fmt == XINE_IMGFMT_YUY2) && (this->emu_yuy2 || !this->xv_format_yuy2))
540       fmt = XINE_IMGFMT_YV12;
541 
542     if (resize || (frame->xvformat != fmt)) {
543       /* get emulation out of the way first if any */
544       xv_rem_yuy2_emu (frame);
545       /* (re-) allocate xvimage */
546       LOCK_DISPLAY (this);
547       if (frame->image)
548         dispose_ximage (this, &frame->shminfo, frame->image);
549       frame->image = create_ximage (this, &frame->shminfo, width, height, fmt);
550       UNLOCK_DISPLAY (this);
551       if (!frame->image) {
552         frame->vo_frame.base[0] = NULL;
553         frame->vo_frame.base[1] = NULL;
554         frame->vo_frame.base[2] = NULL;
555         frame->req_width = 0;
556         frame->vo_frame.width = 0;
557         return;
558       }
559       frame->xvformat = fmt;
560       /* get params, blackfill */
561       if (fmt == XINE_IMGFMT_YUY2) {
562         frame->vo_frame.pitches[0] = frame->image->pitches[0];
563         frame->vo_frame.base[0]    = frame->image->data + frame->image->offsets[0];
564         {
565           const union {uint8_t bytes[4]; uint32_t word;} black = {{0, 128, 0, 128}};
566           uint32_t *q = (uint32_t *)frame->vo_frame.base[0];
567           int i;
568           for (i = frame->vo_frame.pitches[0] * frame->image->height / 4; i > 0; i--)
569             *q++ = black.word;
570         }
571       } else { /* XINE_IMGFMT_YV12 */
572         frame->vo_frame.pitches[0] = frame->image->pitches[0];
573         frame->vo_frame.pitches[1] = frame->image->pitches[2];
574         frame->vo_frame.pitches[2] = frame->image->pitches[1];
575         frame->vo_frame.base[0] = frame->image->data + frame->image->offsets[0];
576         frame->vo_frame.base[1] = frame->image->data + frame->image->offsets[2];
577         frame->vo_frame.base[2] = frame->image->data + frame->image->offsets[1];
578         memset (frame->vo_frame.base[0],   0, frame->vo_frame.pitches[0] *  frame->image->height);
579         memset (frame->vo_frame.base[1], 128, frame->vo_frame.pitches[1] * (frame->image->height >> 1));
580         memset (frame->vo_frame.base[2], 128, frame->vo_frame.pitches[2] * (frame->image->height >> 1));
581       }
582     }
583     /* update emulation */
584     frame->format = format;
585     if (fmt != format) {
586       if (xv_add_yuy2_emu (frame)) {
587         LOCK_DISPLAY (this);
588         dispose_ximage (this, &frame->shminfo, frame->image);
589         UNLOCK_DISPLAY (this);
590         frame->image = NULL;
591         frame->vo_frame.base[0] = NULL;
592         frame->vo_frame.base[1] = NULL;
593         frame->vo_frame.base[2] = NULL;
594         frame->req_width = 0;
595         frame->vo_frame.width = 0;
596         return;
597       }
598     } else
599       xv_rem_yuy2_emu (frame);
600     /* allocated frame size may not match requested size */
601     frame->req_width  = width;
602     frame->req_height = height;
603     frame->width  = frame->image->width;
604     frame->height = frame->image->height;
605   }
606 
607   if (frame->vo_frame.width > frame->width)
608     frame->vo_frame.width  = frame->width;
609   if (frame->vo_frame.height > frame->height)
610     frame->vo_frame.height = frame->height;
611 
612   frame->ratio = ratio;
613 }
614 
xv_clean_output_area(xv_driver_t * this)615 static void xv_clean_output_area (xv_driver_t *this) {
616   int i;
617 
618   LOCK_DISPLAY(this);
619 
620   XSetForeground (this->display, this->gc, this->black.pixel);
621 
622   for( i = 0; i < 4; i++ ) {
623     if( this->sc.border[i].w && this->sc.border[i].h ) {
624       XFillRectangle(this->display, this->drawable, this->gc,
625 		     this->sc.border[i].x, this->sc.border[i].y,
626 		     this->sc.border[i].w, this->sc.border[i].h);
627     }
628   }
629 
630   if ((this->props[VO_PROP_COLORKEY].atom != None) ||
631       (this->props[VO_PROP_AUTOPAINT_COLORKEY].value == 1)) {
632     XSetForeground (this->display, this->gc, this->props[VO_PROP_COLORKEY].value);
633     XFillRectangle (this->display, this->drawable, this->gc,
634 		    this->sc.output_xoffset, this->sc.output_yoffset,
635 		    this->sc.output_width, this->sc.output_height);
636   }
637 
638   if (this->xoverlay) {
639     x11osd_resize (this->xoverlay, this->sc.gui_width, this->sc.gui_height);
640     this->ovl_changed = 1;
641   }
642 
643   UNLOCK_DISPLAY(this);
644 }
645 
646 /*
647  * convert delivered height/width to ideal width/height
648  * taking into account aspect ratio and zoom factor
649  */
650 
xv_compute_ideal_size(xv_driver_t * this)651 static void xv_compute_ideal_size (xv_driver_t *this) {
652   _x_vo_scale_compute_ideal_size( &this->sc );
653 }
654 
655 
656 /*
657  * make ideal width/height "fit" into the gui
658  */
659 
xv_compute_output_size(xv_driver_t * this)660 static void xv_compute_output_size (xv_driver_t *this) {
661 
662   _x_vo_scale_compute_output_size( &this->sc );
663 }
664 
xv_overlay_begin(vo_driver_t * this_gen,vo_frame_t * frame_gen,int changed)665 static void xv_overlay_begin (vo_driver_t *this_gen,
666 			      vo_frame_t *frame_gen, int changed) {
667   xv_driver_t  *this = (xv_driver_t *) this_gen;
668 
669   this->ovl_changed += changed;
670 
671   if( this->ovl_changed && this->xoverlay ) {
672     LOCK_DISPLAY(this);
673     x11osd_clear(this->xoverlay);
674     UNLOCK_DISPLAY(this);
675   }
676 
677   this->alphablend_extra_data.offset_x = frame_gen->overlay_offset_x;
678   this->alphablend_extra_data.offset_y = frame_gen->overlay_offset_y;
679 }
680 
xv_overlay_end(vo_driver_t * this_gen,vo_frame_t * vo_img)681 static void xv_overlay_end (vo_driver_t *this_gen, vo_frame_t *vo_img) {
682   xv_driver_t  *this = (xv_driver_t *) this_gen;
683 
684   (void)vo_img;
685   if( this->ovl_changed && this->xoverlay ) {
686     LOCK_DISPLAY(this);
687     x11osd_expose(this->xoverlay);
688     UNLOCK_DISPLAY(this);
689   }
690 
691   this->ovl_changed = 0;
692 }
693 
xv_overlay_blend(vo_driver_t * this_gen,vo_frame_t * frame_gen,vo_overlay_t * overlay)694 static void xv_overlay_blend (vo_driver_t *this_gen,
695 			      vo_frame_t *frame_gen, vo_overlay_t *overlay) {
696   xv_driver_t  *this = (xv_driver_t *) this_gen;
697   xv_frame_t   *frame = (xv_frame_t *) frame_gen;
698 
699   if (overlay->rle) {
700     if( overlay->unscaled ) {
701       if( this->ovl_changed && this->xoverlay ) {
702         LOCK_DISPLAY(this);
703         x11osd_blend(this->xoverlay, overlay);
704         UNLOCK_DISPLAY(this);
705       }
706     } else {
707       if (frame->format == XINE_IMGFMT_YV12)
708         _x_blend_yuv(frame->vo_frame.base, overlay,
709 		  frame->width, frame->height, frame->vo_frame.pitches,
710                   &this->alphablend_extra_data);
711       else
712         _x_blend_yuy2(frame->vo_frame.base[0], overlay,
713 		   frame->width, frame->height, frame->vo_frame.pitches[0],
714                    &this->alphablend_extra_data);
715     }
716   }
717 }
718 
xv_add_recent_frame(xv_driver_t * this,xv_frame_t * frame)719 static void xv_add_recent_frame (xv_driver_t *this, xv_frame_t *frame) {
720   int i;
721 
722   i = VO_NUM_RECENT_FRAMES-1;
723   if( this->recent_frames[i] )
724     this->recent_frames[i]->vo_frame.free
725        (&this->recent_frames[i]->vo_frame);
726 
727   for( ; i ; i-- )
728     this->recent_frames[i] = this->recent_frames[i-1];
729 
730   this->recent_frames[0] = frame;
731 }
732 
xv_flush_recent_frames(xv_driver_t * this)733 static int xv_flush_recent_frames (xv_driver_t *this) {
734   int i, n = 0;
735 
736   for (i = 0; i < VO_NUM_RECENT_FRAMES; i++) {
737     if (this->recent_frames[i]) {
738       this->recent_frames[i]->vo_frame.free (&this->recent_frames[i]->vo_frame);
739       this->recent_frames[i] = NULL;
740       n++;
741     }
742   }
743   return n;
744 }
745 
xv_redraw_needed(vo_driver_t * this_gen)746 static int xv_redraw_needed (vo_driver_t *this_gen) {
747   xv_driver_t  *this = (xv_driver_t *) this_gen;
748   int           ret  = !this->cm_active;
749 
750   if( this->cur_frame ) {
751 
752     this->sc.delivered_height = this->cur_frame->height;
753     this->sc.delivered_width  = this->cur_frame->width;
754     this->sc.delivered_ratio  = this->cur_frame->ratio;
755 
756     this->sc.crop_left        = this->cur_frame->vo_frame.crop_left;
757     this->sc.crop_right       = this->cur_frame->vo_frame.crop_right;
758     this->sc.crop_top         = this->cur_frame->vo_frame.crop_top;
759     this->sc.crop_bottom      = this->cur_frame->vo_frame.crop_bottom;
760 
761     xv_compute_ideal_size(this);
762 
763     if( _x_vo_scale_redraw_needed( &this->sc ) ) {
764 
765       xv_compute_output_size (this);
766 
767       xv_clean_output_area (this);
768 
769       ret = 1;
770     }
771   }
772   else
773     ret = 1;
774 
775   return ret;
776 }
777 
778 /* Used in xv_display_frame to determine how long XvShmPutImage takes
779    - if slower than 60fps, print a message
780 */
timeOfDay()781 static double timeOfDay()
782 {
783     struct timeval t;
784     gettimeofday( &t, NULL );
785     return ((double)t.tv_sec) + (((double)t.tv_usec)/1000000.0);
786 }
787 
xv_new_color(xv_driver_t * this,int cm)788 static void xv_new_color (xv_driver_t *this, int cm) {
789   int brig = this->props[VO_PROP_BRIGHTNESS].value;
790   int cont = this->props[VO_PROP_CONTRAST].value;
791   int satu = this->props[VO_PROP_SATURATION].value;
792   int cm2, fr = 0, a, b;
793   Atom atom;
794 
795   if (cm & 1) {
796     /* fullrange emulation. Do not report this via get_property (). */
797     if (this->fullrange_mode == 1) {
798       /* modification routine 1 for TV set style bcs controls 0% - 200% */
799       satu -= this->props[VO_PROP_SATURATION].min;
800       satu  = (satu * (112 * 255) + (127 * 219 / 2)) / (127 * 219);
801       satu += this->props[VO_PROP_SATURATION].min;
802       if (satu > this->props[VO_PROP_SATURATION].max)
803         satu = this->props[VO_PROP_SATURATION].max;
804       cont -= this->props[VO_PROP_CONTRAST].min;
805       cont  = (cont * 219 + 127) / 255;
806       a     = cont * (this->props[VO_PROP_BRIGHTNESS].max - this->props[VO_PROP_BRIGHTNESS].min);
807       cont += this->props[VO_PROP_CONTRAST].min;
808       b     = 256 * (this->props[VO_PROP_CONTRAST].max - this->props[VO_PROP_CONTRAST].min);
809       brig += (16 * a + b / 2) / b;
810       if (brig > this->props[VO_PROP_BRIGHTNESS].max)
811         brig = this->props[VO_PROP_BRIGHTNESS].max;
812       fr = 1;
813     }
814     /* maybe add more routines for non-standard controls later */
815   }
816   LOCK_DISPLAY (this);
817   atom = this->props[VO_PROP_BRIGHTNESS].atom;
818   if (atom != None)
819     XvSetPortAttribute (this->display, this->xv_port, atom, brig);
820   atom = this->props[VO_PROP_CONTRAST].atom;
821   if (atom != None)
822     XvSetPortAttribute (this->display, this->xv_port, atom, cont);
823   atom = this->props[VO_PROP_SATURATION].atom;
824   if (atom != None)
825     XvSetPortAttribute (this->display, this->xv_port, atom, satu);
826   UNLOCK_DISPLAY(this);
827 
828   if (this->props[XV_PROP_ITURBT_709].atom != None) {
829     /* 0 = 601 (SD), 1 = 709 (HD) */
830     /* so far only binary nvidia drivers support this. why not nuveau? */
831     cm2 = (0xc00c >> cm) & 1;
832     LOCK_DISPLAY(this);
833     XvSetPortAttribute (this->display, this->xv_port, this->props[XV_PROP_ITURBT_709].atom, cm2);
834     UNLOCK_DISPLAY(this);
835     this->props[XV_PROP_ITURBT_709].value = cm2;
836     cm2 = cm2 ? 2 : 10;
837   } else if (this->props[XV_PROP_COLORSPACE].atom != None) {
838     /* radeonhd: 0 = size based auto, 1 = 601 (SD), 2 = 709 (HD) */
839     cm2 = ((0xc00c >> cm) & 1) + 1;
840     LOCK_DISPLAY(this);
841     XvSetPortAttribute (this->display, this->xv_port, this->props[XV_PROP_COLORSPACE].atom, cm2);
842     UNLOCK_DISPLAY(this);
843     this->props[XV_PROP_COLORSPACE].value = cm2;
844     cm2 = cm2 == 2 ? 2 : 10;
845   } else {
846     cm2 = 10;
847   }
848 
849   cm2 |= fr;
850   xprintf (this->xine, XINE_VERBOSITY_LOG, "video_out_xv: %s b %d  c %d  s %d  [%s]\n",
851     fr ? "modified " : "", brig, cont, satu, cm_names[cm2]);
852 
853   this->cm_active = cm;
854 }
855 
xv_display_frame(vo_driver_t * this_gen,vo_frame_t * frame_gen)856 static void xv_display_frame (vo_driver_t *this_gen, vo_frame_t *frame_gen) {
857   xv_driver_t  *this  = (xv_driver_t *) this_gen;
858   xv_frame_t   *frame = (xv_frame_t *) frame_gen;
859   int cm;
860   /*
861   printf (LOG_MODULE ": xv_display_frame...\n");
862   */
863 
864   cm = cm_from_frame (frame_gen);
865   if (cm != this->cm_active)
866     xv_new_color (this, cm);
867 
868   /*
869    * queue frames (deinterlacing)
870    * free old frames
871    */
872 
873   xv_add_recent_frame (this, frame); /* deinterlacing */
874 
875   this->cur_frame = frame;
876 
877   /*
878    * let's see if this frame is different in size / aspect
879    * ratio from the previous one
880    */
881   if ( (frame->width != this->sc.delivered_width)
882        || (frame->height != this->sc.delivered_height)
883        || (frame->ratio != this->sc.delivered_ratio)
884        || (frame->vo_frame.crop_left != this->sc.crop_left)
885        || (frame->vo_frame.crop_right != this->sc.crop_right)
886        || (frame->vo_frame.crop_top != this->sc.crop_top)
887        || (frame->vo_frame.crop_bottom != this->sc.crop_bottom) ) {
888     lprintf("frame format changed\n");
889     this->sc.force_redraw = 1;    /* trigger re-calc of output size */
890   }
891 
892   /*
893    * tell gui that we are about to display a frame,
894    * ask for offset and output size
895    */
896   xv_redraw_needed (this_gen);
897   {
898   double start_time;
899   double end_time;
900   double elapse_time;
901   int factor;
902 
903   LOCK_DISPLAY(this);
904   start_time = timeOfDay();
905   if (this->use_shm) {
906     XvShmPutImage(this->display, this->xv_port,
907                   this->drawable, this->gc, this->cur_frame->image,
908                   this->sc.displayed_xoffset, this->sc.displayed_yoffset,
909                   this->sc.displayed_width, this->sc.displayed_height,
910                   this->sc.output_xoffset, this->sc.output_yoffset,
911                   this->sc.output_width, this->sc.output_height, True);
912 
913   } else {
914     XvPutImage(this->display, this->xv_port,
915                this->drawable, this->gc, this->cur_frame->image,
916                this->sc.displayed_xoffset, this->sc.displayed_yoffset,
917                this->sc.displayed_width, this->sc.displayed_height,
918                this->sc.output_xoffset, this->sc.output_yoffset,
919                this->sc.output_width, this->sc.output_height);
920   }
921 
922   XSync(this->display, False);
923   end_time = timeOfDay();
924 
925   UNLOCK_DISPLAY(this);
926 
927   elapse_time = end_time - start_time;
928   factor = (int)(elapse_time/(1.0/60.0));
929 
930   if( factor > 1 )
931   {
932     lprintf( "%s PutImage %dX interval (%fs)\n",
933              LOG_MODULE, factor, elapse_time );
934   }
935   }
936 
937   /*
938   printf (LOG_MODULE ": xv_display_frame... done\n");
939   */
940 }
941 
xv_get_property(vo_driver_t * this_gen,int property)942 static int xv_get_property (vo_driver_t *this_gen, int property) {
943   xv_driver_t *this = (xv_driver_t *) this_gen;
944 
945   if ((property < 0) || (property >= VO_NUM_PROPERTIES)) return (0);
946 
947   switch (property) {
948     case VO_PROP_WINDOW_WIDTH:
949       this->props[property].value = this->sc.gui_width;
950       break;
951     case VO_PROP_WINDOW_HEIGHT:
952       this->props[property].value = this->sc.gui_height;
953       break;
954     case VO_PROP_OUTPUT_WIDTH:
955       this->props[property].value = this->sc.output_width;
956       break;
957     case VO_PROP_OUTPUT_HEIGHT:
958       this->props[property].value = this->sc.output_height;
959       break;
960     case VO_PROP_OUTPUT_XOFFSET:
961       this->props[property].value = this->sc.output_xoffset;
962       break;
963     case VO_PROP_OUTPUT_YOFFSET:
964       this->props[property].value = this->sc.output_yoffset;
965       break;
966   }
967 
968   lprintf(LOG_MODULE ": property #%d = %d\n", property, this->props[property].value);
969 
970   return this->props[property].value;
971 }
972 
xv_set_property(vo_driver_t * this_gen,int property,int value)973 static int xv_set_property (vo_driver_t *this_gen,
974 			    int property, int value) {
975   xv_driver_t *this = (xv_driver_t *) this_gen;
976 
977   printf("xv_set_property: property=%d, value=%d\n", property, value );
978 
979   if ((property < 0) || (property >= VO_NUM_PROPERTIES)) return 0;
980 
981   if (this->props[property].defer == 1) {
982     /* value is out of bound */
983     if((value < this->props[property].min) || (value > this->props[property].max))
984       value = (this->props[property].min + this->props[property].max) >> 1;
985     this->props[property].value = value;
986     this->cm_active = 0;
987     return value;
988   }
989 
990   if (this->props[property].atom != None) {
991 
992     /* value is out of bound */
993     if((value < this->props[property].min) || (value > this->props[property].max))
994       value = (this->props[property].min + this->props[property].max) >> 1;
995 
996     LOCK_DISPLAY(this);
997     XvSetPortAttribute (this->display, this->xv_port,
998 			this->props[property].atom, value);
999     XvGetPortAttribute (this->display, this->xv_port,
1000 			this->props[property].atom,
1001 			&this->props[property].value);
1002     UNLOCK_DISPLAY(this);
1003 
1004     if (this->props[property].entry)
1005       this->props[property].entry->num_value = this->props[property].value;
1006 
1007     return this->props[property].value;
1008   }
1009   else {
1010     switch (property) {
1011 
1012     case VO_PROP_DISCARD_FRAMES:
1013       if (value == -1)
1014         value = xv_flush_recent_frames (this);
1015       break;
1016 
1017     case VO_PROP_ASPECT_RATIO:
1018       if (value>=XINE_VO_ASPECT_NUM_RATIOS)
1019 	value = XINE_VO_ASPECT_AUTO;
1020 
1021       this->props[property].value = value;
1022       xprintf(this->xine, XINE_VERBOSITY_LOG,
1023 	      LOG_MODULE ": VO_PROP_ASPECT_RATIO(%d)\n", this->props[property].value);
1024       this->sc.user_ratio = value;
1025 
1026       xv_compute_ideal_size (this);
1027 
1028       this->sc.force_redraw = 1;    /* trigger re-calc of output size */
1029       break;
1030 
1031     case VO_PROP_ZOOM_X:
1032       if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) {
1033         this->props[property].value = value;
1034 	xprintf(this->xine, XINE_VERBOSITY_LOG,
1035 		LOG_MODULE ": VO_PROP_ZOOM_X = %d\n", this->props[property].value);
1036 
1037 	this->sc.zoom_factor_x = (double)value / (double)XINE_VO_ZOOM_STEP;
1038 
1039 	xv_compute_ideal_size (this);
1040 
1041 	this->sc.force_redraw = 1;    /* trigger re-calc of output size */
1042       }
1043       break;
1044 
1045     case VO_PROP_ZOOM_Y:
1046       if ((value >= XINE_VO_ZOOM_MIN) && (value <= XINE_VO_ZOOM_MAX)) {
1047         this->props[property].value = value;
1048 	xprintf(this->xine, XINE_VERBOSITY_LOG,
1049 		LOG_MODULE ": VO_PROP_ZOOM_Y = %d\n", this->props[property].value);
1050 
1051 	this->sc.zoom_factor_y = (double)value / (double)XINE_VO_ZOOM_STEP;
1052 
1053 	xv_compute_ideal_size (this);
1054 
1055 	this->sc.force_redraw = 1;    /* trigger re-calc of output size */
1056       }
1057       break;
1058     }
1059   }
1060 
1061   return value;
1062 }
1063 
xv_get_property_min_max(vo_driver_t * this_gen,int property,int * min,int * max)1064 static void xv_get_property_min_max (vo_driver_t *this_gen,
1065 				     int property, int *min, int *max) {
1066   xv_driver_t *this = (xv_driver_t *) this_gen;
1067 
1068   if ((property < 0) || (property >= VO_NUM_PROPERTIES)) {
1069     *min = *max = 0;
1070     return;
1071   }
1072 
1073   *min = this->props[property].min;
1074   *max = this->props[property].max;
1075 }
1076 
xv_gui_data_exchange(vo_driver_t * this_gen,int data_type,void * data)1077 static int xv_gui_data_exchange (vo_driver_t *this_gen,
1078 				 int data_type, void *data) {
1079   xv_driver_t     *this = (xv_driver_t *) this_gen;
1080 
1081   switch (data_type) {
1082 #ifndef XINE_DISABLE_DEPRECATED_FEATURES
1083   case XINE_GUI_SEND_COMPLETION_EVENT:
1084     break;
1085 #endif
1086 
1087   case XINE_GUI_SEND_EXPOSE_EVENT: {
1088     /* XExposeEvent * xev = (XExposeEvent *) data; */
1089 
1090     if (this->cur_frame) {
1091       int i;
1092 
1093       LOCK_DISPLAY(this);
1094 
1095       if (this->use_shm) {
1096 	XvShmPutImage(this->display, this->xv_port,
1097 		      this->drawable, this->gc, this->cur_frame->image,
1098 		      this->sc.displayed_xoffset, this->sc.displayed_yoffset,
1099 		      this->sc.displayed_width, this->sc.displayed_height,
1100 		      this->sc.output_xoffset, this->sc.output_yoffset,
1101 		      this->sc.output_width, this->sc.output_height, True);
1102       } else {
1103 	XvPutImage(this->display, this->xv_port,
1104 		   this->drawable, this->gc, this->cur_frame->image,
1105 		   this->sc.displayed_xoffset, this->sc.displayed_yoffset,
1106 		   this->sc.displayed_width, this->sc.displayed_height,
1107 		   this->sc.output_xoffset, this->sc.output_yoffset,
1108 		   this->sc.output_width, this->sc.output_height);
1109       }
1110 
1111       XSetForeground (this->display, this->gc, this->black.pixel);
1112 
1113       for( i = 0; i < 4; i++ ) {
1114 	if( this->sc.border[i].w && this->sc.border[i].h ) {
1115 	  XFillRectangle(this->display, this->drawable, this->gc,
1116 			 this->sc.border[i].x, this->sc.border[i].y,
1117 			 this->sc.border[i].w, this->sc.border[i].h);
1118 	}
1119       }
1120 
1121       if(this->xoverlay)
1122 	x11osd_expose(this->xoverlay);
1123 
1124       XSync(this->display, False);
1125       UNLOCK_DISPLAY(this);
1126     }
1127   }
1128   break;
1129 
1130   case XINE_GUI_SEND_DRAWABLE_CHANGED:
1131     LOCK_DISPLAY(this);
1132     this->drawable = (Drawable) data;
1133     XFreeGC(this->display, this->gc);
1134     this->gc = XCreateGC (this->display, this->drawable, 0, NULL);
1135     if(this->xoverlay)
1136       x11osd_drawable_changed(this->xoverlay, this->drawable);
1137     this->ovl_changed = 1;
1138     UNLOCK_DISPLAY(this);
1139     this->sc.force_redraw = 1;
1140     break;
1141 
1142   case XINE_GUI_SEND_TRANSLATE_GUI_TO_VIDEO:
1143     {
1144       int x1, y1, x2, y2;
1145       x11_rectangle_t *rect = data;
1146 
1147       _x_vo_scale_translate_gui2video(&this->sc, rect->x, rect->y,
1148 				   &x1, &y1);
1149       _x_vo_scale_translate_gui2video(&this->sc, rect->x + rect->w, rect->y + rect->h,
1150 				   &x2, &y2);
1151       rect->x = x1;
1152       rect->y = y1;
1153       rect->w = x2-x1;
1154       rect->h = y2-y1;
1155     }
1156     break;
1157 
1158   default:
1159     return -1;
1160   }
1161 
1162   return 0;
1163 }
1164 
xv_restore_port_attributes(xv_driver_t * this)1165 static void xv_restore_port_attributes(xv_driver_t *this) {
1166   int i;
1167 
1168   for (i = 0; i < XV_NUM_PROPERTIES; i++) {
1169     xv_property_t *prop = &this->props[i];
1170     if (prop->atom == None)
1171       continue;
1172     if (prop->defer || (prop->value != prop->initial_value)) {
1173       LOCK_DISPLAY (this);
1174       XvSetPortAttribute (this->display, this->xv_port, prop->atom, prop->initial_value);
1175       UNLOCK_DISPLAY(this);
1176     }
1177   }
1178 
1179   LOCK_DISPLAY(this);
1180   XSync(this->display, False);
1181   UNLOCK_DISPLAY(this);
1182 }
1183 
xv_dispose(vo_driver_t * this_gen)1184 static void xv_dispose (vo_driver_t *this_gen) {
1185   xv_driver_t *this = (xv_driver_t *) this_gen;
1186   int          i;
1187 
1188   /* restore port attributes to their initial values */
1189   xv_restore_port_attributes (this);
1190 
1191   LOCK_DISPLAY(this);
1192   if (this->xv_port) {
1193     if (XvUngrabPort (this->display, this->xv_port, CurrentTime) != Success) {
1194       xprintf (this->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": xv_exit: XvUngrabPort() failed.\n");
1195     }
1196   }
1197   if (this->gc) {
1198     XFreeGC(this->display, this->gc);
1199   }
1200   UNLOCK_DISPLAY(this);
1201 
1202   /* should do nothing as video_out has sent VO_PROP_DISCARD_FRAMES = -1 before */
1203   for( i=0; i < VO_NUM_RECENT_FRAMES; i++ ) {
1204     if( this->recent_frames[i] )
1205       this->recent_frames[i]->vo_frame.dispose
1206          (&this->recent_frames[i]->vo_frame);
1207     this->recent_frames[i] = NULL;
1208   }
1209 
1210   if( this->xoverlay ) {
1211     LOCK_DISPLAY(this);
1212     x11osd_destroy (this->xoverlay);
1213     UNLOCK_DISPLAY(this);
1214   }
1215 
1216   _x_alphablend_free(&this->alphablend_extra_data);
1217   _x_vo_scale_cleanup (&this->sc, this->xine->config);
1218 
1219   cm_close (this);
1220 
1221   /* cm_close already does this.
1222   this->xine->config->unregister_callbacks (this->xine->config, NULL, NULL, this, sizeof (*this));
1223   */
1224   free (this);
1225 }
1226 
1227 /* called xlocked */
xv_prop_init(xv_driver_t * this,const xv_prop_list_t * l,const XvAttribute attr)1228 static void xv_prop_init (xv_driver_t *this, const xv_prop_list_t *l, const XvAttribute attr) {
1229   xv_property_t *prop = &this->props[l->index];
1230 
1231   Atom atom = XInternAtom (this->display, l->name, False);
1232   if (atom == None)
1233     return;
1234   prop->atom = atom;
1235 
1236   prop->name = l->name;
1237   this->capabilities |= l->caps;
1238 
1239   /*
1240    * some Xv drivers (Gatos ATI) report some ~0 as max values, this is confusing.
1241    */
1242   prop->min = attr.min_value;
1243   prop->max = attr.max_value;
1244   if ((prop->min >= 0) && (prop->max < 0))
1245     prop->max = 2147483615;
1246 
1247   XvGetPortAttribute (this->display, this->xv_port, atom, &prop->initial_value);
1248   prop->value = prop->initial_value;
1249 
1250   xprintf (this->xine, XINE_VERBOSITY_DEBUG,
1251     LOG_MODULE ": port attribute %s (%d) value is %d\n", l->name, l->index, prop->value);
1252 }
1253 
xv_prop_update_int(xv_property_t * prop,int value)1254 static void xv_prop_update_int (xv_property_t *prop, int value) {
1255   xv_driver_t   *this = prop->this;
1256 
1257   LOCK_DISPLAY (this);
1258   XvSetPortAttribute (this->display, this->xv_port, prop->atom, value);
1259   UNLOCK_DISPLAY (this);
1260   prop->value = value;
1261 
1262   xprintf (this->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": %s = %d\n", prop->name, value);
1263 }
1264 
xv_prop_update(void * prop_gen,xine_cfg_entry_t * entry)1265 static void xv_prop_update (void *prop_gen, xine_cfg_entry_t *entry) {
1266   xv_prop_update_int ((xv_property_t *)prop_gen, entry->num_value);
1267 }
1268 
xv_prop_conf(xv_driver_t * this,int property,const char * config_name,const char * config_desc,const char * config_help)1269 static void xv_prop_conf (xv_driver_t *this, int property,
1270   const char *config_name, const char *config_desc, const char *config_help) {
1271   config_values_t *config = this->xine->config;
1272   xv_property_t *prop = &this->props[property];
1273   cfg_entry_t *entry;
1274 
1275   /* is this a boolean property ? */
1276   if ((prop->min == 0) && (prop->max == 1))
1277     config->register_bool (config, config_name, prop->value,
1278       config_desc, config_help, 20, xv_prop_update, prop);
1279   else
1280     config->register_range (config, config_name, prop->value,
1281       prop->min, prop->max, config_desc, config_help, 20, xv_prop_update, prop);
1282 
1283   entry = config->lookup_entry (config, config_name);
1284 
1285   if ((entry->num_value < prop->min) || (entry->num_value > prop->max))
1286     xv_prop_update_int (prop, (prop->min + prop->max) >> 1);
1287 
1288   prop->entry = entry;
1289 
1290   xv_set_property (&this->vo_driver, property, entry->num_value);
1291 }
1292 
xv_update_xv_pitch_alignment(void * this_gen,xine_cfg_entry_t * entry)1293 static void xv_update_xv_pitch_alignment(void *this_gen, xine_cfg_entry_t *entry) {
1294   xv_driver_t *this = (xv_driver_t *) this_gen;
1295 
1296   this->use_pitch_alignment = entry->num_value;
1297 }
1298 
xv_fullrange_cb_config(void * this_gen,xine_cfg_entry_t * entry)1299 static void xv_fullrange_cb_config (void *this_gen, xine_cfg_entry_t *entry) {
1300   xv_driver_t *this = (xv_driver_t *)this_gen;
1301   this->fullrange_mode = entry->num_value;
1302   if (this->fullrange_mode)
1303     this->capabilities |= VO_CAP_FULLRANGE;
1304   else
1305     this->capabilities &= ~VO_CAP_FULLRANGE;
1306   this->cm_active = 0;
1307 }
1308 
1309 #ifdef DEBUG_EMU
xv_debug_emu_cb_config(void * this_gen,xine_cfg_entry_t * entry)1310 static void xv_debug_emu_cb_config (void *this_gen, xine_cfg_entry_t *entry) {
1311   xv_driver_t *this = (xv_driver_t *)this_gen;
1312   this->emu_yuy2 = entry->num_value;
1313 }
1314 #endif
1315 
xv_open_port(xv_driver_t * this,XvPortID port)1316 static int xv_open_port (xv_driver_t *this, XvPortID port) {
1317   {
1318     int i, formats;
1319     XvImageFormatValues *fo;
1320 
1321     LOCK_DISPLAY (this);
1322     fo = XvListImageFormats (this->display, port, (int*)&formats);
1323     UNLOCK_DISPLAY (this);
1324     if (!fo)
1325       return 0;
1326 
1327     this->xv_format_yv12 = 0;
1328     this->xv_format_yuy2 = 0;
1329 
1330     for (i = 0; i < formats; i++) {
1331       lprintf ("Xv image format: 0x%x (%4.4s) %s\n", fo[i].id, (char*)&fo[i].id,
1332         (fo[i].format == XvPacked) ? "packed" : "planar");
1333       switch (fo[i].id) {
1334         case XINE_IMGFMT_YV12:
1335           this->xv_format_yv12 = fo[i].id;
1336           this->capabilities |= VO_CAP_YV12;
1337           xprintf (this->xine, XINE_VERBOSITY_LOG,
1338             _("%s: this adaptor supports the %s format.\n"), LOG_MODULE, "YV12");
1339         break;
1340         case XINE_IMGFMT_YUY2:
1341           this->xv_format_yuy2 = fo[i].id;
1342           this->capabilities |= VO_CAP_YUY2;
1343           xprintf (this->xine, XINE_VERBOSITY_LOG,
1344             _("%s: this adaptor supports the %s format.\n"), LOG_MODULE, "YUY2");
1345         break;
1346         default: ;
1347       }
1348     }
1349 
1350     LOCK_DISPLAY (this);
1351     XFree (fo);
1352     UNLOCK_DISPLAY (this);
1353   }
1354 
1355   /* No yv12: bail out. */
1356   if (!this->xv_format_yv12) {
1357     xprintf (this->xine, XINE_VERBOSITY_LOG,
1358       LOG_MODULE ": this adaptor does not support YV12 format.\n");
1359     return 0;
1360   }
1361   /* No yuy2: just report. Who can deliver yv12 with little effort shall do that.
1362    * Otherwise, accept anyway and emulate. */
1363 
1364   {
1365     int ret;
1366     x11_InstallXErrorHandler (this);
1367     ret = XvGrabPort(this->display, port, 0) == Success;
1368     x11_DeInstallXErrorHandler (this);
1369     return ret;
1370   }
1371 }
1372 
1373 static unsigned int
xv_find_adaptor_by_port(int port,unsigned int adaptors,XvAdaptorInfo * adaptor_info)1374 xv_find_adaptor_by_port (int port, unsigned int adaptors,
1375 			 XvAdaptorInfo *adaptor_info)
1376 {
1377   unsigned int an;
1378   for (an = 0; an < adaptors; an++)
1379     if (adaptor_info[an].type & XvImageMask)
1380       if (port >= (int)adaptor_info[an].base_id &&
1381 	  port < (int)(adaptor_info[an].base_id + adaptor_info[an].num_ports))
1382 	return an;
1383   return 0; /* shouldn't happen */
1384 }
1385 
xv_autodetect_port(xv_driver_t * this,unsigned int adaptors,XvAdaptorInfo * adaptor_info,unsigned int * adaptor_num,XvPortID base,xv_prefertype prefer_type)1386 static XvPortID xv_autodetect_port(xv_driver_t *this,
1387 				   unsigned int adaptors,
1388 				   XvAdaptorInfo *adaptor_info,
1389 				   unsigned int *adaptor_num,
1390 				   XvPortID base,
1391 				   xv_prefertype prefer_type)
1392 {
1393   unsigned int an, j;
1394 
1395   for (an = 0; an < adaptors; an++)
1396     if (adaptor_info[an].type & XvImageMask &&
1397         (prefer_type == xv_prefer_none ||
1398          strcasestr (adaptor_info[an].name, prefer_substrings[prefer_type])))
1399       for (j = 0; j < adaptor_info[an].num_ports; j++) {
1400 	XvPortID port = adaptor_info[an].base_id + j;
1401 	if (port >= base && xv_open_port(this, port)) {
1402 	  *adaptor_num = an;
1403 	  return port;
1404 	}
1405       }
1406 
1407   return 0;
1408 }
1409 
xv_default_lock_display(void * user_data)1410 static void xv_default_lock_display (void *user_data) {
1411   xv_driver_t *this = user_data;
1412   XLockDisplay (this->display);
1413 }
1414 
xv_default_unlock_display(void * user_data)1415 static void xv_default_unlock_display (void *user_data) {
1416   xv_driver_t *this = user_data;
1417   XUnlockDisplay (this->display);
1418 }
1419 
1420 /* expects XINE_VISUAL_TYPE_X11_2 with configurable locking */
open_plugin_2(video_driver_class_t * class_gen,const void * visual_gen)1421 static vo_driver_t *open_plugin_2 (video_driver_class_t *class_gen, const void *visual_gen) {
1422   xv_class_t           *class = (xv_class_t *) class_gen;
1423   config_values_t      *config = class->xine->config;
1424   xv_driver_t          *this;
1425   int                   i;
1426   const x11_visual_t   *visual = (const x11_visual_t *) visual_gen;
1427   XvPortID              xv_port;
1428   char                 *adaptor_name;
1429 
1430   this = (xv_driver_t *) calloc(1, sizeof(xv_driver_t));
1431   if (!this)
1432     return NULL;
1433 
1434 #ifndef HAVE_ZERO_SAFE_MEM
1435   /* 0/NULL inits, for optimizing away. */
1436   this->x11_old_error_handler = NULL;
1437   this->xoverlay              = NULL;
1438   this->ovl_changed           = 0;
1439   this->xv_format_yv12        = 0;
1440   this->xv_format_yuy2        = 0;
1441   this->emu_yuy2              = 0;
1442   this->fullrange_mode        = 0;
1443   for (i = 0; i < XV_NUM_PROPERTIES; i++) {
1444     this->props[i].initial_value = 0;
1445     this->props[i].value = 0;
1446     this->props[i].min   = 0;
1447     this->props[i].max   = 0;
1448     this->props[i].defer = 0;
1449     this->props[i].name  = NULL;
1450     this->props[i].entry = NULL;
1451   }
1452 #endif
1453 
1454   this->use_shm               = 1;
1455   this->xine                  = class->xine;
1456   this->display               = visual->display;
1457   this->screen                = visual->screen;
1458   this->capabilities          = VO_CAP_CROP | VO_CAP_ZOOM_X | VO_CAP_ZOOM_Y;
1459 
1460   /* configurable X11 locking */
1461   if (visual->lock_display) {
1462     this->lock_display = visual->lock_display;
1463     this->ld_user_data = visual->user_data;
1464   } else {
1465     this->lock_display = xv_default_lock_display;
1466     this->ld_user_data = this;
1467   }
1468   if (visual->unlock_display) {
1469     this->unlock_display = visual->unlock_display;
1470     this->ud_user_data   = visual->user_data;
1471   } else {
1472     this->unlock_display = xv_default_unlock_display;
1473     this->ud_user_data   = this;
1474   }
1475 
1476   LOCK_DISPLAY(this);
1477 
1478   /*
1479    * check for Xvideo support
1480    */
1481   {
1482     unsigned int ver, rel, req, ev, err;
1483     if (Success != XvQueryExtension (this->display, &ver, &rel, &req, &ev, &err)) {
1484       UNLOCK_DISPLAY (this);
1485       xprintf (class->xine, XINE_VERBOSITY_LOG, _("%s: Xv extension not present.\n"), LOG_MODULE);
1486       free (this);
1487       return NULL;
1488     }
1489   }
1490 
1491   /*
1492    * check adaptors, search for one that supports (at least) yuv12
1493    */
1494   {
1495     unsigned int   adaptor_num;
1496     xv_prefertype  prefer_type;
1497     unsigned int   adaptors;
1498     XvAdaptorInfo *adaptor_info;
1499 
1500     if (Success != XvQueryAdaptors (this->display, DefaultRootWindow (this->display), &adaptors, &adaptor_info)) {
1501       xprintf (class->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": XvQueryAdaptors failed.\n");
1502       UNLOCK_DISPLAY (this);
1503       free (this);
1504       return NULL;
1505     }
1506 
1507     xv_port = config->register_num (config, "video.device.xv_port", 0,
1508       VIDEO_DEVICE_XV_PORT_HELP, 20, NULL, NULL);
1509     prefer_type = config->register_enum (config, "video.device.xv_preferred_method", 0,
1510       (char **)prefer_labels, VIDEO_DEVICE_XV_PREFER_TYPE_HELP, 10, NULL, NULL);
1511 
1512     if (xv_port != 0) {
1513       if (!xv_open_port (this, xv_port)) {
1514         xprintf (class->xine, XINE_VERBOSITY_NONE,
1515           _("%s: could not open Xv port %lu - autodetecting\n"), LOG_MODULE, (unsigned long)xv_port);
1516         xv_port = xv_autodetect_port (this, adaptors, adaptor_info, &adaptor_num, xv_port, prefer_type);
1517       } else
1518         adaptor_num = xv_find_adaptor_by_port (xv_port, adaptors, adaptor_info);
1519     }
1520 
1521     if (!xv_port)
1522       xv_port = xv_autodetect_port (this, adaptors, adaptor_info, &adaptor_num, 0, prefer_type);
1523 
1524     if (!xv_port) {
1525       if (prefer_type)
1526         xprintf (class->xine, XINE_VERBOSITY_NONE,
1527           _("%s: no available ports of type \"%s\", defaulting...\n"), LOG_MODULE, prefer_labels[prefer_type]);
1528       xv_port = xv_autodetect_port (this, adaptors, adaptor_info, &adaptor_num, 0, xv_prefer_none);
1529     }
1530 
1531     if (!xv_port) {
1532       xprintf (class->xine, XINE_VERBOSITY_LOG,
1533         _("%s: Xv extension is present but I couldn't find a usable yuv12 port.\n"
1534           "\tLooks like your graphics hardware driver doesn't support Xv?!\n"), LOG_MODULE);
1535       /* XvFreeAdaptorInfo (adaptor_info); this crashed on me (gb)*/
1536       XvFreeAdaptorInfo (adaptor_info);
1537       UNLOCK_DISPLAY (this);
1538       free (this);
1539       return NULL;
1540     }
1541     adaptor_name = strdup (adaptor_info[adaptor_num].name ? adaptor_info[adaptor_num].name : "");
1542     XvFreeAdaptorInfo (adaptor_info);
1543   }
1544 
1545   xprintf (class->xine, XINE_VERBOSITY_LOG,
1546     _("%s: using Xv port %ld from adaptor %s for hardware "
1547       "colour space conversion and scaling.\n"), LOG_MODULE, xv_port, adaptor_name);
1548   UNLOCK_DISPLAY (this);
1549 
1550   _x_alphablend_init(&this->alphablend_extra_data, class->xine);
1551 
1552   this->xv_port           = xv_port;
1553 
1554   _x_vo_scale_init (&this->sc, 1, 0, config );
1555   this->sc.frame_output_cb   = visual->frame_output_cb;
1556   this->sc.user_data         = visual->user_data;
1557 
1558   this->drawable                = visual->d;
1559   LOCK_DISPLAY(this);
1560   this->gc                      = XCreateGC (this->display, this->drawable, 0, NULL);
1561   UNLOCK_DISPLAY(this);
1562 
1563   LOCK_DISPLAY(this);
1564   {
1565     XColor dummy;
1566     XAllocNamedColor (this->display, DefaultColormap (this->display, this->screen),
1567       "black", &this->black, &dummy);
1568   }
1569   UNLOCK_DISPLAY(this);
1570 
1571   this->vo_driver.get_capabilities     = xv_get_capabilities;
1572   this->vo_driver.alloc_frame          = xv_alloc_frame;
1573   this->vo_driver.update_frame_format  = xv_update_frame_format;
1574   this->vo_driver.overlay_begin        = xv_overlay_begin;
1575   this->vo_driver.overlay_blend        = xv_overlay_blend;
1576   this->vo_driver.overlay_end          = xv_overlay_end;
1577   this->vo_driver.display_frame        = xv_display_frame;
1578   this->vo_driver.get_property         = xv_get_property;
1579   this->vo_driver.set_property         = xv_set_property;
1580   this->vo_driver.get_property_min_max = xv_get_property_min_max;
1581   this->vo_driver.gui_data_exchange    = xv_gui_data_exchange;
1582   this->vo_driver.dispose              = xv_dispose;
1583   this->vo_driver.redraw_needed        = xv_redraw_needed;
1584 
1585   /*
1586    * init properties
1587    */
1588 
1589   for (i = 0; i < XV_NUM_PROPERTIES; i++) {
1590     this->props[i].atom  = None;
1591     this->props[i].this  = this;
1592   }
1593 
1594   this->sc.user_ratio                        =
1595     this->props[VO_PROP_ASPECT_RATIO].value  = XINE_VO_ASPECT_AUTO;
1596   this->props[VO_PROP_ZOOM_X].value          = 100;
1597   this->props[VO_PROP_ZOOM_Y].value          = 100;
1598 
1599   cm_init (this);
1600 
1601   /*
1602    * check this adaptor's capabilities
1603    */
1604   {
1605     XvAttribute *attr;
1606     int          nattr;
1607 
1608     LOCK_DISPLAY (this);
1609     attr = XvQueryPortAttributes (this->display, xv_port, &nattr);
1610     if (attr && nattr) {
1611       int k;
1612 
1613       for (k = 0; k < nattr; k++) {
1614         if ((attr[k].flags & XvSettable) && (attr[k].flags & XvGettable)) {
1615           const xv_prop_list_t *l = xv_find_prop (attr[k].name);
1616           if (!l)
1617             continue;
1618           if ((l->index == VO_PROP_HUE) && !strncmp (adaptor_name, "NV", 2)) {
1619             xprintf (this->xine, XINE_VERBOSITY_NONE, LOG_MODULE ": ignoring broken XV_HUE settings on NVidia cards\n");
1620             continue;
1621           }
1622           xv_prop_init (this, l, attr[k]);
1623 
1624           switch (l->index) {
1625             case XV_PROP_ITURBT_709: /* nvidia */
1626               this->cm_active = this->props[XV_PROP_ITURBT_709].value ? 2 : 10;
1627               break;
1628             case XV_PROP_COLORSPACE: /* radeonhd */
1629               this->cm_active = this->props[XV_PROP_COLORSPACE].value == 2 ? 2
1630                               : (this->props[XV_PROP_COLORSPACE].value == 1 ? 10 : 0);
1631               break;
1632             case VO_PROP_COLORKEY:
1633               xv_prop_conf (this, VO_PROP_COLORKEY, "video.device.xv_colorkey", VIDEO_DEVICE_XV_COLORKEY_HELP);
1634               break;
1635             case VO_PROP_AUTOPAINT_COLORKEY:
1636               /* disable autopaint colorkey by default, might be overridden using config entry */
1637               this->props[VO_PROP_AUTOPAINT_COLORKEY].value = 0;
1638               xv_prop_conf (this, VO_PROP_AUTOPAINT_COLORKEY,
1639                 "video.device.xv_autopaint_colorkey", VIDEO_DEVICE_XV_AUTOPAINT_COLORKEY_HELP);
1640               break;
1641             case XV_PROP_FILTER: {
1642               /* This setting is specific to Permedia 2/3 cards. */
1643               int xv_filter = config->register_range (config, "video.device.xv_filter", 0,
1644                 attr[k].min_value, attr[k].max_value, VIDEO_DEVICE_XV_FILTER_HELP,
1645                 20, xv_prop_update, &this->props[XV_PROP_FILTER]);
1646               xv_prop_update_int (&this->props[XV_PROP_FILTER], xv_filter);
1647               break;
1648             }
1649             case XV_PROP_DOUBLE_BUFFER: {
1650               int xv_double_buffer = config->register_bool (config, "video.device.xv_double_buffer", 1,
1651                 VIDEO_DEVICE_XV_DOUBLE_BUFFER_HELP, 20, xv_prop_update, &this->props[XV_PROP_DOUBLE_BUFFER]);
1652               xv_prop_update_int (&this->props[XV_PROP_DOUBLE_BUFFER], xv_double_buffer);
1653               break;
1654             }
1655             case XV_PROP_SYNC_TO_VBLANK: {
1656               int xv_sync_to_vblank = config->register_bool (config, "video.device.xv_sync_to_vblank", 1,
1657                 _("enable vblank sync"),
1658                 _("This option will synchronize the update of the video image to the "
1659                   "repainting of the entire screen (\"vertical retrace\"). This eliminates "
1660                   "flickering and tearing artifacts. On nvidia cards one may also "
1661                   "need to run \"nvidia-settings\" and choose which display device to "
1662                   "sync to under the XVideo Settings tab"),
1663                 20, xv_prop_update, &this->props[XV_PROP_SYNC_TO_VBLANK]);
1664               xv_prop_update_int (&this->props[XV_PROP_SYNC_TO_VBLANK], xv_sync_to_vblank);
1665               break;
1666             }
1667             case XV_PROP_BICUBIC: {
1668               int xv_bicubic = config->register_enum (config, "video.device.xv_bicubic", 2,
1669                 (char **)bicubic_types, VIDEO_DEVICE_XV_BICUBIC_HELP, 20, xv_prop_update, &this->props[XV_PROP_BICUBIC]);
1670               xv_prop_update_int (&this->props[XV_PROP_BICUBIC], xv_bicubic);
1671               break;
1672             }
1673           }
1674         }
1675       }
1676       XFree (attr);
1677     } else
1678       xprintf (this->xine, XINE_VERBOSITY_DEBUG, LOG_MODULE ": no port attributes defined.\n");
1679     UNLOCK_DISPLAY (this);
1680   }
1681 
1682   free (adaptor_name);
1683 
1684   this->props[VO_PROP_BRIGHTNESS].defer = 1;
1685   this->props[VO_PROP_CONTRAST].defer   = 1;
1686   this->props[VO_PROP_SATURATION].defer = 1;
1687   if ((this->props[VO_PROP_BRIGHTNESS].atom != None) &&
1688     (this->props[VO_PROP_CONTRAST].atom != None)) {
1689     static const char * const xv_fullrange_conf_labels[] = {"Off", "Normal", NULL};
1690     this->fullrange_mode = config->register_enum (
1691       config,
1692       "video.output.xv_fullrange",
1693       0,
1694       (char **)xv_fullrange_conf_labels,
1695       _("Fullrange colour emulation"),
1696       _("Emulate fullrange colour by modifying brightness/contrast/saturation settings.\n\n"
1697         "Off:    Let decoders convert such video.\n"
1698         "        Works with all graphics cards, but needs a bit more CPU power.\n\n"
1699         "Normal: Fast and better quality. Should work at least with some newer cards\n"
1700         "        (GeForce 7+, 210+).\n\n"),
1701       10,
1702       xv_fullrange_cb_config,
1703       this
1704     );
1705     if (this->fullrange_mode)
1706       this->capabilities |= VO_CAP_FULLRANGE;
1707   }
1708 
1709 #ifdef DEBUG_EMU
1710   this->emu_yuy2 = config->register_bool (
1711     config,
1712     "video.output.xv_debug_emu",
1713     0,
1714     _("Force emulating yuy2"),
1715     _("Debug."),
1716     10,
1717     xv_debug_emu_cb_config,
1718     this
1719   );
1720 #endif
1721 
1722   /*
1723    * check max. supported image size
1724    */
1725   {
1726     unsigned int    nencode = 0;
1727     XvEncodingInfo *encodings = NULL;
1728 
1729     LOCK_DISPLAY (this);
1730     XvQueryEncodings (this->display, xv_port, &nencode, &encodings);
1731     if (encodings) {
1732       unsigned int n;
1733       for (n = 0; n < nencode; n++) {
1734         if (!strcmp (encodings[n].name, "XV_IMAGE")) {
1735           xprintf (this->xine, XINE_VERBOSITY_LOG,
1736             LOG_MODULE ": max XvImage size %li x %li\n", encodings[n].width, encodings[n].height);
1737           this->props[VO_PROP_MAX_VIDEO_WIDTH].value  = encodings[n].width;
1738           this->props[VO_PROP_MAX_VIDEO_HEIGHT].value = encodings[n].height;
1739           break;
1740         }
1741       }
1742       XvFreeEncodingInfo (encodings);
1743     }
1744     UNLOCK_DISPLAY (this);
1745   }
1746 
1747   /*
1748    * try to create a shared image
1749    * to find out if MIT shm really works, using supported format
1750    */
1751   LOCK_DISPLAY(this);
1752   {
1753     XvImage         *myimage;
1754     XShmSegmentInfo  myshminfo;
1755     myimage = create_ximage (this, &myshminfo, 100, 100,
1756       (this->xv_format_yv12 != 0) ? XINE_IMGFMT_YV12 : XINE_IMGFMT_YUY2);
1757     if (myimage)
1758       dispose_ximage (this, &myshminfo, myimage);
1759   }
1760   UNLOCK_DISPLAY(this);
1761 
1762   this->use_pitch_alignment =
1763     config->register_bool (config, "video.device.xv_pitch_alignment", 0,
1764 			   VIDEO_DEVICE_XV_PITCH_ALIGNMENT_HELP,
1765 			   10, xv_update_xv_pitch_alignment, this);
1766 
1767   LOCK_DISPLAY(this);
1768   if ((this->props[VO_PROP_COLORKEY].atom != None) &&
1769       (this->props[VO_PROP_AUTOPAINT_COLORKEY].value != 1)) {
1770     this->xoverlay = x11osd_create (this->xine, this->display, this->screen,
1771                                     this->drawable, X11OSD_COLORKEY);
1772     if(this->xoverlay)
1773       x11osd_colorkey (this->xoverlay, this->props[VO_PROP_COLORKEY].value, &this->sc);
1774   } else {
1775     this->xoverlay = x11osd_create (this->xine, this->display, this->screen,
1776                                     this->drawable, X11OSD_SHAPED);
1777   }
1778   UNLOCK_DISPLAY(this);
1779 
1780   if( this->xoverlay )
1781     this->capabilities |= VO_CAP_UNSCALED_OVERLAY;
1782 
1783   return &this->vo_driver;
1784 }
1785 
open_plugin_old(video_driver_class_t * class_gen,const void * visual_gen)1786 static vo_driver_t *open_plugin_old (video_driver_class_t *class_gen, const void *visual_gen) {
1787   const x11_visual_t  *old_visual  = (const x11_visual_t *) visual_gen;
1788   x11_visual_t         visual;
1789 
1790   /* provides compatibility for XINE_VISUAL_TYPE_X11 */
1791   visual.display         = old_visual->display;
1792   visual.screen          = old_visual->screen;
1793   visual.d               = old_visual->d;
1794   visual.user_data       = old_visual->user_data;
1795   visual.dest_size_cb    = old_visual->dest_size_cb;
1796   visual.frame_output_cb = old_visual->frame_output_cb;
1797   visual.lock_display    = NULL;
1798   visual.unlock_display  = NULL;
1799 
1800   return open_plugin_2(class_gen, (void *)&visual);
1801 }
1802 
1803 /*
1804  * class functions
1805  */
init_class(xine_t * xine,const void * visual_gen)1806 static void *init_class (xine_t *xine, const void *visual_gen) {
1807   xv_class_t *this;
1808 
1809   (void)visual_gen;
1810 
1811   this = calloc(1, sizeof(*this));
1812   if (!this)
1813     return NULL;
1814 
1815   this->driver_class.open_plugin     = open_plugin_old;
1816   this->driver_class.identifier      = "Xv";
1817   this->driver_class.description     = N_("xine video output plugin using the MIT X video extension");
1818   this->driver_class.dispose         = default_video_driver_class_dispose;
1819 
1820   this->xine                         = xine;
1821 
1822   return this;
1823 }
1824 
init_class_2(xine_t * xine,const void * visual_gen)1825 static void *init_class_2 (xine_t *xine, const void *visual_gen) {
1826   xv_class_t	       *this;
1827   this = init_class (xine, visual_gen);
1828   if (this) {
1829     this->driver_class.open_plugin     = open_plugin_2;
1830   }
1831   return this;
1832 }
1833 
1834 static const vo_info_t vo_info_xv = {
1835   .priority    = 9,
1836   .visual_type = XINE_VISUAL_TYPE_X11,
1837 };
1838 
1839 /* visual type with configurable X11 locking */
1840 static const vo_info_t vo_info_xv_2 = {
1841   .priority    = 9,
1842   .visual_type = XINE_VISUAL_TYPE_X11_2,
1843 };
1844 
1845 /*
1846  * exported plugin catalog entry
1847  */
1848 
1849 const plugin_info_t xine_plugin_info[] EXPORTED = {
1850   /* type, API, "name", version, special_info, init_function */
1851   { PLUGIN_VIDEO_OUT, 22, "xv", XINE_VERSION_CODE, &vo_info_xv, init_class },
1852   { PLUGIN_VIDEO_OUT, 22, "xv", XINE_VERSION_CODE, &vo_info_xv_2, init_class_2 },
1853   { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
1854 };
1855 
1856