Cope with OSD changes.
[vaapi:sewalliniusms-mplayer.git] / libvo / vo_vaapi.c
1 /*
2  * VA API output module
3  *
4  * Copyright (C) 2008-2009 Splitted-Desktop Systems
5  *
6  * This file is part of MPlayer.
7  *
8  * MPlayer is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * MPlayer is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include "config.h"
24 #include "mp_msg.h"
25 #include "help_mp.h"
26 #include "subopt-helper.h"
27 #include "video_out.h"
28 #include "video_out_internal.h"
29 #include "fastmemcpy.h"
30 #include "sub/sub.h"
31 #include "sub/eosd.h"
32 #include "sub/ass_mp.h"
33 #include "x11_common.h"
34 #include "libavutil/common.h"
35 #include "libavcodec/vaapi.h"
36 #include "gui/interface.h"
37 #include "stats.h"
38 #include <stdarg.h>
39
40 #if CONFIG_GL
41 #include "gl_common.h"
42 #include <GL/glu.h>
43 #include <GL/glx.h>
44 #endif
45
46 #include <assert.h>
47 #include <X11/Xlib.h>
48 #include <X11/Xutil.h>
49 #include <va/va_x11.h>
50 #if CONFIG_VAAPI_GLX
51 #include <va/va_glx.h>
52 #endif
53
54 #if CONFIG_XRENDER
55 #include <X11/extensions/Xrender.h>
56 #endif
57
58 /* Compatibility glue with VA-API >= 0.30 */
59 #ifndef VA_INVALID_ID
60 #define VA_INVALID_ID           0xffffffff
61 #endif
62 #ifndef VA_FOURCC
63 #define VA_FOURCC(ch0, ch1, ch2, ch3)           \
64     ((uint32_t)(uint8_t)(ch0) |                 \
65      ((uint32_t)(uint8_t)(ch1) << 8) |          \
66      ((uint32_t)(uint8_t)(ch2) << 16) |         \
67      ((uint32_t)(uint8_t)(ch3) << 24 ))
68 #endif
69 #if defined VA_SRC_BT601 && defined VA_SRC_BT709
70 #define USE_VAAPI_COLORSPACE 1
71 #else
72 #define USE_VAAPI_COLORSPACE 0
73 #endif
74
75 /* Defined to 1 if VA/GLX 'bind' API is available */
76 #define USE_VAAPI_GLX_BIND                                \
77     (VA_MAJOR_VERSION == 0 &&                             \
78      ((VA_MINOR_VERSION == 30 &&                          \
79        VA_MICRO_VERSION == 4 && VA_SDS_VERSION >= 5) ||   \
80       (VA_MINOR_VERSION == 31 &&                          \
81        VA_MICRO_VERSION == 0 && VA_SDS_VERSION < 5)))
82
83 /* Compatibility glue with VA-API >= 0.31 */
84 #if defined VA_CHECK_VERSION
85 #if VA_CHECK_VERSION(0,31,0)
86 #define vaPutImage2             vaPutImage
87 #define vaAssociateSubpicture2  vaAssociateSubpicture
88 #endif
89 #endif
90
91 static vo_info_t info = {
92     "VA API with X11",
93     "vaapi",
94     "Gwenole Beauchesne <gbeauchesne@splitted-desktop.com>",
95     ""
96 };
97
98 const LIBVO_EXTERN(vaapi)
99
100 /* Numbers of video surfaces */
101 #define MAX_OUTPUT_SURFACES       2 /* Maintain synchronisation points in flip_page() */
102 #define MAX_VIDEO_SURFACES       21 /* Maintain free surfaces in a queue (use least-recently-used) */
103 #define NUM_VIDEO_SURFACES_MPEG2  3 /* 1 decode frame, up to  2 references */
104 #define NUM_VIDEO_SURFACES_MPEG4  3 /* 1 decode frame, up to  2 references */
105 #define NUM_VIDEO_SURFACES_H264  21 /* 1 decode frame, up to 20 references */
106 #define NUM_VIDEO_SURFACES_VC1    3 /* 1 decode frame, up to  2 references */
107
108 static void ensure_osd(void);
109 static int reset_xrender_specific(void);
110
111 typedef void (*draw_alpha_func)(int x0, int y0, int w, int h,
112                                 unsigned char *src, unsigned char *srca,
113                                 int stride);
114
115 typedef void (*eosd_draw_alpha_func)(unsigned char *src,
116                                      int src_w, int src_h, int src_stride,
117                                      int dst_x, int dst_y,
118                                      uint32_t color);
119
120 struct vaapi_surface {
121     VASurfaceID id;
122     VAImage     image;
123     int         is_bound; /* Flag: image bound to the surface? */
124 };
125
126 struct vaapi_equalizer {
127     VADisplayAttribute brightness;
128     VADisplayAttribute contrast;
129     VADisplayAttribute hue;
130     VADisplayAttribute saturation;
131 };
132
133 static int                      g_is_visible;
134 static int                      g_is_paused;
135 static uint32_t                 g_image_width;
136 static uint32_t                 g_image_height;
137 static uint32_t                 g_image_format;
138 static uint32_t                 g_image_fields;
139 static Pixmap                   g_image_pixmap;
140 static struct vo_rect           g_output_rect;
141 static struct vaapi_surface    *g_output_surfaces[MAX_OUTPUT_SURFACES];
142 static unsigned int             g_output_surface;
143 static int                      g_deint;
144 static int                      g_deint_type;
145 static int                      g_colorspace;
146
147 static int                      gl_enabled;
148 static int                      gl_use_tfp;
149 #if CONFIG_GL
150 static MPGLContext              gl_context;
151 static int                      gl_binding;
152 static int                      gl_reflect;
153 static int                      gl_finish;
154 static GLuint                   gl_texture;
155 static GLuint                   gl_font_base;
156 static Pixmap                   gl_pixmap;
157 static int                      gl_visual_attr[] = {
158     GLX_RGBA,
159     GLX_RED_SIZE, 1,
160     GLX_GREEN_SIZE, 1,
161     GLX_BLUE_SIZE, 1,
162     GLX_DOUBLEBUFFER,
163     GL_NONE
164 };
165 #endif
166
167 #if CONFIG_VAAPI_GLX
168 static void                    *gl_surface;
169 #endif
170
171 static int                      xr_enabled;
172 #if CONFIG_XRENDER
173 static Pixmap                   g_image_pixmap;
174 static Picture                  xr_video_picture;
175 static Picture                  xr_window_picture;
176 #endif
177
178 static struct vaapi_context    *va_context;
179 static VAProfile               *va_profiles;
180 static int                      va_num_profiles;
181 static VAEntrypoint            *va_entrypoints;
182 static int                      va_num_entrypoints;
183 static VASurfaceID             *va_surface_ids;
184 static int                      va_num_surfaces;
185 static struct vaapi_surface   **va_free_surfaces;
186 static int                      va_free_surfaces_head_index;
187 static int                      va_free_surfaces_tail_index;
188 static VAImageFormat           *va_image_formats;
189 static int                      va_num_image_formats;
190 static VAImageFormat           *va_subpic_formats;
191 static unsigned int            *va_subpic_flags;
192 static int                      va_num_subpic_formats;
193 static VAImage                  va_osd_image;
194 static uint8_t                 *va_osd_image_data;
195 static VASubpictureID           va_osd_subpicture;
196 static int                      va_osd_associated;
197 static draw_alpha_func          va_osd_draw_alpha;
198 static uint8_t                 *va_osd_palette;
199 static struct vaapi_equalizer   va_equalizer;
200 static VAImage                  va_eosd_image;
201 static uint8_t                 *va_eosd_image_data;
202 static VASubpictureID           va_eosd_subpicture;
203 static int                      va_eosd_associated;
204 static eosd_draw_alpha_func     va_eosd_draw_alpha;
205
206 ///< Flag: direct surface mapping: use mpi->number to select free VA surface?
207 static int                      va_dm;
208
209 ///< Flag: gather run-time statistics (CPU usage, frequency)
210 static int                      cpu_stats;
211 static unsigned int             cpu_frequency;
212 static float                    cpu_usage;
213
214 // X error trap
215 static int x11_error_code = 0;
216 static int (*old_error_handler)(Display *, XErrorEvent *);
217
218 static int error_handler(Display *dpy, XErrorEvent *error)
219 {
220     x11_error_code = error->error_code;
221     return 0;
222 }
223
224 static void x11_trap_errors(void)
225 {
226     x11_error_code    = 0;
227     old_error_handler = XSetErrorHandler(error_handler);
228 }
229
230 static int x11_untrap_errors(void)
231 {
232     XSetErrorHandler(old_error_handler);
233     return x11_error_code;
234 }
235
236 static int check_status(VAStatus status, const char *msg)
237 {
238     if (status != VA_STATUS_SUCCESS) {
239         mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] %s: %s\n", msg, vaErrorStr(status));
240         return 0;
241     }
242     return 1;
243 }
244
245 static const char *string_of_VAImageFormat(VAImageFormat *imgfmt)
246 {
247     static char str[5];
248     str[0] = imgfmt->fourcc;
249     str[1] = imgfmt->fourcc >> 8;
250     str[2] = imgfmt->fourcc >> 16;
251     str[3] = imgfmt->fourcc >> 24;
252     str[4] = '\0';
253     return str;
254 }
255
256 static const char *string_of_VAProfile(VAProfile profile)
257 {
258     switch (profile) {
259 #define PROFILE(profile) \
260         case VAProfile##profile: return "VAProfile" #profile
261         PROFILE(MPEG2Simple);
262         PROFILE(MPEG2Main);
263         PROFILE(MPEG4Simple);
264         PROFILE(MPEG4AdvancedSimple);
265         PROFILE(MPEG4Main);
266         PROFILE(H264Baseline);
267         PROFILE(H264Main);
268         PROFILE(H264High);
269         PROFILE(VC1Simple);
270         PROFILE(VC1Main);
271         PROFILE(VC1Advanced);
272 #undef PROFILE
273     }
274     return "<unknown>";
275 }
276
277 static const char *string_of_VAEntrypoint(VAEntrypoint entrypoint)
278 {
279     switch (entrypoint) {
280 #define ENTRYPOINT(entrypoint) \
281         case VAEntrypoint##entrypoint: return "VAEntrypoint" #entrypoint
282         ENTRYPOINT(VLD);
283         ENTRYPOINT(IZZ);
284         ENTRYPOINT(IDCT);
285         ENTRYPOINT(MoComp);
286         ENTRYPOINT(Deblocking);
287 #undef ENTRYPOINT
288     }
289     return "<unknown>";
290 }
291
292 static int has_profile(VAProfile profile)
293 {
294     if (va_profiles && va_num_profiles > 0) {
295         int i;
296         for (i = 0; i < va_num_profiles; i++) {
297             if (va_profiles[i] == profile)
298                 return 1;
299         }
300     }
301     return 0;
302 }
303
304 static int VAProfile_from_imgfmt(uint32_t format)
305 {
306     static const int mpeg2_profiles[] =
307         { VAProfileMPEG2Main, VAProfileMPEG2Simple, -1 };
308     static const int mpeg4_profiles[] =
309         { VAProfileMPEG4Main, VAProfileMPEG4AdvancedSimple, VAProfileMPEG4Simple, -1 };
310     static const int h264_profiles[] =
311         { VAProfileH264High, VAProfileH264Main, VAProfileH264Baseline, -1 };
312     static const int wmv3_profiles[] =
313         { VAProfileVC1Main, VAProfileVC1Simple, -1 };
314     static const int vc1_profiles[] =
315         { VAProfileVC1Advanced, -1 };
316
317     const int *profiles = NULL;
318     switch (IMGFMT_VAAPI_CODEC(format)) {
319     case IMGFMT_VAAPI_CODEC_MPEG2:
320         profiles = mpeg2_profiles;
321         break;
322     case IMGFMT_VAAPI_CODEC_MPEG4:
323         profiles = mpeg4_profiles;
324         break;
325     case IMGFMT_VAAPI_CODEC_H264:
326         profiles = h264_profiles;
327         break;
328     case IMGFMT_VAAPI_CODEC_VC1:
329         switch (format) {
330         case IMGFMT_VAAPI_WMV3:
331             profiles = wmv3_profiles;
332             break;
333         case IMGFMT_VAAPI_VC1:
334             profiles = vc1_profiles;
335             break;
336         }
337         break;
338     }
339
340     if (profiles) {
341         for (int i = 0; profiles[i] != -1; i++) {
342             if (has_profile(profiles[i]))
343                 return profiles[i];
344         }
345     }
346     return -1;
347 }
348
349 static int has_entrypoint(VAEntrypoint entrypoint)
350 {
351     if (va_entrypoints && va_num_entrypoints > 0) {
352         int i;
353         for (i = 0; i < va_num_entrypoints; i++) {
354             if (va_entrypoints[i] == entrypoint)
355                 return 1;
356         }
357     }
358     return 0;
359 }
360
361 static int VAEntrypoint_from_imgfmt(uint32_t format)
362 {
363     int entrypoint = 0;
364     switch (format) {
365     case IMGFMT_VAAPI_MPEG2:
366     case IMGFMT_VAAPI_MPEG4:
367     case IMGFMT_VAAPI_H263:
368     case IMGFMT_VAAPI_H264:
369     case IMGFMT_VAAPI_WMV3:
370     case IMGFMT_VAAPI_VC1:
371         entrypoint = VAEntrypointVLD;
372         break;
373     case IMGFMT_VAAPI_MPEG2_IDCT:
374         entrypoint = VAEntrypointIDCT;
375         break;
376     case IMGFMT_VAAPI_MPEG2_MOCO:
377         entrypoint = VAEntrypointMoComp;
378         break;
379     }
380
381     if (entrypoint)
382         return has_entrypoint(entrypoint);
383
384     return -1;
385 }
386
387 static VAImageFormat *find_image_format(uint32_t fourcc)
388 {
389     if (va_image_formats && va_num_image_formats > 0) {
390         int i;
391         for (i = 0; i < va_num_image_formats; i++) {
392             if (va_image_formats[i].fourcc == fourcc)
393                 return &va_image_formats[i];
394         }
395     }
396     return NULL;
397 }
398
399 static VAImageFormat *VAImageFormat_from_imgfmt(uint32_t format)
400 {
401     uint32_t fourcc = 0;
402
403     switch (format) {
404     case IMGFMT_NV12: fourcc = VA_FOURCC('N','V','1','2'); break;
405     case IMGFMT_YV12: fourcc = VA_FOURCC('Y','V','1','2'); break;
406     case IMGFMT_I420: fourcc = VA_FOURCC('I','4','2','0'); break;
407     case IMGFMT_IYUV: fourcc = VA_FOURCC('I','Y','U','V'); break;
408     }
409
410     if (fourcc)
411         return find_image_format(fourcc);
412
413     return NULL;
414 }
415
416 static struct vaapi_surface *alloc_vaapi_surface(unsigned int width,
417                                                  unsigned int height,
418                                                  unsigned int format)
419 {
420     struct vaapi_surface *surface = NULL;
421     struct vaapi_surface **surfaces;
422     VASurfaceID *surface_ids;
423     VAStatus status;
424
425     surface = calloc(1, sizeof(*surface));
426     if (!surface)
427         goto error;
428
429     surfaces = realloc(va_free_surfaces,
430                        (1 + va_num_surfaces) * sizeof(surfaces[0]));
431     if (!surfaces)
432         goto error;
433
434     surface_ids = realloc(va_surface_ids,
435                           (1 + va_num_surfaces) * sizeof(surface_ids[0]));
436     if (!surface_ids)
437         goto error;
438
439     status = vaCreateSurfaces(va_context->display, width, height, format,
440                               1, &surface->id);
441     if (!check_status(status, "vaCreateSurfaces()"))
442         goto error;
443
444     va_surface_ids                    = surface_ids;
445     va_surface_ids[va_num_surfaces]   = surface->id;
446     va_free_surfaces                  = surfaces;
447     va_free_surfaces[va_num_surfaces] = surface;
448     surface->image.image_id           = VA_INVALID_ID;
449     surface->image.buf                = VA_INVALID_ID;
450     ++va_num_surfaces;
451     return surface;
452 error:
453     free(surface);
454     return NULL;
455 }
456
457 static void resize(void)
458 {
459     struct vo_rect src;
460
461     calc_src_dst_rects(g_image_width, g_image_height,
462                        &src, &g_output_rect, NULL, NULL);
463
464     ensure_osd();
465
466     vo_x11_clearwindow(mDisplay, vo_window);
467
468 #if CONFIG_GL
469 #define FOVY     60.0f
470 #define ASPECT   1.0f
471 #define Z_NEAR   0.1f
472 #define Z_FAR    100.0f
473 #define Z_CAMERA 0.869f
474
475     if (gl_enabled) {
476         glViewport(0, 0, vo_dwidth, vo_dheight);
477         glMatrixMode(GL_PROJECTION);
478         glLoadIdentity();
479         gluPerspective(FOVY, ASPECT, Z_NEAR, Z_FAR);
480         glMatrixMode(GL_MODELVIEW);
481         glLoadIdentity();
482
483         glTranslatef(-0.5f, -0.5f, -Z_CAMERA);
484         glScalef(1.0f / (GLfloat)vo_dwidth,
485                  -1.0f / (GLfloat)vo_dheight,
486                  1.0f / (GLfloat)vo_dwidth);
487         glTranslatef(0.0f, -1.0f * (GLfloat)vo_dheight, 0.0f);
488     }
489 #endif
490
491 #if CONFIG_XRENDER
492     if (xr_enabled)
493         reset_xrender_specific();
494 #endif
495
496     if (g_is_visible)
497         flip_page();
498 }
499
500 #if CONFIG_GL
501 static int gl_build_font(void)
502 {
503     XFontStruct *fi;
504
505     gl_font_base = glGenLists(96);
506
507     fi = XLoadQueryFont(mDisplay, "-adobe-helvetica-medium-r-normal--16-*-*-*-p-*-iso8859-1" );
508     if (!fi) {
509         fi = XLoadQueryFont(mDisplay, "fixed");
510         if (!fi)
511             return -1;
512     }
513
514     glXUseXFont(fi->fid, 32, 96, gl_font_base);
515     XFreeFont(mDisplay, fi);
516     return 0;
517 }
518
519 static void gl_printf(const char *format, ...)
520 {
521     va_list args;
522     char *text;
523     int textlen;
524
525     va_start(args, format);
526     textlen = vsnprintf(NULL, 0, format, args);
527     va_end(args);
528
529     text = malloc(textlen + 1);
530     if (!text)
531         return;
532
533     va_start(args, format);
534     vsprintf(text, format, args);
535     va_end(args);
536
537     glPushAttrib(GL_LIST_BIT);
538     glListBase(gl_font_base - 32);
539     glCallLists(textlen, GL_UNSIGNED_BYTE, text);
540     glPopAttrib();
541     free(text);
542 }
543
544 static void gl_draw_rectangle(int x, int y, int w, int h, unsigned int rgba)
545 {
546     glColor4f((GLfloat)((rgba >> 24) & 0xff) / 255.0,
547               (GLfloat)((rgba >> 16) & 0xff) / 255.0,
548               (GLfloat)((rgba >> 8) & 0xff) / 255.0,
549               (GLfloat)(rgba & 0xff) / 255.0);
550
551     glTranslatef((GLfloat)x, (GLfloat)y, 0.0f);
552     glBegin(GL_QUADS);
553     {
554         glVertex2i(0, 0);
555         glVertex2i(w, 0);
556         glVertex2i(w, h);
557         glVertex2i(0, h);
558     }
559     glEnd();
560 }
561 #endif
562
563 #if CONFIG_XRENDER
564 static int init_xrender(void)
565 {
566     int dummy;
567
568     return XRenderQueryExtension(mDisplay, &dummy, &dummy);
569 }
570
571 static void uninit_xrender(void)
572 {
573 }
574 #endif
575
576 static inline unsigned char *get_osd_image_data(int x0, int y0)
577 {
578     return (va_osd_image_data +
579             va_osd_image.offsets[0] +
580             va_osd_image.pitches[0] * y0 +
581             x0 * ((va_osd_image.format.bits_per_pixel + 7) / 8));
582 }
583
584 static void draw_alpha_rgb32(int x0, int y0, int w, int h,
585                              unsigned char *src, unsigned char *srca,
586                              int stride)
587 {
588     int x, y;
589     const unsigned int dststride = va_osd_image.pitches[0];
590     unsigned char *dst = get_osd_image_data(x0, y0);
591
592     for (y = 0; y < h; y++, dst += dststride, src += stride, srca += stride)
593         for (x = 0; x < w; x++) {
594             const unsigned char c = src[x];
595             dst[4*x + 0] = c;
596             dst[4*x + 1] = c;
597             dst[4*x + 2] = c;
598             dst[4*x + 3] = -srca[x];
599         }
600 }
601
602 static void draw_alpha_IA44(int x0, int y0, int w, int h,
603                             unsigned char *src, unsigned char *srca,
604                             int stride)
605 {
606     int x, y;
607     const unsigned int dststride = va_osd_image.pitches[0];
608     unsigned char *dst = get_osd_image_data(x0, y0);
609
610     for (y = 0; y < h; y++, dst += dststride)
611         for (x = 0; x < w; x++)
612             dst[x] = (src[y*stride + x] & 0xf0) | (-srca[y*stride + x] >> 4);
613 }
614
615 static void draw_alpha_AI44(int x0, int y0, int w, int h,
616                             unsigned char *src, unsigned char *srca,
617                             int stride)
618 {
619     int x, y;
620     const unsigned int dststride = va_osd_image.pitches[0];
621     unsigned char *dst = get_osd_image_data(x0, y0);
622
623     for (y = 0; y < h; y++, dst += dststride)
624         for (x = 0; x < w; x++)
625             dst[x] = (src[y*stride + x] >> 4) | (-srca[y*stride + x] & 0xf0);
626 }
627
628 static void draw_alpha_IA88(int x0, int y0, int w, int h,
629                             unsigned char *src, unsigned char *srca,
630                             int stride)
631 {
632     int x, y;
633     const unsigned int dststride = va_osd_image.pitches[0];
634     unsigned char *dst = get_osd_image_data(x0, y0);
635
636     for (y = 0; y < h; y++, dst += dststride)
637         for (x = 0; x < w; x++) {
638             dst[2*x + 0] =  src [y*stride + x];
639             dst[2*x + 1] = -srca[y*stride + x];
640         }
641 }
642
643 static void draw_alpha_AI88(int x0, int y0, int w, int h,
644                             unsigned char *src, unsigned char *srca,
645                             int stride)
646 {
647     int x, y;
648     const unsigned int dststride = va_osd_image.pitches[0];
649     unsigned char *dst = get_osd_image_data(x0, y0);
650
651     for (y = 0; y < h; y++, dst += dststride)
652         for (x = 0; x < w; x++) {
653             dst[2*x + 0] = -srca[y*stride + x];
654             dst[2*x + 1] =  src [y*stride + x];
655         }
656 }
657
658 ///< List of subpicture formats in preferred order
659 static const struct {
660     uint32_t format;
661     draw_alpha_func draw_alpha;
662 }
663 va_osd_info[] = {
664     { VA_FOURCC('I','A','4','4'), draw_alpha_IA44  },
665     { VA_FOURCC('A','I','4','4'), draw_alpha_AI44  },
666     { VA_FOURCC('I','A','8','8'), draw_alpha_IA88  },
667     { VA_FOURCC('A','I','8','8'), draw_alpha_AI88  },
668     { VA_FOURCC('B','G','R','A'), draw_alpha_rgb32 },
669     { VA_FOURCC('R','G','B','A'), draw_alpha_rgb32 },
670     { 0, NULL }
671 };
672
673 static uint8_t *gen_osd_palette(const VAImage *image)
674 {
675     uint8_t *palette;
676     int i, is_rgb;
677     int r_idx = -1, g_idx = -1, b_idx = -1;
678     int y_idx = -1, u_idx = -1, v_idx = -1;
679     int i_idx = -1, a_idx = -1;
680
681     if (image->num_palette_entries < 1)
682         return NULL;
683
684     palette = malloc(image->num_palette_entries * image->entry_bytes);
685     if (!palette)
686         return NULL;
687
688     for (i = 0; i < image->entry_bytes; i++) {
689         switch (image->component_order[i]) {
690         case 'R': r_idx = i; is_rgb = 1; break;
691         case 'G': g_idx = i; is_rgb = 1; break;
692         case 'B': b_idx = i; is_rgb = 1; break;
693         case 'Y': y_idx = i; is_rgb = 0; break;
694         case 'U': u_idx = i; is_rgb = 0; break;
695         case 'V': v_idx = i; is_rgb = 0; break;
696         case 'I': i_idx = i; break;
697         case 'A': a_idx = i; break;
698         }
699     }
700
701     if (r_idx != -1 && g_idx != -1 && b_idx != -1) {      /* RGB format */
702         for (i = 0; i < image->num_palette_entries; i++) {
703             const int n = i * image->entry_bytes;
704             palette[n + r_idx] = i * 0xff / (image->num_palette_entries - 1);
705             palette[n + g_idx] = i * 0xff / (image->num_palette_entries - 1);
706             palette[n + b_idx] = i * 0xff / (image->num_palette_entries - 1);
707         }
708     }
709     else if (y_idx != -1 && u_idx != -1 && v_idx != -1) { /* YUV format */
710         for (i = 0; i < image->num_palette_entries; i++) {
711             const int n = i * image->entry_bytes;
712             palette[n + y_idx] = i * 0xff / (image->num_palette_entries - 1);
713             palette[n + u_idx] = 0x80;
714             palette[n + v_idx] = 0x80;
715         }
716     }
717     else if (i_idx != -1 && a_idx != -1) {/* AYUV format (GMA500 "psb" bug) */
718         for (i = 0; i < image->num_palette_entries; i++) {
719             const int n = i * image->entry_bytes;
720             palette[n + 0] = 0x80;
721             palette[n + 1] = 0x80;
722             palette[n + 2] = 16 + i * 220 / (image->num_palette_entries - 1);
723             palette[n + 3] = 0;
724         }
725     }
726     else {
727         mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] Could not set up subpicture palette\n");
728         free(palette);
729         palette = NULL;
730     }
731     return palette;
732 }
733
734 static void disable_osd(void)
735 {
736     if (!va_osd_associated)
737         return;
738
739     vaDeassociateSubpicture(va_context->display,
740                             va_osd_subpicture,
741                             va_surface_ids, va_num_surfaces);
742
743     va_osd_associated = 0;
744 }
745
746 static int enable_osd(void)
747 {
748     VAStatus status;
749
750     disable_osd();
751
752     status = vaAssociateSubpicture2(va_context->display,
753                                     va_osd_subpicture,
754                                     va_surface_ids, va_num_surfaces,
755                                     0, 0,
756                                     va_osd_image.width, va_osd_image.height,
757                                     0, 0,
758                                     g_image_width, g_image_height,
759                                     0);
760     if (!check_status(status, "vaAssociateSubpicture()"))
761         return -1;
762
763     va_osd_associated = 1;
764     return 0;
765 }
766
767 static void destroy_osd(void)
768 {
769     disable_osd();
770
771     if (va_osd_subpicture != VA_INVALID_ID) {
772         vaDestroySubpicture(va_context->display, va_osd_subpicture);
773         va_osd_subpicture = VA_INVALID_ID;
774     }
775
776     if (va_osd_image.image_id != VA_INVALID_ID) {
777         vaDestroyImage(va_context->display, va_osd_image.image_id);
778         va_osd_image.image_id = VA_INVALID_ID;
779         va_osd_image.width    = 0;
780         va_osd_image.height   = 0;
781     }
782 }
783
784 static void create_osd(void)
785 {
786     VAStatus status;
787     int i, j;
788
789     for (i = 0; va_osd_info[i].format; i++) {
790         for (j = 0; j < va_num_subpic_formats; j++)
791             if (va_subpic_formats[j].fourcc == va_osd_info[i].format)
792                 break;
793         if (j < va_num_subpic_formats &&
794             vaCreateImage(va_context->display, &va_subpic_formats[j],
795                           g_output_rect.width, g_output_rect.height,
796                           &va_osd_image) == VA_STATUS_SUCCESS) {
797             va_osd_palette = gen_osd_palette(&va_osd_image);
798             if (((!va_osd_image.num_palette_entries) ^ (!va_osd_palette)) == 0)
799                 break;
800             vaDestroyImage(va_context->display, va_osd_image.image_id);
801             va_osd_image.image_id = VA_INVALID_ID;
802         }
803     }
804
805     if (va_osd_info[i].format &&
806         vaCreateSubpicture(va_context->display, va_osd_image.image_id,
807                            &va_osd_subpicture) == VA_STATUS_SUCCESS) {
808         va_osd_draw_alpha = va_osd_info[i].draw_alpha;
809         if (va_osd_palette) {
810             status = vaSetImagePalette(va_context->display,
811                                        va_osd_image.image_id, va_osd_palette);
812             check_status(status, "vaSetImagePalette()");
813         }
814         mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] Using %s surface for OSD\n",
815                string_of_VAImageFormat(&va_osd_image.format));
816     }
817 }
818
819 static void ensure_osd(void)
820 {
821     if (g_output_rect.width  == va_osd_image.width &&
822         g_output_rect.height == va_osd_image.height)
823         return;
824
825     destroy_osd();
826     create_osd();
827 }
828
829 static inline unsigned char *get_eosd_image_data(int x0, int y0)
830 {
831     return (va_eosd_image_data +
832             va_eosd_image.offsets[0] +
833             va_eosd_image.pitches[0] * y0 +
834             x0 * ((va_eosd_image.format.bits_per_pixel + 7) / 8));
835 }
836
837 static void eosd_draw_alpha_bgra(unsigned char *src,
838                                  int src_w, int src_h, int src_stride,
839                                  int dst_x, int dst_y,
840                                  uint32_t color)
841 {
842     int x, y;
843     const unsigned int dst_stride = va_eosd_image.pitches[0];
844     unsigned char *dst = get_eosd_image_data(dst_x, dst_y);
845     const unsigned int r = (color >> 24) & 0xff;
846     const unsigned int g = (color >> 16) & 0xff;
847     const unsigned int b = (color >>  8) & 0xff;
848     const unsigned int a = 0xff - (color & 0xff);
849
850     for (y = 0; y < src_h; y++, dst += dst_stride, src += src_stride)
851         for (x = 0; x < src_w; x++) {
852             const unsigned int v = src[x];
853             dst[4*x + 0] = (b * v + dst[4*x + 0] * (0xff - v)) / 255;
854             dst[4*x + 1] = (g * v + dst[4*x + 1] * (0xff - v)) / 255;
855             dst[4*x + 2] = (r * v + dst[4*x + 2] * (0xff - v)) / 255;
856             dst[4*x + 3] = (a * v + dst[4*x + 3] * (0xff - v)) / 255;
857         }
858 }
859
860 static void eosd_draw_alpha_rgba(unsigned char *src,
861                                  int src_w, int src_h, int src_stride,
862                                  int dst_x, int dst_y,
863                                  uint32_t color)
864 {
865     int x, y;
866     const unsigned int dst_stride = va_eosd_image.pitches[0];
867     unsigned char *dst = get_eosd_image_data(dst_x, dst_y);
868     const unsigned int r = (color >> 24) & 0xff;
869     const unsigned int g = (color >> 16) & 0xff;
870     const unsigned int b = (color >>  8) & 0xff;
871     const unsigned int a = 0xff - (color & 0xff);
872
873     for (y = 0; y < src_h; y++, dst += dst_stride, src += src_stride)
874         for (x = 0; x < src_w; x++) {
875             const unsigned int v = src[x];
876             dst[4*x + 0] = (r * v + dst[4*x + 0] * (0xff - v)) / 255;
877             dst[4*x + 1] = (g * v + dst[4*x + 1] * (0xff - v)) / 255;
878             dst[4*x + 2] = (b * v + dst[4*x + 2] * (0xff - v)) / 255;
879             dst[4*x + 3] = (a * v + dst[4*x + 3] * (0xff - v)) / 255;
880         }
881 }
882
883 static void disable_eosd(void)
884 {
885     if (!va_eosd_associated)
886         return;
887
888     vaDeassociateSubpicture(va_context->display,
889                             va_eosd_subpicture,
890                             va_surface_ids, va_num_surfaces);
891
892     va_eosd_associated = 0;
893 }
894
895 static int enable_eosd(void)
896 {
897     VAStatus status;
898
899     if (va_eosd_associated)
900         return 0;
901
902     status = vaAssociateSubpicture2(va_context->display,
903                                     va_eosd_subpicture,
904                                     va_surface_ids, va_num_surfaces,
905                                     0, 0, g_image_width, g_image_height,
906                                     0, 0, g_image_width, g_image_height,
907                                     0);
908     if (!check_status(status, "vaAssociateSubpicture()"))
909         return -1;
910
911     va_eosd_associated = 1;
912     return 0;
913 }
914
915 ///< List of subpicture formats in preferred order
916 static const struct {
917     uint32_t format;
918     eosd_draw_alpha_func draw_alpha;
919 }
920 va_eosd_info[] = {
921     { VA_FOURCC('B','G','R','A'), eosd_draw_alpha_bgra },
922     { VA_FOURCC('R','G','B','A'), eosd_draw_alpha_rgba },
923     { 0, NULL }
924 };
925
926 static int is_direct_mapping_init(void)
927 {
928     VADisplayAttribute attr;
929     VAStatus status;
930
931     if (va_dm < 2)
932         return va_dm;
933
934     /* If the driver doesn't make a copy of the VA surface for
935        display, then we have to retain it until it's no longer the
936        visible surface. In other words, if the driver is using
937        DirectSurface mode, we don't want to decode the new surface
938        into the previous one that was used for display. */
939     attr.type  = VADisplayAttribDirectSurface;
940     attr.flags = VA_DISPLAY_ATTRIB_GETTABLE;
941
942     status = vaGetDisplayAttributes(va_context->display, &attr, 1);
943     if (status == VA_STATUS_SUCCESS)
944         return !attr.value;
945     return 0;
946 }
947
948 static inline int is_direct_mapping(void)
949 {
950     static int dm = -1;
951     if (dm < 0) {
952         dm = is_direct_mapping_init();
953         if (dm)
954             mp_msg(MSGT_VO, MSGL_INFO,
955                    "[vo_vaapi] Using 1:1 VA surface mapping\n");
956     }
957     return dm;
958 }
959
960 static int int_012(int *n)
961 {
962     return *n >= 0 && *n <= 2;
963 }
964
965 static const opt_t subopts[] = {
966     { "dm",          OPT_ARG_INT,  &va_dm,        (opt_test_f)int_012 },
967     { "stats",       OPT_ARG_BOOL, &cpu_stats,    NULL },
968     { "deint",       OPT_ARG_INT,  &g_deint,      (opt_test_f)int_012 },
969 #if USE_VAAPI_COLORSPACE
970     { "colorspace",  OPT_ARG_INT,  &g_colorspace, (opt_test_f)int_012 },
971 #endif
972 #if CONFIG_GL
973     { "gl",          OPT_ARG_BOOL, &gl_enabled,   NULL },
974     { "glfinish",    OPT_ARG_BOOL, &gl_finish,    NULL },
975 #if USE_VAAPI_GLX_BIND
976     { "bind",        OPT_ARG_BOOL, &gl_binding,   NULL },
977 #endif
978     { "reflect",     OPT_ARG_BOOL, &gl_reflect,   NULL },
979     { "tfp",         OPT_ARG_BOOL, &gl_use_tfp,   NULL },
980 #endif
981 #if CONFIG_XRENDER
982     { "xrender",     OPT_ARG_BOOL, &xr_enabled,   NULL },
983 #endif
984     { NULL, }
985 };
986
987 static int preinit(const char *arg)
988 {
989     VADisplayAttribute *display_attrs;
990     VAStatus status;
991     int va_major_version, va_minor_version;
992     int i, max_image_formats, max_subpic_formats, max_profiles;
993     int num_display_attrs, max_display_attrs;
994
995     va_dm = 2;
996     g_deint = 0;
997     g_deint_type = 2;
998     g_colorspace = 1;
999     if (subopt_parse(arg, subopts) != 0) {
1000         mp_msg(MSGT_VO, MSGL_FATAL,
1001                "\n-vo vaapi command line help:\n"
1002                "Example: mplayer -vo vaapi:gl\n"
1003                "\nOptions:\n"
1004                "  dm\n"
1005                "    0: use least-recently-used VA surface\n"
1006                "    1: identify VA surface with MPI index\n"
1007                "    2: auto-detect use of direct surface mapping (default)\n"
1008                "  deint (all modes > 0 respect -field-dominance)\n"
1009                "    0: no deinterlacing (default)\n"
1010                "    1: only show first field\n"
1011                "    2: bob deinterlacing\n"
1012                "  colorspace\n"
1013                "    0: guess based on video resolution\n"
1014                "    1: ITU-R BT.601 (default)\n"
1015                "    2: ITU-R BT.709\n"
1016 #if CONFIG_GL
1017                "  gl\n"
1018                "    Enable OpenGL rendering\n"
1019                "  glfinish\n"
1020                "    Call glFinish() before swapping buffers\n"
1021                "  tfp\n"
1022                "    Use GLX texture-from-pixmap instead of VA/GLX extensions\n"
1023 #if USE_VAAPI_GLX_BIND
1024                "  bind\n"
1025                "    Use VA surface binding instead of copy\n"
1026 #endif
1027                "  reflect\n"
1028                "    Enable OpenGL reflection effects\n"
1029 #endif
1030 #if CONFIG_XRENDER
1031                "  xrender\n"
1032                "    Enable Xrender rendering, thus vaPutSurface() to a Pixmap\n"
1033 #endif
1034                "\n" );
1035         return -1;
1036     }
1037     if (gl_enabled && xr_enabled) {
1038         mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] User requested both Xrender and OpenGL rendering\n");
1039         return -1;
1040     }
1041     if (g_deint)
1042         g_deint_type = g_deint;
1043 #if CONFIG_GL
1044     if (gl_enabled)
1045         mp_msg(MSGT_VO, MSGL_INFO, "[vo_vaapi] Using OpenGL rendering%s\n",
1046                gl_reflect ? ", with reflection effects" : "");
1047 #endif
1048 #if CONFIG_XRENDER
1049     if (xr_enabled)
1050         mp_msg(MSGT_VO, MSGL_INFO, "[vo_vaapi] Using Xrender rendering\n");
1051 #endif
1052
1053     stats_init();
1054
1055 #if CONFIG_GL
1056     if (gl_enabled && !init_mpglcontext(&gl_context, GLTYPE_X11))
1057         return -1;
1058     else
1059 #endif
1060     if (!vo_init())
1061         return -1;
1062 #if CONFIG_XRENDER
1063     if (xr_enabled && !init_xrender())
1064         return -1;
1065 #endif
1066
1067     va_context = calloc(1, sizeof(*va_context));
1068     if (!va_context)
1069         return -1;
1070
1071 #if CONFIG_VAAPI_GLX
1072     if (gl_enabled)
1073         va_context->display = vaGetDisplayGLX(mDisplay);
1074     else
1075 #endif
1076         va_context->display = vaGetDisplay(mDisplay);
1077     if (!va_context->display)
1078         return -1;
1079     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): VA display %p\n", va_context->display);
1080
1081     status = vaInitialize(va_context->display, &va_major_version, &va_minor_version);
1082     if (!check_status(status, "vaInitialize()"))
1083         return -1;
1084     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): VA API version %d.%d\n",
1085            va_major_version, va_minor_version);
1086
1087     max_image_formats = vaMaxNumImageFormats(va_context->display);
1088     va_image_formats = calloc(max_image_formats, sizeof(*va_image_formats));
1089     if (!va_image_formats)
1090         return -1;
1091     status = vaQueryImageFormats(va_context->display, va_image_formats, &va_num_image_formats);
1092     if (!check_status(status, "vaQueryImageFormats()"))
1093         return -1;
1094     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): %d image formats available\n",
1095            va_num_image_formats);
1096     for (i = 0; i < va_num_image_formats; i++)
1097         mp_msg(MSGT_VO, MSGL_DBG2, "  %s\n", string_of_VAImageFormat(&va_image_formats[i]));
1098
1099     max_subpic_formats = vaMaxNumSubpictureFormats(va_context->display);
1100     va_subpic_formats = calloc(max_subpic_formats, sizeof(*va_subpic_formats));
1101     if (!va_subpic_formats)
1102         return -1;
1103     va_subpic_flags = calloc(max_subpic_formats, sizeof(*va_subpic_flags));
1104     if (!va_subpic_flags)
1105         return -1;
1106     status = vaQuerySubpictureFormats(va_context->display, va_subpic_formats, va_subpic_flags, &va_num_subpic_formats);
1107     if (!check_status(status, "vaQuerySubpictureFormats()"))
1108         va_num_subpic_formats = 0; /* XXX: don't error out for IEGD */
1109     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): %d subpicture formats available\n",
1110            va_num_subpic_formats);
1111     for (i = 0; i < va_num_subpic_formats; i++)
1112         mp_msg(MSGT_VO, MSGL_DBG2, "  %s, flags 0x%x\n", string_of_VAImageFormat(&va_subpic_formats[i]), va_subpic_flags[i]);
1113
1114     max_profiles = vaMaxNumProfiles(va_context->display);
1115     va_profiles = calloc(max_profiles, sizeof(*va_profiles));
1116     if (!va_profiles)
1117         return -1;
1118     status = vaQueryConfigProfiles(va_context->display, va_profiles, &va_num_profiles);
1119     if (!check_status(status, "vaQueryConfigProfiles()"))
1120         return -1;
1121     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] preinit(): %d profiles available\n",
1122            va_num_profiles);
1123     for (i = 0; i < va_num_profiles; i++)
1124         mp_msg(MSGT_VO, MSGL_DBG2, "  %s\n", string_of_VAProfile(va_profiles[i]));
1125
1126     va_osd_subpicture      = VA_INVALID_ID;
1127     va_osd_image.image_id  = VA_INVALID_ID;
1128     va_eosd_subpicture     = VA_INVALID_ID;
1129     va_eosd_image.image_id = VA_INVALID_ID;
1130
1131     max_display_attrs = vaMaxNumDisplayAttributes(va_context->display);
1132     display_attrs = calloc(max_display_attrs, sizeof(*display_attrs));
1133     if (display_attrs) {
1134         num_display_attrs = 0;
1135         status = vaQueryDisplayAttributes(va_context->display,
1136                                           display_attrs, &num_display_attrs);
1137         if (check_status(status, "vaQueryDisplayAttributes()")) {
1138             for (i = 0; i < num_display_attrs; i++) {
1139                 VADisplayAttribute *attr;
1140                 switch (display_attrs[i].type) {
1141                 case VADisplayAttribBrightness:
1142                     attr = &va_equalizer.brightness;
1143                     break;
1144                 case VADisplayAttribContrast:
1145                     attr = &va_equalizer.contrast;
1146                     break;
1147                 case VADisplayAttribHue:
1148                     attr = &va_equalizer.hue;
1149                     break;
1150                 case VADisplayAttribSaturation:
1151                     attr = &va_equalizer.saturation;
1152                     break;
1153                 default:
1154                     attr = NULL;
1155                     break;
1156                 }
1157                 if (attr)
1158                     *attr = display_attrs[i];
1159             }
1160         }
1161         free(display_attrs);
1162     }
1163     return 0;
1164 }
1165
1166 static void free_video_specific(void)
1167 {
1168     int i;
1169
1170 #if CONFIG_VAAPI_GLX
1171     if (gl_surface) {
1172         VAStatus status;
1173         status = vaDestroySurfaceGLX(va_context->display, gl_surface);
1174         check_status(status, "vaDestroySurfaceGLX()");
1175         gl_surface = NULL;
1176     }
1177 #endif
1178
1179     if (va_context && va_context->context_id) {
1180         vaDestroyContext(va_context->display, va_context->context_id);
1181         va_context->context_id = 0;
1182     }
1183
1184     if (va_free_surfaces) {
1185         for (i = 0; i < va_num_surfaces; i++) {
1186             if (!va_free_surfaces[i])
1187                 continue;
1188             if (va_free_surfaces[i]->image.image_id != VA_INVALID_ID) {
1189                 vaDestroyImage(va_context->display,
1190                                va_free_surfaces[i]->image.image_id);
1191                 va_free_surfaces[i]->image.image_id = VA_INVALID_ID;
1192             }
1193             free(va_free_surfaces[i]);
1194             va_free_surfaces[i] = NULL;
1195         }
1196         free(va_free_surfaces);
1197         va_free_surfaces = NULL;
1198         va_free_surfaces_head_index = 0;
1199         va_free_surfaces_tail_index = 0;
1200     }
1201
1202     g_output_surface = 0;
1203     memset(g_output_surfaces, 0, sizeof(g_output_surfaces));
1204
1205     if (va_osd_palette) {
1206         free(va_osd_palette);
1207         va_osd_palette = NULL;
1208     }
1209
1210     disable_eosd();
1211     disable_osd();
1212
1213     if (va_eosd_subpicture != VA_INVALID_ID) {
1214         vaDestroySubpicture(va_context->display, va_eosd_subpicture);
1215         va_eosd_subpicture = VA_INVALID_ID;
1216     }
1217
1218     if (va_eosd_image.image_id != VA_INVALID_ID) {
1219         vaDestroyImage(va_context->display, va_eosd_image.image_id);
1220         va_eosd_image.image_id = VA_INVALID_ID;
1221     }
1222
1223     destroy_osd();
1224
1225     if (va_surface_ids) {
1226         vaDestroySurfaces(va_context->display, va_surface_ids, va_num_surfaces);
1227         free(va_surface_ids);
1228         va_surface_ids = NULL;
1229         va_num_surfaces = 0;
1230     }
1231
1232     if (va_context && va_context->config_id) {
1233         vaDestroyConfig(va_context->display, va_context->config_id);
1234         va_context->config_id = 0;
1235     }
1236
1237     if (va_entrypoints) {
1238         free(va_entrypoints);
1239         va_entrypoints = NULL;
1240     }
1241
1242 #if CONFIG_GL
1243     if (gl_pixmap) {
1244         x11_trap_errors();
1245         mpglXDestroyPixmap(mDisplay, gl_pixmap);
1246         XSync(mDisplay, False);
1247         x11_untrap_errors();
1248         gl_pixmap = None;
1249     }
1250
1251     if (g_image_pixmap) {
1252         XFreePixmap(mDisplay, g_image_pixmap);
1253         g_image_pixmap = None;
1254     }
1255
1256     if (gl_texture) {
1257         glDeleteTextures(1, &gl_texture);
1258         gl_texture = GL_NONE;
1259     }
1260 #endif
1261
1262 #if CONFIG_XRENDER
1263     if (xr_window_picture) {
1264         XRenderFreePicture(mDisplay, xr_window_picture);
1265         xr_window_picture = None;
1266     }
1267 #endif
1268
1269     g_is_visible = 0;
1270 }
1271
1272 static void uninit(void)
1273 {
1274     if (!vo_config_count)
1275         return;
1276
1277     free_video_specific();
1278
1279     if (va_profiles) {
1280         free(va_profiles);
1281         va_profiles = NULL;
1282     }
1283
1284     if (va_subpic_flags) {
1285         free(va_subpic_flags);
1286         va_subpic_flags = NULL;
1287     }
1288
1289     if (va_subpic_formats) {
1290         free(va_subpic_formats);
1291         va_subpic_formats = NULL;
1292     }
1293
1294     if (va_image_formats) {
1295         free(va_image_formats);
1296         va_image_formats = NULL;
1297     }
1298
1299     if (va_context && va_context->display) {
1300         vaTerminate(va_context->display);
1301         va_context->display = NULL;
1302     }
1303
1304     if (va_context) {
1305         free(va_context);
1306         va_context = NULL;
1307     }
1308
1309 #ifdef CONFIG_XF86VM
1310     vo_vm_close();
1311 #endif
1312 #if CONFIG_XRENDER
1313     if (xr_enabled)
1314         uninit_xrender();
1315 #endif
1316 #if CONFIG_GL
1317     if (gl_enabled)
1318         uninit_mpglcontext(&gl_context);
1319     else
1320 #endif
1321     vo_x11_uninit();
1322
1323     stats_exit();
1324 }
1325
1326 static int config_x11(uint32_t width, uint32_t height,
1327                       uint32_t display_width, uint32_t display_height,
1328                       uint32_t flags, char *title)
1329 {
1330     Colormap cmap;
1331     XVisualInfo visualInfo;
1332     XVisualInfo *vi;
1333     XSetWindowAttributes xswa;
1334     unsigned long xswa_mask;
1335     XWindowAttributes wattr;
1336     int depth;
1337
1338 #ifdef CONFIG_GUI
1339     if (use_gui)
1340         guiGetEvent(guiSetShVideo, 0);  // the GUI will set up / resize our window
1341     else
1342 #endif
1343     {
1344 #ifdef CONFIG_XF86VM
1345         if (flags & VOFLAG_MODESWITCHING)
1346             vo_vm_switch();
1347 #endif
1348         XGetWindowAttributes(mDisplay, DefaultRootWindow(mDisplay), &wattr);
1349         depth = wattr.depth;
1350         if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
1351             depth = 24;
1352         XMatchVisualInfo(mDisplay, mScreen, depth, TrueColor, &visualInfo);
1353
1354 #if CONFIG_GL
1355         if (gl_enabled) {
1356             vi = glXChooseVisual(mDisplay, mScreen, gl_visual_attr);
1357             if (!vi)
1358                 return -1;
1359             cmap = XCreateColormap(mDisplay, mRootWin, vi->visual, AllocNone);
1360             if (cmap == None)
1361                 return -1;
1362         }
1363         else
1364 #endif
1365         {
1366             vi = &visualInfo;
1367             XMatchVisualInfo(mDisplay, mScreen, depth, TrueColor, vi);
1368             cmap = CopyFromParent;
1369         }
1370
1371         vo_x11_create_vo_window(vi,
1372                                 vo_dx, vo_dy, display_width, display_height,
1373                                 flags, cmap, "vaapi", title);
1374
1375         if (vi != &visualInfo)
1376             XFree(vi);
1377
1378         xswa_mask             = CWBorderPixel | CWBackPixel;
1379         xswa.border_pixel     = 0;
1380         xswa.background_pixel = 0;
1381         XChangeWindowAttributes(mDisplay, vo_window, xswa_mask, &xswa);
1382
1383 #ifdef CONFIG_XF86VM
1384         if (flags & VOFLAG_MODESWITCHING) {
1385             /* Grab the mouse pointer in our window */
1386             if (vo_grabpointer)
1387                 XGrabPointer(mDisplay, vo_window, True, 0,
1388                              GrabModeAsync, GrabModeAsync,
1389                              vo_window, None, CurrentTime);
1390             XSetInputFocus(mDisplay, vo_window, RevertToNone, CurrentTime);
1391         }
1392 #endif
1393     }
1394     return 0;
1395 }
1396
1397 #if CONFIG_GL
1398 static GLXFBConfig *get_fbconfig_for_depth(int depth)
1399 {
1400     GLXFBConfig *fbconfigs, *ret = NULL;
1401     int          n_elements, i, found;
1402     int          db, stencil, alpha, mipmap, rgba, value;
1403
1404     static GLXFBConfig *cached_config = NULL;
1405     static int          have_cached_config = 0;
1406
1407     if (have_cached_config)
1408         return cached_config;
1409
1410     fbconfigs = glXGetFBConfigs(mDisplay, mScreen, &n_elements);
1411
1412     db      = SHRT_MAX;
1413     stencil = SHRT_MAX;
1414     mipmap  = 0;
1415     rgba    = 0;
1416
1417     found = n_elements;
1418
1419     for (i = 0; i < n_elements; i++) {
1420         XVisualInfo *vi;
1421         int          visual_depth;
1422
1423         vi = glXGetVisualFromFBConfig(mDisplay, fbconfigs[i]);
1424         if (!vi)
1425             continue;
1426
1427         visual_depth = vi->depth;
1428         XFree(vi);
1429
1430         if (visual_depth != depth)
1431             continue;
1432
1433         glXGetFBConfigAttrib(mDisplay, fbconfigs[i], GLX_ALPHA_SIZE, &alpha);
1434         glXGetFBConfigAttrib(mDisplay, fbconfigs[i], GLX_BUFFER_SIZE, &value);
1435         if (value != depth && (value - alpha) != depth)
1436             continue;
1437
1438         value = 0;
1439         if (depth == 32) {
1440             glXGetFBConfigAttrib(mDisplay, fbconfigs[i],
1441                                  GLX_BIND_TO_TEXTURE_RGBA_EXT, &value);
1442             if (value)
1443                 rgba = 1;
1444         }
1445
1446         if (!value) {
1447             if (rgba)
1448                 continue;
1449
1450             glXGetFBConfigAttrib(mDisplay, fbconfigs[i],
1451                                  GLX_BIND_TO_TEXTURE_RGB_EXT, &value);
1452             if (!value)
1453                 continue;
1454         }
1455
1456         glXGetFBConfigAttrib(mDisplay, fbconfigs[i], GLX_DOUBLEBUFFER, &value);
1457         if (value > db)
1458             continue;
1459         db = value;
1460
1461         glXGetFBConfigAttrib(mDisplay, fbconfigs[i], GLX_STENCIL_SIZE, &value);
1462         if (value > stencil)
1463             continue;
1464         stencil = value;
1465
1466         found = i;
1467     }
1468
1469     if (found != n_elements) {
1470         ret = malloc(sizeof(*ret));
1471         *ret = fbconfigs[found];
1472     }
1473
1474     if (n_elements)
1475         XFree(fbconfigs);
1476
1477     have_cached_config = 1;
1478     cached_config = ret;
1479     return ret;
1480 }
1481
1482 static int config_tfp(unsigned int width, unsigned int height)
1483 {
1484     GLXFBConfig *fbconfig;
1485     int attribs[7], i = 0;
1486     const int depth = 24;
1487
1488     if (!mpglXBindTexImage || !mpglXReleaseTexImage) {
1489         mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] No GLX texture-from-pixmap extension available\n");
1490         return -1;
1491     }
1492
1493     if (depth != 24 && depth != 32)
1494         return -1;
1495
1496     g_image_pixmap = XCreatePixmap(mDisplay, vo_window, width, height, depth);
1497     if (!g_image_pixmap) {
1498         mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] Could not create X11 pixmap\n");
1499         return -1;
1500     }
1501
1502     fbconfig = get_fbconfig_for_depth(depth);
1503     if (!fbconfig) {
1504         mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] Could not find an FBConfig for 32-bit pixmap\n");
1505         return -1;
1506     }
1507
1508     attribs[i++] = GLX_TEXTURE_TARGET_EXT;
1509     attribs[i++] = GLX_TEXTURE_2D_EXT;
1510     attribs[i++] = GLX_TEXTURE_FORMAT_EXT;
1511     if (depth == 24)
1512         attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT;
1513     else if (depth == 32)
1514         attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT;
1515     attribs[i++] = GLX_MIPMAP_TEXTURE_EXT;
1516     attribs[i++] = GL_FALSE;
1517     attribs[i++] = None;
1518
1519     x11_trap_errors();
1520     gl_pixmap = mpglXCreatePixmap(mDisplay, *fbconfig, g_image_pixmap, attribs);
1521     XSync(mDisplay, False);
1522     if (x11_untrap_errors()) {
1523         mp_msg(MSGT_VO, MSGL_ERR, "[vo_vaapi] Could not create GLX pixmap\n");
1524         return -1;
1525     }
1526     return 0;
1527 }
1528
1529 static int config_glx(unsigned int width, unsigned int height)
1530 {
1531     if (gl_context.setGlWindow(&gl_context) == SET_WINDOW_FAILED)
1532         return -1;
1533
1534     glDisable(GL_DEPTH_TEST);
1535     glDepthMask(GL_FALSE);
1536     glDisable(GL_CULL_FACE);
1537     glEnable(GL_TEXTURE_2D);
1538     glDrawBuffer(vo_doublebuffering ? GL_BACK : GL_FRONT);
1539     glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
1540     glEnable(GL_BLEND);
1541     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1542
1543     /* Create TFP resources */
1544     if (gl_use_tfp && config_tfp(width, height) == 0)
1545         mp_msg(MSGT_VO, MSGL_INFO, "[vo_vaapi] Using GLX texture-from-pixmap extension\n");
1546     else
1547         gl_use_tfp = 0;
1548
1549     /* Create OpenGL texture */
1550     /* XXX: assume GL_ARB_texture_non_power_of_two is available */
1551     glEnable(GL_TEXTURE_2D);
1552     glGenTextures(1, &gl_texture);
1553     mpglBindTexture(GL_TEXTURE_2D, gl_texture);
1554     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1555     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1556     if (!gl_use_tfp) {
1557         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
1558         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
1559         glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
1560         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
1561                      GL_BGRA, GL_UNSIGNED_BYTE, NULL);
1562     }
1563     mpglBindTexture(GL_TEXTURE_2D, 0);
1564     glDisable(GL_TEXTURE_2D);
1565
1566     glClearColor(0.0, 0.0, 0.0, 1.0);
1567     glClear(GL_COLOR_BUFFER_BIT);
1568
1569     if (gl_build_font() < 0)
1570         return -1;
1571     return 0;
1572 }
1573 #endif
1574
1575 #if CONFIG_XRENDER
1576 static XRenderPictFormat *get_xrender_argb32_format(void)
1577 {
1578     static XRenderPictFormat *pictformat = NULL;
1579     XRenderPictFormat templ;
1580
1581     const unsigned long mask =
1582         PictFormatType      |
1583         PictFormatDepth     |
1584         PictFormatRed       |
1585         PictFormatRedMask   |
1586         PictFormatGreen     |
1587         PictFormatGreenMask |
1588         PictFormatBlue      |
1589         PictFormatBlueMask  |
1590         PictFormatAlphaMask;
1591
1592     if (pictformat)
1593         return pictformat;
1594
1595     /* First, look for a 32-bit format which ignores the alpha component */
1596     templ.depth            = 32;
1597     templ.type             = PictTypeDirect;
1598     templ.direct.red       = 16;
1599     templ.direct.green     = 8;
1600     templ.direct.blue      = 0;
1601     templ.direct.redMask   = 0xff;
1602     templ.direct.greenMask = 0xff;
1603     templ.direct.blueMask  = 0xff;
1604     templ.direct.alphaMask = 0;
1605
1606     pictformat = XRenderFindFormat(mDisplay, mask, &templ, 0);
1607
1608     if (!pictformat) {
1609         /* Not all X servers support xRGB32 formats. However, the
1610          * XRENDER spec says that they must support an ARGB32 format,
1611          * so we can always return that.
1612          */
1613         pictformat = XRenderFindStandardFormat(mDisplay, PictStandardARGB32);
1614         if (!pictformat)
1615             mp_msg(MSGT_VO, MSGL_ERR, "XRENDER ARGB32 format not supported\n");
1616     }
1617     return pictformat;
1618 }
1619
1620 static int create_xrender_specific(void)
1621 {
1622     XRenderPictFormat *pictformat;
1623
1624     if (g_output_rect.width == 0 && g_output_rect.height == 0)
1625         return 0;
1626
1627     g_image_pixmap = XCreatePixmap(mDisplay, vo_window, g_output_rect.width,
1628                                    g_output_rect.height, 32);
1629     if (!g_image_pixmap) {
1630         mp_msg(MSGT_VO, MSGL_ERR, "Could not create video pixmap\n");
1631         return -1;
1632     }
1633
1634     pictformat = get_xrender_argb32_format();
1635     if (!pictformat)
1636         return -1;
1637     xr_video_picture = XRenderCreatePicture(mDisplay, g_image_pixmap,
1638                                             pictformat, 0, NULL);
1639     if (!xr_video_picture) {
1640         mp_msg(MSGT_VO, MSGL_ERR, "Could not create XRENDER backing picture for Pixmap\n");
1641         return -1;
1642     }
1643     return 0;
1644 }
1645
1646 static void free_xrender_specific(void)
1647 {
1648     if (xr_video_picture) {
1649         XRenderFreePicture(mDisplay, xr_video_picture);
1650         xr_video_picture = None;
1651     }
1652
1653     if (g_image_pixmap) {
1654         XFreePixmap(mDisplay, g_image_pixmap);
1655         g_image_pixmap = None;
1656     }
1657 }
1658
1659 static int reset_xrender_specific(void)
1660 {
1661     free_xrender_specific();
1662     return create_xrender_specific();
1663 }
1664
1665 /* XXX: create a Pixmap as large as the display rect */
1666 static int config_xrender(unsigned int width, unsigned int height)
1667 {
1668     XWindowAttributes wattr;
1669     XRenderPictFormat *pictformat;
1670
1671     XGetWindowAttributes(mDisplay, vo_window, &wattr);
1672     pictformat = XRenderFindVisualFormat(mDisplay, wattr.visual);
1673     if (!pictformat) {
1674         mp_msg(MSGT_VO, MSGL_ERR, "XRENDER does not support Window visual\n");
1675         return -1;
1676     }
1677
1678     xr_window_picture = XRenderCreatePicture(mDisplay, vo_window, pictformat,
1679                                              0, NULL);
1680     if (!xr_window_picture) {
1681         mp_msg(MSGT_VO, MSGL_ERR, "Could not create XRENDER backing picture for Window\n");
1682         return -1;
1683     }
1684     return reset_xrender_specific();
1685 }
1686 #endif
1687
1688 static int config_vaapi(uint32_t width, uint32_t height, uint32_t format)
1689 {
1690     VAConfigAttrib attrib;
1691     VAStatus status;
1692     int i, j, profile, entrypoint, max_entrypoints, num_surfaces;
1693
1694     /* Create video surfaces */
1695     if (!IMGFMT_IS_VAAPI(format))
1696         num_surfaces = MAX_OUTPUT_SURFACES;
1697     else {
1698         switch (IMGFMT_VAAPI_CODEC(format)) {
1699         case IMGFMT_VAAPI_CODEC_MPEG2:
1700             num_surfaces = NUM_VIDEO_SURFACES_MPEG2;
1701             break;
1702         case IMGFMT_VAAPI_CODEC_MPEG4:
1703             num_surfaces = NUM_VIDEO_SURFACES_MPEG4;
1704             break;
1705         case IMGFMT_VAAPI_CODEC_H264:
1706             num_surfaces = NUM_VIDEO_SURFACES_H264;
1707             break;
1708         case IMGFMT_VAAPI_CODEC_VC1:
1709             num_surfaces = NUM_VIDEO_SURFACES_VC1;
1710             break;
1711         default:
1712             num_surfaces = 0;
1713             break;
1714         }
1715         if (num_surfaces == 0)
1716             return -1;
1717         if (!is_direct_mapping())
1718             num_surfaces = FFMIN(2 * num_surfaces, MAX_VIDEO_SURFACES);
1719     }
1720     for (i = 0; i < num_surfaces; i++) {
1721         struct vaapi_surface *surface;
1722         surface = alloc_vaapi_surface(width, height, VA_RT_FORMAT_YUV420);
1723         if (!surface)
1724             return -1;
1725     }
1726     assert(va_num_surfaces == num_surfaces);
1727
1728 #if CONFIG_VAAPI_GLX
1729     /* Create GLX surfaces */
1730     if (gl_enabled && !gl_use_tfp) {
1731         status = vaCreateSurfaceGLX(va_context->display,
1732                                     GL_TEXTURE_2D, gl_texture,
1733                                     &gl_surface);
1734         if (!check_status(status, "vaCreateSurfaceGLX()"))
1735             return -1;
1736     }
1737 #endif
1738
1739     /* Create OSD data */
1740     va_osd_draw_alpha     = NULL;
1741     va_osd_image.image_id = VA_INVALID_ID;
1742     va_osd_image.buf      = VA_INVALID_ID;
1743     va_osd_subpicture     = VA_INVALID_ID;
1744     ensure_osd();
1745
1746     /* Create EOSD data */
1747     va_eosd_draw_alpha     = NULL;
1748     va_eosd_image.image_id = VA_INVALID_ID;
1749     va_eosd_image.buf      = VA_INVALID_ID;
1750     va_eosd_subpicture     = VA_INVALID_ID;
1751     for (i = 0; va_eosd_info[i].format; i++) {
1752         for (j = 0; j < va_num_subpic_formats; j++)
1753             if (va_subpic_formats[j].fourcc == va_eosd_info[i].format)
1754                 break;
1755         if (j < va_num_subpic_formats &&
1756             vaCreateImage(va_context->display, &va_subpic_formats[j],
1757                           width, height, &va_eosd_image) == VA_STATUS_SUCCESS)
1758             break;
1759     }
1760     if (va_eosd_info[i].format &&
1761         vaCreateSubpicture(va_context->display, va_eosd_image.image_id,
1762                            &va_eosd_subpicture) == VA_STATUS_SUCCESS) {
1763         va_eosd_draw_alpha = va_eosd_info[i].draw_alpha;
1764         mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] Using %s surface for EOSD\n",
1765                string_of_VAImageFormat(&va_eosd_image.format));
1766     }
1767
1768     /* Allocate VA images */
1769     if (!IMGFMT_IS_VAAPI(format)) {
1770         VAImageFormat *image_format = VAImageFormat_from_imgfmt(format);
1771         if (!image_format)
1772             return -1;
1773         for (i = 0; i < va_num_surfaces; i++) {
1774             struct vaapi_surface * const s = va_free_surfaces[i];
1775             s->is_bound = 0;
1776             status = vaDeriveImage(va_context->display, s->id, &s->image);
1777             if (status == VA_STATUS_SUCCESS) {
1778                 /* vaDeriveImage() is supported, check format */
1779                 if (s->image.format.fourcc != image_format->fourcc) {
1780                     vaDestroyImage(va_context->display, s->image.image_id);
1781                     return -1;
1782                 }
1783                 if (s->image.width == width && s->image.height == height) {
1784                     s->is_bound = 1;
1785                     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] Using vaDeriveImage()\n");
1786                 }
1787                 else {
1788                     vaDestroyImage(va_context->display, s->image.image_id);
1789                     status = VA_STATUS_ERROR_OPERATION_FAILED;
1790                 }
1791                 
1792             }
1793             if (status != VA_STATUS_SUCCESS) {
1794                 status = vaCreateImage(va_context->display, image_format,
1795                                        width, height, &s->image);
1796                 if (!check_status(status, "vaCreateImage()"))
1797                     return -1;
1798             }
1799         }
1800         return 0;
1801     }
1802
1803     /* Check profile */
1804     profile = VAProfile_from_imgfmt(format);
1805     if (profile < 0)
1806         return -1;
1807
1808     /* Check entry-point (only VLD for now) */
1809     max_entrypoints = vaMaxNumEntrypoints(va_context->display);
1810     va_entrypoints = calloc(max_entrypoints, sizeof(*va_entrypoints));
1811     if (!va_entrypoints)
1812         return -1;
1813
1814     status = vaQueryConfigEntrypoints(va_context->display, profile,
1815                                       va_entrypoints, &va_num_entrypoints);
1816     if (!check_status(status, "vaQueryConfigEntrypoints()"))
1817         return -1;
1818
1819     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] config_vaapi(%s): %d entrypoints available\n",
1820            string_of_VAProfile(profile), va_num_entrypoints);
1821     for (i = 0; i < va_num_entrypoints; i++)
1822         mp_msg(MSGT_VO, MSGL_DBG2, "  %s\n", string_of_VAEntrypoint(va_entrypoints[i]));
1823
1824     entrypoint = VAEntrypoint_from_imgfmt(format);
1825     if (entrypoint != VAEntrypointVLD)
1826         return -1;
1827
1828     /* Check chroma format (only 4:2:0 for now) */
1829     attrib.type = VAConfigAttribRTFormat;
1830     status = vaGetConfigAttributes(va_context->display, profile, entrypoint, &attrib, 1);
1831     if (!check_status(status, "vaGetConfigAttributes()"))
1832         return -1;
1833     if ((attrib.value & VA_RT_FORMAT_YUV420) == 0)
1834         return -1;
1835
1836     /* Create a configuration for the decode pipeline */
1837     status = vaCreateConfig(va_context->display, profile, entrypoint, &attrib, 1, &va_context->config_id);
1838     if (!check_status(status, "vaCreateConfig()"))
1839         return -1;
1840
1841     /* Create a context for the decode pipeline */
1842     status = vaCreateContext(va_context->display, va_context->config_id,
1843                              width, height, VA_PROGRESSIVE,
1844                              va_surface_ids, va_num_surfaces,
1845                              &va_context->context_id);
1846     if (!check_status(status, "vaCreateContext()"))
1847         return -1;
1848     return 0;
1849 }
1850
1851 static int config(uint32_t width, uint32_t height,
1852                   uint32_t display_width, uint32_t display_height,
1853                   uint32_t flags, char *title, uint32_t format)
1854 {
1855     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] config(): size %dx%d, display size %dx%d, flags %x, title '%s', format %x (%s)\n",
1856            width, height, display_width, display_height, flags, title, format, vo_format_name(format));
1857
1858     free_video_specific();
1859
1860     if (config_x11(width, height, display_width, display_height, flags, title) < 0)
1861         return -1;
1862
1863 #if CONFIG_GL
1864     if (gl_enabled && config_glx(width, height) < 0)
1865         return -1;
1866 #endif
1867
1868 #if CONFIG_XRENDER
1869     if (xr_enabled && config_xrender(width, height) < 0)
1870         return -1;
1871 #endif
1872
1873     if (config_vaapi(width, height, format) < 0)
1874         return -1;
1875
1876     g_is_visible   = 0;
1877     g_is_paused    = 0;
1878     g_image_width  = width;
1879     g_image_height = height;
1880     g_image_format = format;
1881     resize();
1882     return 0;
1883 }
1884
1885 static int query_format(uint32_t format)
1886 {
1887     const int default_caps = (VFCAP_CSP_SUPPORTED |
1888                               VFCAP_CSP_SUPPORTED_BY_HW |
1889                               VFCAP_HWSCALE_UP |
1890                               VFCAP_HWSCALE_DOWN |
1891                               VFCAP_OSD |
1892                               VFCAP_EOSD);
1893
1894     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] query_format(): format %x (%s)\n",
1895            format, vo_format_name(format));
1896
1897     switch (format) {
1898     case IMGFMT_VAAPI_MPEG2:
1899     case IMGFMT_VAAPI_MPEG4:
1900     case IMGFMT_VAAPI_H263:
1901     case IMGFMT_VAAPI_H264:
1902     case IMGFMT_VAAPI_WMV3:
1903     case IMGFMT_VAAPI_VC1:
1904         return default_caps | VOCAP_NOSLICES;
1905     case IMGFMT_NV12:
1906     case IMGFMT_YV12:
1907     case IMGFMT_I420:
1908     case IMGFMT_IYUV:
1909         if (VAImageFormat_from_imgfmt(format))
1910             return default_caps;
1911         break;
1912     }
1913     return 0;
1914 }
1915
1916 static inline int get_field_flags(int i)
1917 {
1918     return (g_deint && (g_image_fields & MP_IMGFIELD_INTERLACED) ? 
1919             (((!!(g_image_fields & MP_IMGFIELD_TOP_FIRST)) ^ i) == 0 ?
1920              VA_BOTTOM_FIELD : VA_TOP_FIELD) : VA_FRAME_PICTURE);
1921 }
1922
1923 static inline int get_colorspace_flags(void)
1924 {
1925     int csp = 0;
1926 #if USE_VAAPI_COLORSPACE
1927     switch (g_colorspace) {
1928     case 0:
1929         csp = ((g_image_width >= 1280 || g_image_height > 576) ?
1930                VA_SRC_BT709 : VA_SRC_BT601);
1931         break;
1932     case 1:
1933         csp = VA_SRC_BT601;
1934         break;
1935     case 2:
1936         csp = VA_SRC_BT709;
1937         break;
1938     default:
1939         assert(0);
1940         break;
1941     }
1942 #endif
1943     return csp;
1944 }
1945
1946 static void put_surface_x11(struct vaapi_surface *surface)
1947 {
1948     VAStatus status;
1949     int i;
1950
1951     for (i = 0; i <= !!(g_deint > 1); i++) {
1952         status = vaPutSurface(va_context->display,
1953                               surface->id,
1954                               vo_window,
1955                               0, 0, g_image_width, g_image_height,
1956                               g_output_rect.left,
1957                               g_output_rect.top,
1958                               g_output_rect.width,
1959                               g_output_rect.height,
1960                               NULL, 0,
1961                               get_field_flags(i) | get_colorspace_flags());
1962         if (!check_status(status, "vaPutSurface()"))
1963             return;
1964     }
1965 }
1966
1967 #if CONFIG_GL
1968 static void put_surface_glx(struct vaapi_surface *surface)
1969 {
1970     VAStatus status;
1971     int i;
1972
1973     if (gl_use_tfp) {
1974         for (i = 0; i <= !!(g_deint > 1); i++) {
1975             status = vaPutSurface(va_context->display,
1976                                   surface->id,
1977                                   g_image_pixmap,
1978                                   0, 0, g_image_width, g_image_height,
1979                                   0, 0, g_image_width, g_image_height,
1980                                   NULL, 0,
1981                                   get_field_flags(i) | get_colorspace_flags());
1982             if (!check_status(status, "vaPutSurface()"))
1983                 return;
1984         }
1985         g_output_surfaces[g_output_surface] = surface;
1986         return;
1987     }
1988
1989 #if CONFIG_VAAPI_GLX
1990     if (gl_binding) {
1991 #if USE_VAAPI_GLX_BIND
1992         for (i = 0; i <= !!(g_deint > 1); i++) {
1993             status = vaAssociateSurfaceGLX(va_context->display,
1994                                            gl_surface,
1995                                            surface->id,
1996                                            get_field_flags(i) | get_colorspace_flags());
1997             if (!check_status(status, "vaAssociateSurfaceGLX()"))
1998                 return;
1999         }
2000 #else
2001         mp_msg(MSGT_VO, MSGL_WARN, "vaAssociateSurfaceGLX() is not implemented\n");
2002         gl_binding = 0;
2003 #endif
2004     }
2005
2006     if (!gl_binding) {
2007         for (i = 0; i <= !!(g_deint > 1); i++) {
2008             status = vaCopySurfaceGLX(va_context->display,
2009                                       gl_surface,
2010                                       surface->id,
2011                                       get_field_flags(i) | get_colorspace_flags());
2012
2013             if (status == VA_STATUS_ERROR_UNIMPLEMENTED) {
2014                 mp_msg(MSGT_VO, MSGL_WARN,
2015                        "[vo_vaapi] vaCopySurfaceGLX() is not implemented\n");
2016                 gl_binding = 1;
2017             }
2018             else {
2019                 if (!check_status(status, "vaCopySurfaceGLX()"))
2020                     return;
2021             }
2022         }
2023     }
2024 #endif
2025     g_output_surfaces[g_output_surface] = surface;
2026 }
2027
2028 static int glx_bind_texture(void)
2029 {
2030     glEnable(GL_TEXTURE_2D);
2031     mpglBindTexture(GL_TEXTURE_2D, gl_texture);
2032
2033     if (gl_use_tfp) {
2034         x11_trap_errors();
2035         mpglXBindTexImage(mDisplay, gl_pixmap, GLX_FRONT_LEFT_EXT, NULL);
2036         XSync(mDisplay, False);
2037         if (x11_untrap_errors())
2038             mp_msg(MSGT_VO, MSGL_WARN, "[vo_vaapi] Update bind_tex_image failed\n");
2039     }
2040
2041 #if USE_VAAPI_GLX_BIND
2042     if (gl_binding) {
2043         VAStatus status;
2044         status = vaBeginRenderSurfaceGLX(va_context->display, gl_surface);
2045         if (!check_status(status, "vaBeginRenderSurfaceGLX()"))
2046             return -1;
2047     }
2048 #endif
2049     return 0;
2050 }
2051
2052 static int glx_unbind_texture(void)
2053 {
2054     if (gl_use_tfp) {
2055         x11_trap_errors();
2056         mpglXReleaseTexImage(mDisplay, gl_pixmap, GLX_FRONT_LEFT_EXT);
2057         if (x11_untrap_errors())
2058             mp_msg(MSGT_VO, MSGL_WARN, "[vo_vaapi] Failed to release?\n");
2059     }
2060
2061 #if USE_VAAPI_GLX_BIND
2062     if (gl_binding) {
2063         VAStatus status;
2064         status = vaEndRenderSurfaceGLX(va_context->display, gl_surface);
2065         if (!check_status(status, "vaEndRenderSurfaceGLX()"))
2066             return -1;
2067     }
2068 #endif
2069
2070     mpglBindTexture(GL_TEXTURE_2D, 0);
2071     glDisable(GL_TEXTURE_2D);
2072     return 0;
2073 }
2074
2075 static void render_background(void)
2076 {
2077     /* Original code from Mirco Muller (MacSlow):
2078        <http://cgit.freedesktop.org/~macslow/gl-gst-player/> */
2079     GLfloat fStartX = 0.0f;
2080     GLfloat fStartY = 0.0f;
2081     GLfloat fWidth  = (GLfloat)vo_dwidth;
2082     GLfloat fHeight = (GLfloat)vo_dheight;
2083
2084     glBegin(GL_QUADS);
2085     {
2086         /* top third, darker grey to white */
2087         glColor3f(0.85f, 0.85f, 0.85f);
2088         glVertex3f(fStartX, fStartY, 0.0f);
2089         glColor3f(0.85f, 0.85f, 0.85f);
2090         glVertex3f(fStartX + fWidth, fStartY, 0.0f);
2091         glColor3f(1.0f, 1.0f, 1.0f);
2092         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
2093         glColor3f(1.0f, 1.0f, 1.0f);
2094         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
2095
2096         /* middle third, just plain white */
2097         glColor3f(1.0f, 1.0f, 1.0f);
2098         glVertex3f(fStartX, fStartY + fHeight / 3.0f, 0.0f);
2099         glVertex3f(fStartX + fWidth, fStartY + fHeight / 3.0f, 0.0f);
2100         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
2101         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
2102
2103         /* bottom third, white to lighter grey */
2104         glColor3f(1.0f, 1.0f, 1.0f);
2105         glVertex3f(fStartX, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
2106         glColor3f(1.0f, 1.0f, 1.0f);
2107         glVertex3f(fStartX + fWidth, fStartY + 2.0f * fHeight / 3.0f, 0.0f);
2108         glColor3f(0.62f, 0.66f, 0.69f);
2109         glVertex3f(fStartX + fWidth, fStartY + fHeight, 0.0f);
2110         glColor3f(0.62f, 0.66f, 0.69f);
2111         glVertex3f(fStartX, fStartY + fHeight, 0.0f);
2112     }
2113     glEnd();
2114 }
2115
2116 static void render_frame(void)
2117 {
2118     struct vo_rect * const r = &g_output_rect;
2119
2120     if (glx_bind_texture() < 0)
2121         return;
2122     glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
2123     glBegin(GL_QUADS);
2124     {
2125         glTexCoord2f(0.0f, 0.0f); glVertex2i(r->left, r->top);
2126         glTexCoord2f(0.0f, 1.0f); glVertex2i(r->left, r->bottom);
2127         glTexCoord2f(1.0f, 1.0f); glVertex2i(r->right, r->bottom);
2128         glTexCoord2f(1.0f, 0.0f); glVertex2i(r->right, r->top);
2129     }
2130     glEnd();
2131     if (glx_unbind_texture() < 0)
2132         return;
2133 }
2134
2135 static void render_reflection(void)
2136 {
2137     struct vo_rect * const r = &g_output_rect;
2138     const unsigned int rh  = g_output_rect.height / 5;
2139     GLfloat ry = 1.0f - (GLfloat)rh / (GLfloat)r->height;
2140
2141     if (glx_bind_texture() < 0)
2142         return;
2143     glBegin(GL_QUADS);
2144     {
2145         glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
2146         glTexCoord2f(0.0f, 1.0f); glVertex2i(r->left, r->top);
2147         glTexCoord2f(1.0f, 1.0f); glVertex2i(r->right, r->top);
2148
2149         glColor4f(1.0f, 1.0f, 1.0f, 0.0f);
2150         glTexCoord2f(1.0f, ry); glVertex2i(r->right, r->top + rh);
2151         glTexCoord2f(0.0f, ry); glVertex2i(r->left, r->top + rh);
2152     }
2153     glEnd();
2154     if (glx_unbind_texture() < 0)
2155         return;
2156 }
2157
2158 static void flip_page_glx(void)
2159 {
2160     glClear(GL_COLOR_BUFFER_BIT);
2161
2162     if (gl_reflect) {
2163         render_background();
2164
2165         glPushMatrix();
2166         glRotatef(20.0f, 0.0f, 1.0f, 0.0f);
2167         glTranslatef(50.0f, 0.0f, 0.0f);
2168     }
2169
2170     render_frame();
2171
2172     if (gl_reflect) {
2173         glPushMatrix();
2174         glTranslatef(0.0, (GLfloat)g_output_rect.height + 5.0f, 0.0f);
2175         render_reflection();
2176         glPopMatrix();
2177         glPopMatrix();
2178     }
2179
2180     if (cpu_stats) {
2181         gl_draw_rectangle(0, 0, vo_dwidth, 32, 0x000000ff);
2182         glColor3f(1.0f, 1.0f, 1.0f);
2183         glRasterPos2i(16, 20);
2184         gl_printf("MPlayer: %.1f%% of CPU @ %u MHz", cpu_usage, cpu_frequency);
2185     }
2186
2187     if (gl_finish)
2188         mpglFinish();
2189     gl_context.swapGlBuffers(&gl_context);
2190
2191     if (vo_fs) /* avoid flickering borders in fullscreen mode */
2192         glClear(GL_COLOR_BUFFER_BIT);
2193 }
2194 #endif
2195
2196 #if CONFIG_XRENDER
2197 static void put_surface_xrender(struct vaapi_surface *surface)
2198 {
2199     VAStatus status;
2200     int i;
2201
2202     for (i = 0; i <= !!(g_deint > 1); i++) {
2203         status = vaPutSurface(va_context->display,
2204                               surface->id,
2205                               g_image_pixmap,
2206                               0, 0, g_image_width, g_image_height,
2207                               0, 0, g_output_rect.width, g_output_rect.height,
2208                               NULL, 0,
2209                               get_field_flags(i) | get_colorspace_flags());
2210         if (!check_status(status, "vaPutSurface()"))
2211             return;
2212         XRenderComposite(mDisplay,
2213                          PictOpSrc, xr_video_picture, 0, xr_window_picture,
2214                          0, 0,
2215                          0, 0,
2216                          g_output_rect.left, g_output_rect.top,
2217                          g_output_rect.width, g_output_rect.height);
2218     }
2219 }
2220 #endif
2221
2222 static void put_surface(struct vaapi_surface *surface)
2223 {
2224     if (!surface || surface->id == VA_INVALID_SURFACE)
2225         return;
2226
2227 #if CONFIG_GL
2228     if (gl_enabled)
2229         put_surface_glx(surface);
2230     else
2231 #endif
2232 #if CONFIG_XRENDER
2233     if (xr_enabled)
2234         put_surface_xrender(surface);
2235     else
2236 #endif
2237         put_surface_x11(surface);
2238 }
2239
2240 static int draw_slice(uint8_t * image[], int stride[],
2241                       int w, int h, int x, int y)
2242 {
2243     struct vaapi_surface * const surface = va_free_surfaces[g_output_surface];
2244     VAImage * const va_image = &surface->image;
2245     VAStatus status;
2246     uint8_t *image_data = NULL;
2247     uint8_t *dst[3] = { 0, };
2248     unsigned int dst_stride[3];
2249
2250     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] draw_slice(): location (%d,%d), size %dx%d\n", x, y, w, h);
2251
2252     status = vaMapBuffer(va_context->display, va_image->buf,
2253                          (void *)&image_data);
2254     if (!check_status(status, "vaMapBuffer()"))
2255         return VO_FALSE;
2256
2257     dst_stride[0] = va_image->pitches[0];
2258     dst[0] = image_data + va_image->offsets[0] + y * dst_stride[0] + x;
2259
2260     memcpy_pic(dst[0], image[0], w, h, dst_stride[0], stride[0]);
2261
2262     x /= 2;
2263     y /= 2;
2264     w /= 2;
2265     h /= 2;
2266
2267     if (g_image_format == IMGFMT_YV12) {
2268         /* MPlayer's YV12 is actually I420, so swap U/V components */
2269         dst_stride[1] = va_image->pitches[2];
2270         dst[1] = image_data + va_image->offsets[2] + y * dst_stride[1] + x;
2271         dst_stride[2] = va_image->pitches[1];
2272         dst[2] = image_data + va_image->offsets[1] + y * dst_stride[2] + x;
2273     }
2274     else {
2275         if (image[1]) {
2276             dst_stride[1] = va_image->pitches[1];
2277             dst[1] = image_data + va_image->offsets[1] + y * dst_stride[1] + x;
2278         }
2279         if (image[2]) {
2280             dst_stride[2] = va_image->pitches[2];
2281             dst[2] = image_data + va_image->offsets[2] + y * dst_stride[2] + x;
2282         }
2283     }
2284
2285     if (image[1]) /* RGBA only has a single plane */
2286         memcpy_pic(dst[1], image[1], w, h, dst_stride[1], stride[1]);
2287
2288     if (image[2]) /* NV12 only has two planes */
2289         memcpy_pic(dst[2], image[2], w, h, dst_stride[2], stride[2]);
2290
2291     status = vaUnmapBuffer(va_context->display, surface->image.buf);
2292     if (!check_status(status, "vaUnmapBuffer()"))
2293         return VO_FALSE;
2294
2295     return VO_TRUE;
2296 }
2297
2298 static int draw_frame(uint8_t * src[])
2299 {
2300     mp_msg(MSGT_VO,MSGL_INFO, MSGTR_LIBVO_X11_DrawFrameCalled);
2301
2302     return -1;
2303 }
2304
2305 static void draw_osd(void)
2306 {
2307     VAStatus status;
2308     const int osd_width  = va_osd_image.width;
2309     const int osd_height = va_osd_image.height;
2310
2311     ensure_osd();
2312     if (va_osd_image.image_id == VA_INVALID_ID)
2313         return;
2314
2315     if (!va_osd_draw_alpha)
2316         return;
2317
2318     if (!vo_update_osd(osd_width, osd_height))
2319         return;
2320  
2321     if (!vo_osd_check_range_update(0, 0, osd_width, osd_height)) {
2322         disable_osd();
2323         return;
2324     }
2325
2326     status = vaMapBuffer(va_context->display, va_osd_image.buf,
2327                          (void *)&va_osd_image_data);
2328     if (!check_status(status, "vaMapBuffer()"))
2329         return;
2330
2331     memset(va_osd_image_data, 0, va_osd_image.data_size);
2332
2333     vo_draw_text(osd_width, osd_height, va_osd_draw_alpha);
2334
2335     status = vaUnmapBuffer(va_context->display, va_osd_image.buf);
2336     if (!check_status(status, "vaUnmapBuffer()"))
2337         return;
2338     va_osd_image_data = NULL;
2339
2340     enable_osd();
2341 }
2342
2343 static void draw_eosd(EOSD_ImageList *imgs)
2344 {
2345     ASS_Image *img = imgs->imgs;
2346     ASS_Image *i;
2347     VAStatus status;
2348
2349     if (!va_eosd_draw_alpha)
2350         return;
2351
2352     // Nothing changed, no need to redraw
2353     if (imgs->changed == 0)
2354         return;
2355
2356     // There's nothing to render!
2357     if (!img) {
2358         disable_eosd();
2359         return;
2360     }
2361
2362     if (imgs->changed == 1)
2363         goto eosd_skip_upload;
2364
2365     status = vaMapBuffer(va_context->display, va_eosd_image.buf,
2366                          (void *)&va_eosd_image_data);
2367     if (!check_status(status, "vaMapBuffer()"))
2368         return;
2369
2370     memset(va_eosd_image_data, 0, va_eosd_image.data_size);
2371
2372     for (i = img; i; i = i->next)
2373         va_eosd_draw_alpha(i->bitmap, i->w, i->h, i->stride,
2374                            i->dst_x, i->dst_y, i->color);
2375
2376     status = vaUnmapBuffer(va_context->display, va_eosd_image.buf);
2377     if (!check_status(status, "vaUnmapBuffer()"))
2378         return;
2379     va_eosd_image_data = NULL;
2380
2381 eosd_skip_upload:
2382     enable_eosd();
2383 }
2384
2385 static void flip_page(void)
2386 {
2387     struct vaapi_surface *surface;
2388
2389     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] flip_page()\n");
2390
2391     surface = g_output_surfaces[g_output_surface];
2392     if (!surface)
2393         return;
2394
2395     put_surface(surface);
2396     g_output_surface = (g_output_surface + 1) % MAX_OUTPUT_SURFACES;
2397     g_is_visible     = 1;
2398
2399 #if CONFIG_GL
2400     if (gl_enabled)
2401         flip_page_glx();
2402 #endif
2403 }
2404
2405 static struct vaapi_surface *get_surface(mp_image_t *mpi)
2406 {
2407     struct vaapi_surface *surface;
2408
2409     if (mpi->type == MP_IMGTYPE_NUMBERED && is_direct_mapping()) {
2410         assert(mpi->number < va_num_surfaces);
2411         surface = va_free_surfaces[mpi->number];
2412         return surface;
2413     }
2414
2415     /* Push current surface to a free slot */
2416     if (mpi->priv) {
2417         assert(!va_free_surfaces[va_free_surfaces_tail_index]);
2418         va_free_surfaces[va_free_surfaces_tail_index] = mpi->priv;
2419         va_free_surfaces_tail_index = (va_free_surfaces_tail_index + 1) % va_num_surfaces;
2420     }
2421
2422     /* Pop the least recently used free surface */
2423     assert(va_free_surfaces[va_free_surfaces_head_index]);
2424     surface = va_free_surfaces[va_free_surfaces_head_index];
2425     va_free_surfaces[va_free_surfaces_head_index] = NULL;
2426     va_free_surfaces_head_index = (va_free_surfaces_head_index + 1) % va_num_surfaces;
2427     return surface;
2428 }
2429
2430 static uint32_t get_image(mp_image_t *mpi)
2431 {
2432     struct vaapi_surface *surface;
2433
2434     if (mpi->type != MP_IMGTYPE_NUMBERED)
2435         return VO_FALSE;
2436
2437     if (!IMGFMT_IS_VAAPI(g_image_format))
2438         return VO_FALSE;
2439
2440     surface = get_surface(mpi);
2441     if (!surface)
2442         return VO_FALSE;
2443
2444     mpi->flags |= MP_IMGFLAG_DIRECT;
2445     mpi->stride[0] = mpi->stride[1] = mpi->stride[2] = mpi->stride[3] = 0;
2446     mpi->planes[0] = mpi->planes[1] = mpi->planes[2] = mpi->planes[3] = NULL;
2447     mpi->planes[0] = (char *)surface;
2448     mpi->planes[3] = (char *)(uintptr_t)surface->id;
2449     mpi->num_planes = 1;
2450     mpi->priv = surface;
2451
2452     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] get_image(): surface 0x%08x\n", surface->id);
2453
2454     return VO_TRUE;
2455 }
2456
2457 static int put_image(mp_image_t *mpi, struct vaapi_surface *surface)
2458 {
2459     VAStatus status;
2460  
2461     if ((mpi->flags & (MP_IMGFLAG_PLANAR|MP_IMGFLAG_YUV)) != (MP_IMGFLAG_PLANAR|MP_IMGFLAG_YUV))
2462         return VO_FALSE;
2463
2464     if (!(mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)) {
2465         if (!draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0))
2466             return VO_FALSE;
2467     }
2468
2469     if (!surface->is_bound) {
2470         status = vaPutImage2(va_context->display,
2471                              surface->id,
2472                              surface->image.image_id,
2473                              mpi->x, mpi->y, mpi->w, mpi->h,
2474                              mpi->x, mpi->y, mpi->w, mpi->h);
2475         if (!check_status(status, "vaPutImage()"))
2476             return VO_FALSE;
2477     }
2478
2479     return VO_TRUE;
2480 }
2481
2482 static uint32_t draw_image(mp_image_t *mpi)
2483 {
2484     struct vaapi_surface *surface = (struct vaapi_surface *)mpi->priv;
2485
2486     g_image_fields = mpi->fields;
2487
2488     if (!IMGFMT_IS_VAAPI(mpi->imgfmt)) {
2489         /* XXX: no direct rendering in non-accelerated mode */
2490         surface = va_free_surfaces[g_output_surface];
2491         if (!put_image(mpi, surface))
2492             return VO_FALSE;
2493     }
2494
2495     mp_msg(MSGT_VO, MSGL_DBG2, "[vo_vaapi] draw_image(): surface 0x%08x\n", surface->id);
2496
2497     g_output_surfaces[g_output_surface] = surface;
2498
2499     if (cpu_stats) {
2500         static uint64_t ticks;
2501         if ((ticks++ % 30) == 0) {
2502             cpu_frequency = get_cpu_frequency();
2503             cpu_usage = get_cpu_usage(CPU_USAGE_QUANTUM);
2504         }
2505     }
2506     return VO_TRUE;
2507 }
2508
2509 static void check_events(void)
2510 {
2511     int events = vo_x11_check_events(mDisplay);
2512
2513     if (events & VO_EVENT_RESIZE)
2514         resize();
2515
2516     if ((events & (VO_EVENT_EXPOSE|VO_EVENT_RESIZE)) && g_is_paused) {
2517         /* Redraw the last visible buffer  */
2518         if (g_is_visible) {
2519             struct vaapi_surface *surface = g_output_surfaces[g_output_surface];
2520             if (surface)
2521                 put_surface(surface);
2522         }
2523     }
2524 }
2525
2526 static VADisplayAttribute *get_display_attribute(const char *name)
2527 {
2528     VADisplayAttribute *attr;
2529     if (!strcasecmp(name, "brightness"))
2530         attr = &va_equalizer.brightness;
2531     else if (!strcasecmp(name, "contrast"))
2532         attr = &va_equalizer.contrast;
2533     else if (!strcasecmp(name, "saturation"))
2534         attr = &va_equalizer.saturation;
2535     else if (!strcasecmp(name, "hue"))
2536         attr = &va_equalizer.hue;
2537     else
2538         attr = NULL;
2539     return attr;
2540 }
2541
2542 static int get_equalizer(const char *name, int *value)
2543 {
2544     VADisplayAttribute * const attr = get_display_attribute(name);
2545     int r;
2546
2547     if (!attr || !(attr->flags & VA_DISPLAY_ATTRIB_GETTABLE))
2548         return VO_NOTIMPL;
2549
2550     /* normalize to -100 .. 100 range */
2551     r = attr->max_value - attr->min_value;
2552     if (r == 0)
2553         return VO_NOTIMPL;
2554     *value = ((attr->value - attr->min_value) * 200) / r - 100;
2555     return VO_TRUE;
2556 }
2557
2558 static int set_equalizer(const char *name, int value)
2559 {
2560     VADisplayAttribute * const attr = get_display_attribute(name);
2561     VAStatus status;
2562     int r;
2563
2564     if (!attr || !(attr->flags & VA_DISPLAY_ATTRIB_SETTABLE))
2565         return VO_NOTIMPL;
2566
2567     /* normalize to attribute value range */
2568     r = attr->max_value - attr->min_value;
2569     if (r == 0)
2570         return VO_NOTIMPL;
2571     attr->value = ((value + 100) * r) / 200 + attr->min_value;
2572
2573     status = vaSetDisplayAttributes(va_context->display, attr, 1);
2574     if (!check_status(status, "vaSetDisplayAttributes()"))
2575         return VO_FALSE;
2576     return VO_TRUE;
2577 }
2578
2579 static int control(uint32_t request, void *data, ...)
2580 {
2581     switch (request) {
2582     case VOCTRL_GET_DEINTERLACE:
2583         *(int*)data = g_deint;
2584         return VO_TRUE;
2585     case VOCTRL_SET_DEINTERLACE:
2586         g_deint = *(int*)data;
2587         if (g_deint)
2588             g_deint = g_deint_type;
2589         return VO_TRUE;
2590     case VOCTRL_PAUSE:
2591         return (g_is_paused = 1);
2592     case VOCTRL_RESUME:
2593         return (g_is_paused = 0);
2594     case VOCTRL_QUERY_FORMAT:
2595         return query_format(*((uint32_t *)data));
2596     case VOCTRL_GET_IMAGE:
2597         return get_image(data);
2598     case VOCTRL_DRAW_IMAGE:
2599         return draw_image(data);
2600     case VOCTRL_GUISUPPORT:
2601         return VO_TRUE;
2602     case VOCTRL_BORDER:
2603         vo_x11_border();
2604         resize();
2605         return VO_TRUE;
2606     case VOCTRL_FULLSCREEN:
2607         vo_x11_fullscreen();
2608         resize();
2609         return VO_TRUE;
2610     case VOCTRL_SET_EQUALIZER: {
2611         va_list ap;
2612         int value;
2613
2614         va_start(ap, data);
2615         value = va_arg(ap, int);
2616
2617         va_end(ap);
2618         return set_equalizer(data, value);
2619     }
2620     case VOCTRL_GET_EQUALIZER: {
2621         va_list ap;
2622         int *value;
2623
2624         va_start(ap, data);
2625         value = va_arg(ap, int *);
2626
2627         va_end(ap);
2628         return get_equalizer(data, value);
2629     }
2630     case VOCTRL_ONTOP:
2631         vo_x11_ontop();
2632         return VO_TRUE;
2633     case VOCTRL_UPDATE_SCREENINFO:
2634         update_xinerama_info();
2635         return VO_TRUE;
2636     case VOCTRL_GET_PANSCAN:
2637         return VO_TRUE;
2638     case VOCTRL_SET_PANSCAN:
2639         resize();
2640         return VO_TRUE;
2641     case VOCTRL_GET_HWACCEL_CONTEXT:
2642         *((void **)data) = va_context;
2643         return VO_TRUE;
2644     case VOCTRL_DRAW_EOSD:
2645         if (!data)
2646             return VO_FALSE;
2647         draw_eosd(data);
2648         return VO_TRUE;
2649     case VOCTRL_GET_EOSD_RES: {
2650         struct mp_eosd_settings *r = data;
2651         r->mt = r->mb = r->ml = r->mr = 0;
2652         r->srcw = g_image_width;
2653         r->srch = g_image_height;
2654         r->w    = g_image_width;
2655         r->h    = g_image_height;
2656         return VO_TRUE;
2657     }
2658     }
2659     return VO_NOTIMPL;
2660 }