cleanup: Silence compilation warnings on MinGW-w64
[mplayer.git] / libvo / vo_xv.c
blob4180ac0fd07e564d8aa3d66e31a920b3007f114a
1 /*
2 * X11 Xv interface
4 * This file is part of MPlayer.
6 * MPlayer is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * MPlayer is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 // Number of buffers _FOR_DOUBLEBUFFERING_MODE_
22 // Use option -double to enable double buffering! (default: single buffer)
23 #define NUM_BUFFERS 3
26 Buffer allocation:
28 -nodr:
29 1: TEMP
30 2: 2*TEMP
32 -dr:
33 1: TEMP
34 3: 2*STATIC+TEMP
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <stdint.h>
41 #include <stdbool.h>
43 #include "config.h"
44 #include "options.h"
45 #include "talloc.h"
46 #include "mp_msg.h"
47 #include "video_out.h"
48 #include "libmpcodecs/vfcap.h"
49 #include "libmpcodecs/mp_image.h"
50 #include "osd.h"
52 #include <X11/Xlib.h>
53 #include <X11/Xutil.h>
54 #include <errno.h>
56 #include "x11_common.h"
58 #include "fastmemcpy.h"
59 #include "sub/sub.h"
60 #include "aspect.h"
61 #include "csputils.h"
63 #include "subopt-helper.h"
65 #include "libavutil/common.h"
67 static const vo_info_t info = {
68 "X11/Xv",
69 "xv",
70 "Gerd Knorr <kraxel@goldbach.in-berlin.de> and others",
74 #ifdef HAVE_SHM
75 #include <sys/ipc.h>
76 #include <sys/shm.h>
77 #include <X11/extensions/XShm.h>
78 #endif
80 // Note: depends on the inclusion of X11/extensions/XShm.h
81 #include <X11/extensions/Xv.h>
82 #include <X11/extensions/Xvlib.h>
84 struct xvctx {
85 XvAdaptorInfo *ai;
86 XvImageFormatValues *fo;
87 unsigned int formats, adaptors, xv_format;
88 int current_buf;
89 int current_ip_buf;
90 int num_buffers;
91 int total_buffers;
92 bool have_image_copy;
93 bool unchanged_image;
94 int visible_buf;
95 XvImage *xvimage[NUM_BUFFERS + 1];
96 uint32_t image_width;
97 uint32_t image_height;
98 uint32_t image_format;
99 uint32_t image_d_width;
100 uint32_t image_d_height;
101 int is_paused;
102 struct vo_rect src_rect;
103 struct vo_rect dst_rect;
104 uint32_t max_width, max_height; // zero means: not set
105 int mode_switched;
106 int osd_objects_drawn;
107 void (*draw_alpha_fnc)(void *ctx, int x0, int y0, int w, int h,
108 unsigned char *src, unsigned char *srca,
109 int stride);
110 #ifdef HAVE_SHM
111 XShmSegmentInfo Shminfo[NUM_BUFFERS + 1];
112 int Shmem_Flag;
113 #endif
116 static void allocate_xvimage(struct vo *, int);
119 static void fixup_osd_position(struct vo *vo, int *x0, int *y0, int *w, int *h)
121 struct xvctx *ctx = vo->priv;
122 *x0 += ctx->image_width * (vo->panscan_x >> 1)
123 / (vo->dwidth + vo->panscan_x);
124 *w = av_clip(*w, 0, ctx->image_width);
125 *h = av_clip(*h, 0, ctx->image_height);
126 *x0 = FFMIN(*x0, ctx->image_width - *w);
127 *y0 = FFMIN(*y0, ctx->image_height - *h);
130 static void draw_alpha_yv12(void *p, int x0, int y0, int w, int h,
131 unsigned char *src, unsigned char *srca,
132 int stride)
134 struct vo *vo = p;
135 struct xvctx *ctx = vo->priv;
136 fixup_osd_position(vo, &x0, &y0, &w, &h);
137 vo_draw_alpha_yv12(w, h, src, srca, stride,
138 ctx->xvimage[ctx->current_buf]->data +
139 ctx->xvimage[ctx->current_buf]->offsets[0] +
140 ctx->xvimage[ctx->current_buf]->pitches[0] * y0 + x0,
141 ctx->xvimage[ctx->current_buf]->pitches[0]);
142 ctx->osd_objects_drawn++;
145 static void draw_alpha_yuy2(void *p, int x0, int y0, int w, int h,
146 unsigned char *src, unsigned char *srca,
147 int stride)
149 struct vo *vo = p;
150 struct xvctx *ctx = vo->priv;
151 fixup_osd_position(vo, &x0, &y0, &w, &h);
152 vo_draw_alpha_yuy2(w, h, src, srca, stride,
153 ctx->xvimage[ctx->current_buf]->data +
154 ctx->xvimage[ctx->current_buf]->offsets[0] +
155 ctx->xvimage[ctx->current_buf]->pitches[0] * y0 + 2 * x0,
156 ctx->xvimage[ctx->current_buf]->pitches[0]);
157 ctx->osd_objects_drawn++;
160 static void draw_alpha_uyvy(void *p, int x0, int y0, int w, int h,
161 unsigned char *src, unsigned char *srca,
162 int stride)
164 struct vo *vo = p;
165 struct xvctx *ctx = vo->priv;
166 fixup_osd_position(vo, &x0, &y0, &w, &h);
167 vo_draw_alpha_yuy2(w, h, src, srca, stride,
168 ctx->xvimage[ctx->current_buf]->data +
169 ctx->xvimage[ctx->current_buf]->offsets[0] +
170 ctx->xvimage[ctx->current_buf]->pitches[0] * y0 + 2 * x0 + 1,
171 ctx->xvimage[ctx->current_buf]->pitches[0]);
172 ctx->osd_objects_drawn++;
175 static void draw_alpha_null(void *p, int x0, int y0, int w, int h,
176 unsigned char *src, unsigned char *srca,
177 int stride)
182 static void deallocate_xvimage(struct vo *vo, int foo);
184 static void resize(struct vo *vo)
186 struct xvctx *ctx = vo->priv;
188 calc_src_dst_rects(vo, ctx->image_width, ctx->image_height, &ctx->src_rect,
189 &ctx->dst_rect, NULL, NULL);
190 struct vo_rect *dst = &ctx->dst_rect;
191 vo_x11_clearwindow_part(vo, vo->x11->window, dst->width, dst->height);
192 vo_xv_draw_colorkey(vo, dst->left, dst->top, dst->width, dst->height);
196 * connect to server, create and map window,
197 * allocate colors and (shared) memory
199 static int config(struct vo *vo, uint32_t width, uint32_t height,
200 uint32_t d_width, uint32_t d_height, uint32_t flags,
201 uint32_t format)
203 struct vo_x11_state *x11 = vo->x11;
204 XVisualInfo vinfo;
205 XSetWindowAttributes xswa;
206 XWindowAttributes attribs;
207 unsigned long xswamask;
208 int depth;
209 struct xvctx *ctx = vo->priv;
210 int i;
212 ctx->image_height = height;
213 ctx->image_width = width;
214 ctx->image_format = format;
215 ctx->image_d_width = d_width;
216 ctx->image_d_height = d_height;
218 if ((ctx->max_width != 0 && ctx->max_height != 0)
219 && (ctx->image_width > ctx->max_width
220 || ctx->image_height > ctx->max_height)) {
221 mp_tmsg(MSGT_VO, MSGL_ERR, "Source image dimensions are too high: %ux%u (maximum is %ux%u)\n",
222 ctx->image_width, ctx->image_height, ctx->max_width,
223 ctx->max_height);
224 return -1;
227 ctx->visible_buf = -1;
228 ctx->have_image_copy = false;
230 /* check image formats */
231 ctx->xv_format = 0;
232 for (i = 0; i < ctx->formats; i++) {
233 mp_msg(MSGT_VO, MSGL_V, "Xvideo image format: 0x%x (%4.4s) %s\n",
234 ctx->fo[i].id, (char *) &ctx->fo[i].id,
235 (ctx->fo[i].format == XvPacked) ? "packed" : "planar");
236 if (ctx->fo[i].id == format)
237 ctx->xv_format = ctx->fo[i].id;
239 if (!ctx->xv_format)
240 return -1;
243 #ifdef CONFIG_XF86VM
244 int vm = flags & VOFLAG_MODESWITCHING;
245 if (vm) {
246 vo_vm_switch(vo);
247 ctx->mode_switched = 1;
249 #endif
250 XGetWindowAttributes(x11->display, DefaultRootWindow(x11->display),
251 &attribs);
252 depth = attribs.depth;
253 if (depth != 15 && depth != 16 && depth != 24 && depth != 32)
254 depth = 24;
255 XMatchVisualInfo(x11->display, x11->screen, depth, TrueColor, &vinfo);
257 xswa.border_pixel = 0;
258 xswamask = CWBorderPixel;
259 if (x11->xv_ck_info.method == CK_METHOD_BACKGROUND) {
260 xswa.background_pixel = x11->xv_colorkey;
261 xswamask |= CWBackPixel;
264 vo_x11_create_vo_window(vo, &vinfo, vo->dx, vo->dy, vo->dwidth,
265 vo->dheight, flags, CopyFromParent, "xv");
266 XChangeWindowAttributes(x11->display, x11->window, xswamask, &xswa);
268 #ifdef CONFIG_XF86VM
269 if (vm) {
270 /* Grab the mouse pointer in our window */
271 if (vo_grabpointer)
272 XGrabPointer(x11->display, x11->window, True, 0, GrabModeAsync,
273 GrabModeAsync, x11->window, None, CurrentTime);
274 XSetInputFocus(x11->display, x11->window, RevertToNone,
275 CurrentTime);
277 #endif
280 mp_msg(MSGT_VO, MSGL_V, "using Xvideo port %d for hw scaling\n",
281 x11->xv_port);
283 switch (ctx->xv_format) {
284 case IMGFMT_YV12:
285 case IMGFMT_I420:
286 case IMGFMT_IYUV:
287 ctx->draw_alpha_fnc = draw_alpha_yv12;
288 break;
289 case IMGFMT_YUY2:
290 case IMGFMT_YVYU:
291 ctx->draw_alpha_fnc = draw_alpha_yuy2;
292 break;
293 case IMGFMT_UYVY:
294 ctx->draw_alpha_fnc = draw_alpha_uyvy;
295 break;
296 default:
297 ctx->draw_alpha_fnc = draw_alpha_null;
300 // In case config has been called before
301 for (i = 0; i < ctx->total_buffers; i++)
302 deallocate_xvimage(vo, i);
304 ctx->num_buffers =
305 vo_doublebuffering ? (vo_directrendering ? NUM_BUFFERS : 2) : 1;
306 ctx->total_buffers = ctx->num_buffers + 1;
308 for (i = 0; i < ctx->total_buffers; i++)
309 allocate_xvimage(vo, i);
311 ctx->current_buf = 0;
312 ctx->current_ip_buf = 0;
315 resize(vo);
317 return 0;
320 static void allocate_xvimage(struct vo *vo, int foo)
322 struct xvctx *ctx = vo->priv;
323 struct vo_x11_state *x11 = vo->x11;
325 * allocate XvImages. FIXME: no error checking, without
326 * mit-shm this will bomb... trzing to fix ::atmos
328 #ifdef HAVE_SHM
329 if (x11->display_is_local && XShmQueryExtension(x11->display))
330 ctx->Shmem_Flag = 1;
331 else {
332 ctx->Shmem_Flag = 0;
333 mp_tmsg(MSGT_VO, MSGL_INFO, "[VO_XV] Shared memory not supported\nReverting to normal Xv.\n");
335 if (ctx->Shmem_Flag) {
336 ctx->xvimage[foo] =
337 (XvImage *) XvShmCreateImage(x11->display, x11->xv_port,
338 ctx->xv_format, NULL,
339 ctx->image_width, ctx->image_height,
340 &ctx->Shminfo[foo]);
342 ctx->Shminfo[foo].shmid = shmget(IPC_PRIVATE,
343 ctx->xvimage[foo]->data_size,
344 IPC_CREAT | 0777);
345 ctx->Shminfo[foo].shmaddr = (char *) shmat(ctx->Shminfo[foo].shmid, 0,
347 ctx->Shminfo[foo].readOnly = False;
349 ctx->xvimage[foo]->data = ctx->Shminfo[foo].shmaddr;
350 XShmAttach(x11->display, &ctx->Shminfo[foo]);
351 XSync(x11->display, False);
352 shmctl(ctx->Shminfo[foo].shmid, IPC_RMID, 0);
353 } else
354 #endif
356 ctx->xvimage[foo] =
357 (XvImage *) XvCreateImage(x11->display, x11->xv_port,
358 ctx->xv_format, NULL, ctx->image_width,
359 ctx->image_height);
360 ctx->xvimage[foo]->data = malloc(ctx->xvimage[foo]->data_size);
361 XSync(x11->display, False);
363 memset(ctx->xvimage[foo]->data, 128, ctx->xvimage[foo]->data_size);
364 return;
367 static void deallocate_xvimage(struct vo *vo, int foo)
369 struct xvctx *ctx = vo->priv;
370 #ifdef HAVE_SHM
371 if (ctx->Shmem_Flag) {
372 XShmDetach(vo->x11->display, &ctx->Shminfo[foo]);
373 shmdt(ctx->Shminfo[foo].shmaddr);
374 } else
375 #endif
377 free(ctx->xvimage[foo]->data);
379 XFree(ctx->xvimage[foo]);
381 XSync(vo->x11->display, False);
382 return;
385 static inline void put_xvimage(struct vo *vo, XvImage *xvi)
387 struct xvctx *ctx = vo->priv;
388 struct vo_x11_state *x11 = vo->x11;
389 struct vo_rect *src = &ctx->src_rect;
390 struct vo_rect *dst = &ctx->dst_rect;
391 #ifdef HAVE_SHM
392 if (ctx->Shmem_Flag) {
393 XvShmPutImage(x11->display, x11->xv_port, x11->window, x11->vo_gc, xvi,
394 src->left, src->top, src->width, src->height,
395 dst->left, dst->top, dst->width, dst->height,
396 False);
397 } else
398 #endif
400 XvPutImage(x11->display, x11->xv_port, x11->window, x11->vo_gc, xvi,
401 src->left, src->top, src->width, src->height,
402 dst->left, dst->top, dst->width, dst->height);
406 // Only copies luma for planar formats as draw_alpha doesn't change others */
407 static void copy_backup_image(struct vo *vo, int dest, int src)
409 struct xvctx *ctx = vo->priv;
411 XvImage *vb = ctx->xvimage[dest];
412 XvImage *cp = ctx->xvimage[src];
413 memcpy_pic(vb->data + vb->offsets[0], cp->data + cp->offsets[0],
414 vb->width, vb->height,
415 vb->pitches[0], cp->pitches[0]);
418 static void check_events(struct vo *vo)
420 int e = vo_x11_check_events(vo);
422 if (e & VO_EVENT_EXPOSE || e & VO_EVENT_RESIZE) {
423 resize(vo);
424 vo->want_redraw = true;
428 static void draw_osd(struct vo *vo, struct osd_state *osd)
430 struct xvctx *ctx = vo->priv;
432 ctx->osd_objects_drawn = 0;
433 osd_draw_text(osd,
434 ctx->image_width -
435 ctx->image_width * vo->panscan_x / (vo->dwidth +
436 vo->panscan_x),
437 ctx->image_height, ctx->draw_alpha_fnc, vo);
438 if (ctx->osd_objects_drawn)
439 ctx->unchanged_image = false;
442 static int redraw_frame(struct vo *vo)
444 struct xvctx *ctx = vo->priv;
446 if (ctx->have_image_copy)
447 copy_backup_image(vo, ctx->visible_buf, ctx->num_buffers);
448 else if (ctx->unchanged_image) {
449 copy_backup_image(vo, ctx->num_buffers, ctx->visible_buf);
450 ctx->have_image_copy = true;
451 } else
452 return false;
453 ctx->current_buf = ctx->visible_buf;
454 return true;
457 static void flip_page(struct vo *vo)
459 struct xvctx *ctx = vo->priv;
460 put_xvimage(vo, ctx->xvimage[ctx->current_buf]);
462 /* remember the currently visible buffer */
463 ctx->visible_buf = ctx->current_buf;
465 if (ctx->num_buffers > 1) {
466 ctx->current_buf = vo_directrendering ? 0 : ((ctx->current_buf + 1) %
467 ctx->num_buffers);
468 XFlush(vo->x11->display);
469 } else
470 XSync(vo->x11->display, False);
471 return;
474 static int draw_slice(struct vo *vo, uint8_t *image[], int stride[], int w,
475 int h, int x, int y)
477 struct xvctx *ctx = vo->priv;
478 uint8_t *dst;
479 XvImage *current_image = ctx->xvimage[ctx->current_buf];
481 dst = current_image->data + current_image->offsets[0]
482 + current_image->pitches[0] * y + x;
483 memcpy_pic(dst, image[0], w, h, current_image->pitches[0], stride[0]);
485 x /= 2;
486 y /= 2;
487 w /= 2;
488 h /= 2;
490 dst = current_image->data + current_image->offsets[1]
491 + current_image->pitches[1] * y + x;
492 if (ctx->image_format != IMGFMT_YV12)
493 memcpy_pic(dst, image[1], w, h, current_image->pitches[1], stride[1]);
494 else
495 memcpy_pic(dst, image[2], w, h, current_image->pitches[1], stride[2]);
497 dst = current_image->data + current_image->offsets[2]
498 + current_image->pitches[2] * y + x;
499 if (ctx->image_format == IMGFMT_YV12)
500 memcpy_pic(dst, image[1], w, h, current_image->pitches[1], stride[1]);
501 else
502 memcpy_pic(dst, image[2], w, h, current_image->pitches[1], stride[2]);
504 return 0;
507 static mp_image_t *get_screenshot(struct vo *vo)
509 struct xvctx *ctx = vo->priv;
511 // try to get an image without OSD
512 if (ctx->have_image_copy)
513 copy_backup_image(vo, ctx->visible_buf, ctx->num_buffers);
515 XvImage *xv_image = ctx->xvimage[ctx->visible_buf];
517 int w = xv_image->width;
518 int h = xv_image->height;
520 mp_image_t *image = alloc_mpi(w, h, ctx->image_format);
522 int bytes = 1;
523 if (!(image->flags & MP_IMGFLAG_PLANAR) && (image->flags & MP_IMGFLAG_YUV))
524 // packed YUV
525 bytes = image->bpp / 8;
527 memcpy_pic(image->planes[0], xv_image->data + xv_image->offsets[0],
528 bytes * w, h, image->stride[0], xv_image->pitches[0]);
530 if (image->flags & MP_IMGFLAG_PLANAR) {
531 int swap = ctx->image_format == IMGFMT_YV12;
532 int p1 = swap ? 2 : 1;
533 int p2 = swap ? 1 : 2;
535 w /= 2;
536 h /= 2;
538 memcpy_pic(image->planes[p1], xv_image->data + xv_image->offsets[1],
539 w, h, image->stride[p1], xv_image->pitches[1]);
540 memcpy_pic(image->planes[p2], xv_image->data + xv_image->offsets[2],
541 w, h, image->stride[p2], xv_image->pitches[2]);
544 image->w = ctx->image_d_width;
545 image->h = ctx->image_d_height;
547 return image;
550 static uint32_t draw_image(struct vo *vo, mp_image_t *mpi)
552 struct xvctx *ctx = vo->priv;
554 ctx->have_image_copy = false;
556 if (mpi->flags & MP_IMGFLAG_DIRECT)
557 // direct rendering:
558 ctx->current_buf = (size_t)(mpi->priv); // hack!
559 else if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK)
560 ; // done
561 else if (mpi->flags & MP_IMGFLAG_PLANAR)
562 draw_slice(vo, mpi->planes, mpi->stride, mpi->w, mpi->h, 0, 0);
563 else if (mpi->flags & MP_IMGFLAG_YUV)
564 // packed YUV:
565 memcpy_pic(ctx->xvimage[ctx->current_buf]->data +
566 ctx->xvimage[ctx->current_buf]->offsets[0], mpi->planes[0],
567 mpi->w * (mpi->bpp / 8), mpi->h,
568 ctx->xvimage[ctx->current_buf]->pitches[0], mpi->stride[0]);
569 else
570 return false;
572 if (ctx->is_paused) {
573 copy_backup_image(vo, ctx->num_buffers, ctx->current_buf);
574 ctx->have_image_copy = true;
576 ctx->unchanged_image = true;
577 return true;
580 static uint32_t get_image(struct xvctx *ctx, mp_image_t *mpi)
582 // we shouldn't change current_buf unless we do DR!
583 int buf = ctx->current_buf;
585 if (mpi->type == MP_IMGTYPE_STATIC && ctx->num_buffers > 1)
586 return VO_FALSE; // it is not static
587 if (mpi->imgfmt != ctx->image_format)
588 return VO_FALSE; // needs conversion :(
589 if (mpi->flags & MP_IMGFLAG_READABLE
590 && (mpi->type == MP_IMGTYPE_IPB || mpi->type == MP_IMGTYPE_IP)) {
591 // reference (I/P) frame of IP or IPB:
592 if (ctx->num_buffers < 2)
593 return VO_FALSE; // not enough
594 ctx->current_ip_buf ^= 1;
595 // for IPB with 2 buffers we can DR only one of the 2 P frames:
596 if (mpi->type == MP_IMGTYPE_IPB && ctx->num_buffers < 3
597 && ctx->current_ip_buf)
598 return VO_FALSE;
599 buf = ctx->current_ip_buf;
600 if (mpi->type == MP_IMGTYPE_IPB)
601 ++buf; // preserve space for B
603 if (mpi->height > ctx->xvimage[buf]->height)
604 return VO_FALSE; //buffer to small
605 if (mpi->width * (mpi->bpp / 8) > ctx->xvimage[buf]->pitches[0])
606 return VO_FALSE; //buffer to small
607 if ((mpi->flags & (MP_IMGFLAG_ACCEPT_STRIDE | MP_IMGFLAG_ACCEPT_WIDTH))
608 || (mpi->width * (mpi->bpp / 8) == ctx->xvimage[buf]->pitches[0])) {
609 ctx->current_buf = buf;
610 XvImage *current_image = ctx->xvimage[ctx->current_buf];
611 mpi->planes[0] = current_image->data + current_image->offsets[0];
612 mpi->stride[0] = current_image->pitches[0];
613 mpi->width = mpi->stride[0] / (mpi->bpp / 8);
614 if (mpi->flags & MP_IMGFLAG_PLANAR) {
615 if (mpi->flags & MP_IMGFLAG_SWAPPED) {
616 // I420
617 mpi->planes[1] = current_image->data
618 + current_image->offsets[1];
619 mpi->planes[2] = current_image->data
620 + current_image->offsets[2];
621 mpi->stride[1] = current_image->pitches[1];
622 mpi->stride[2] = current_image->pitches[2];
623 } else {
624 // YV12
625 mpi->planes[1] = current_image->data
626 + current_image->offsets[2];
627 mpi->planes[2] = current_image->data
628 + current_image->offsets[1];
629 mpi->stride[1] = current_image->pitches[2];
630 mpi->stride[2] = current_image->pitches[1];
633 mpi->flags |= MP_IMGFLAG_DIRECT;
634 mpi->priv = (void *)(size_t)ctx->current_buf;
635 return VO_TRUE;
637 return VO_FALSE;
640 static int query_format(struct xvctx *ctx, uint32_t format)
642 uint32_t i;
643 int flag = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_OSD | VFCAP_ACCEPT_STRIDE; // FIXME! check for DOWN
645 /* check image formats */
646 for (i = 0; i < ctx->formats; i++) {
647 if (ctx->fo[i].id == format)
648 return flag; //xv_format = fo[i].id;
650 return 0;
653 static void uninit(struct vo *vo)
655 struct xvctx *ctx = vo->priv;
656 int i;
658 ctx->visible_buf = -1;
659 if (ctx->ai)
660 XvFreeAdaptorInfo(ctx->ai);
661 ctx->ai = NULL;
662 if (ctx->fo) {
663 XFree(ctx->fo);
664 ctx->fo = NULL;
666 for (i = 0; i < ctx->total_buffers; i++)
667 deallocate_xvimage(vo, i);
668 #ifdef CONFIG_XF86VM
669 if (ctx->mode_switched)
670 vo_vm_close(vo);
671 #endif
672 // uninit() shouldn't get called unless initialization went past vo_init()
673 vo_x11_uninit(vo);
676 static int preinit(struct vo *vo, const char *arg)
678 XvPortID xv_p;
679 int busy_ports = 0;
680 unsigned int i;
681 strarg_t ck_src_arg = { 0, NULL };
682 strarg_t ck_method_arg = { 0, NULL };
683 struct xvctx *ctx = talloc_zero(vo, struct xvctx);
684 vo->priv = ctx;
685 struct vo_x11_state *x11 = vo->x11;
686 int xv_adaptor = -1;
688 const opt_t subopts[] =
690 /* name arg type arg var test */
691 { "port", OPT_ARG_INT, &x11->xv_port, int_pos },
692 { "adaptor", OPT_ARG_INT, &xv_adaptor, int_non_neg },
693 { "ck", OPT_ARG_STR, &ck_src_arg, xv_test_ck },
694 { "ck-method", OPT_ARG_STR, &ck_method_arg, xv_test_ckm },
695 { NULL }
698 x11->xv_port = 0;
700 /* parse suboptions */
701 if (subopt_parse(arg, subopts) != 0) {
702 return -1;
705 /* modify colorkey settings according to the given options */
706 xv_setup_colorkeyhandling(vo, ck_method_arg.str, ck_src_arg.str);
708 if (!vo_init(vo))
709 return -1;
711 /* check for Xvideo extension */
712 unsigned int ver, rel, req, ev, err;
713 if (Success != XvQueryExtension(x11->display, &ver, &rel, &req, &ev, &err)) {
714 mp_tmsg(MSGT_VO, MSGL_ERR, "[VO_XV] Sorry, Xv not supported by this X11 version/driver\n[VO_XV] ******** Try with -vo x11 or -vo sdl *********\n");
715 goto error;
718 /* check for Xvideo support */
719 if (Success !=
720 XvQueryAdaptors(x11->display, DefaultRootWindow(x11->display),
721 &ctx->adaptors, &ctx->ai)) {
722 mp_tmsg(MSGT_VO, MSGL_ERR, "[VO_XV] XvQueryAdaptors failed.\n");
723 goto error;
726 /* check adaptors */
727 if (x11->xv_port) {
728 int port_found;
730 for (port_found = 0, i = 0; !port_found && i < ctx->adaptors; i++) {
731 if ((ctx->ai[i].type & XvInputMask)
732 && (ctx->ai[i].type & XvImageMask)) {
733 for (xv_p = ctx->ai[i].base_id;
734 xv_p < ctx->ai[i].base_id + ctx->ai[i].num_ports;
735 ++xv_p) {
736 if (xv_p == x11->xv_port) {
737 port_found = 1;
738 break;
743 if (port_found) {
744 if (XvGrabPort(x11->display, x11->xv_port, CurrentTime))
745 x11->xv_port = 0;
746 } else {
747 mp_tmsg(MSGT_VO, MSGL_WARN, "[VO_XV] Invalid port parameter, overriding with port 0.\n");
748 x11->xv_port = 0;
752 for (i = 0; i < ctx->adaptors && x11->xv_port == 0; i++) {
753 /* check if adaptor number has been specified */
754 if (xv_adaptor != -1 && xv_adaptor != i)
755 continue;
757 if ((ctx->ai[i].type & XvInputMask) && (ctx->ai[i].type & XvImageMask)) {
758 for (xv_p = ctx->ai[i].base_id;
759 xv_p < ctx->ai[i].base_id + ctx->ai[i].num_ports; ++xv_p)
760 if (!XvGrabPort(x11->display, xv_p, CurrentTime)) {
761 x11->xv_port = xv_p;
762 mp_msg(MSGT_VO, MSGL_V,
763 "[VO_XV] Using Xv Adapter #%d (%s)\n",
764 i, ctx->ai[i].name);
765 break;
766 } else {
767 mp_tmsg(MSGT_VO, MSGL_WARN, "[VO_XV] Could not grab port %i.\n",
768 (int) xv_p);
769 ++busy_ports;
773 if (!x11->xv_port) {
774 if (busy_ports)
775 mp_tmsg(MSGT_VO, MSGL_ERR,
776 "[VO_XV] Could not find free Xvideo port - maybe another process is already\n"\
777 "[VO_XV] using it. Close all video applications, and try again. If that does\n"\
778 "[VO_XV] not help, see 'mplayer -vo help' for other (non-xv) video out drivers.\n");
779 else
780 mp_tmsg(MSGT_VO, MSGL_ERR,
781 "[VO_XV] It seems there is no Xvideo support for your video card available.\n"\
782 "[VO_XV] Run 'xvinfo' to verify its Xv support and read\n"\
783 "[VO_XV] DOCS/HTML/en/video.html#xv!\n"\
784 "[VO_XV] See 'mplayer -vo help' for other (non-xv) video out drivers.\n"\
785 "[VO_XV] Try -vo x11.\n");
786 goto error;
789 if (!vo_xv_init_colorkey(vo)) {
790 goto error; // bail out, colorkey setup failed
792 vo_xv_enable_vsync(vo);
793 vo_xv_get_max_img_dim(vo, &ctx->max_width, &ctx->max_height);
795 ctx->fo = XvListImageFormats(x11->display, x11->xv_port,
796 (int *) &ctx->formats);
798 return 0;
800 error:
801 uninit(vo); // free resources
802 return -1;
805 static int control(struct vo *vo, uint32_t request, void *data)
807 struct xvctx *ctx = vo->priv;
808 struct vo_x11_state *x11 = vo->x11;
809 switch (request) {
810 case VOCTRL_PAUSE:
811 return (ctx->is_paused = 1);
812 case VOCTRL_RESUME:
813 return (ctx->is_paused = 0);
814 case VOCTRL_QUERY_FORMAT:
815 return query_format(ctx, *((uint32_t *) data));
816 case VOCTRL_GET_IMAGE:
817 return get_image(ctx, data);
818 case VOCTRL_DRAW_IMAGE:
819 return draw_image(vo, data);
820 case VOCTRL_GET_PANSCAN:
821 return VO_TRUE;
822 case VOCTRL_FULLSCREEN:
823 vo_x11_fullscreen(vo);
824 /* indended, fallthrough to update panscan on fullscreen/windowed switch */
825 case VOCTRL_SET_PANSCAN:
826 resize(vo);
827 return VO_TRUE;
828 case VOCTRL_SET_EQUALIZER: {
829 vo->want_redraw = true;
830 struct voctrl_set_equalizer_args *args = data;
831 return vo_xv_set_eq(vo, x11->xv_port, args->name, args->value);
833 case VOCTRL_GET_EQUALIZER: {
834 struct voctrl_get_equalizer_args *args = data;
835 return vo_xv_get_eq(vo, x11->xv_port, args->name, args->valueptr);
837 case VOCTRL_SET_YUV_COLORSPACE:;
838 struct mp_csp_details* given_cspc = data;
839 int is_709 = given_cspc->format == MP_CSP_BT_709;
840 vo_xv_set_eq(vo, x11->xv_port, "bt_709", is_709 * 200 - 100);
841 vo->want_redraw = true;
842 return true;
843 case VOCTRL_GET_YUV_COLORSPACE:;
844 struct mp_csp_details* cspc = data;
845 *cspc = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
846 int bt709_enabled;
847 if (vo_xv_get_eq(vo, x11->xv_port, "bt_709", &bt709_enabled))
848 cspc->format = bt709_enabled == 100 ? MP_CSP_BT_709 : MP_CSP_BT_601;
849 return true;
850 case VOCTRL_ONTOP:
851 vo_x11_ontop(vo);
852 return VO_TRUE;
853 case VOCTRL_UPDATE_SCREENINFO:
854 update_xinerama_info(vo);
855 return VO_TRUE;
856 case VOCTRL_REDRAW_FRAME:
857 return redraw_frame(vo);
858 case VOCTRL_SCREENSHOT: {
859 struct voctrl_screenshot_args *args = data;
860 args->out_image = get_screenshot(vo);
861 return true;
864 return VO_NOTIMPL;
867 const struct vo_driver video_out_xv = {
868 .is_new = 1,
869 .info = &info,
870 .preinit = preinit,
871 .config = config,
872 .control = control,
873 .draw_slice = draw_slice,
874 .draw_osd = draw_osd,
875 .flip_page = flip_page,
876 .check_events = check_events,
877 .uninit = uninit