x11grab.c 20.6 KB
Newer Older
1 2
/*
 * X11 video grab interface
3
 *
4
 * This file is part of Libav.
5
 *
6
 * Libav integration:
7 8
 * Copyright (C) 2006 Clemens Fruhwirth <clemens@endorphin.org>
 *                    Edouard Gomez <ed.gomez@free.fr>
9
 *
10
 * This file contains code from grab.c:
11
 * Copyright (c) 2000-2001 Fabrice Bellard
12 13
 *
 * This file contains code from the xvidcap project:
14 15
 * Copyright (C) 1997-1998 Rasca, Berlin
 *               2003-2004 Karl H. Beckers, Frankfurt
16
 *
17
 * Libav is free software; you can redistribute it and/or modify
18 19 20 21
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
22
 * Libav is distributed in the hope that it will be useful,
23 24 25 26
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
27
 * You should have received a copy of the GNU General Public License
28
 * along with Libav; if not, write to the Free Software
29
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30
 */
31

Edouard Gomez's avatar
Edouard Gomez committed
32
/**
33
 * @file
34 35 36
 * X11 frame device demuxer
 * @author Clemens Fruhwirth <clemens@endorphin.org>
 * @author Edouard Gomez <ed.gomez@free.fr>
Edouard Gomez's avatar
Edouard Gomez committed
37 38
 */

39
#include "config.h"
40
#include "libavformat/avformat.h"
41
#include "libavformat/internal.h"
42 43 44
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
45 46 47 48 49
#include <time.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/Xproto.h>
50
#include <X11/Xutil.h>
51
#include <sys/shm.h>
52
#include <X11/extensions/shape.h>
53
#include <X11/extensions/XShm.h>
Roxis's avatar
Roxis committed
54
#include <X11/extensions/Xfixes.h>
55

Edouard Gomez's avatar
Edouard Gomez committed
56 57 58
/**
 * X11 Device Demuxer context
 */
59
struct x11_grab
60
{
61
    const AVClass *class;    /**< Class for private options. */
Edouard Gomez's avatar
Edouard Gomez committed
62 63 64 65
    int frame_size;          /**< Size in bytes of a grabbed frame */
    AVRational time_base;    /**< Time base */
    int64_t time_frame;      /**< Current time */

66
    char *video_size;        /**< String describing video size, set by a private option. */
Edouard Gomez's avatar
Edouard Gomez committed
67 68 69 70 71 72 73 74 75
    int height;              /**< Height of the grab frame */
    int width;               /**< Width of the grab frame */
    int x_off;               /**< Horizontal top-left corner coordinate */
    int y_off;               /**< Vertical top-left corner coordinate */

    Display *dpy;            /**< X11 display from which x11grab grabs frames */
    XImage *image;           /**< X11 image holding the grab */
    int use_shm;             /**< !0 when using XShm extension */
    XShmSegmentInfo shminfo; /**< When using XShm, keeps track of XShm infos */
76
    int  draw_mouse;         /**< Set by a private option. */
77
    int  follow_mouse;       /**< Set by a private option. */
78
    int  show_region;        /**< set by a private option. */
79
    char *framerate;         /**< Set by a private option. */
80 81

    Window region_win;       /**< This is used by show_region option. */
82
};
Edouard Gomez's avatar
Edouard Gomez committed
83

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
#define REGION_WIN_BORDER 3
/**
 * Draw grabbing region window
 *
 * @param s x11_grab context
 */
static void
x11grab_draw_region_win(struct x11_grab *s)
{
    Display *dpy = s->dpy;
    int screen;
    Window win = s->region_win;
    GC gc;

    screen = DefaultScreen(dpy);
    gc = XCreateGC(dpy, win, 0, 0);
    XSetForeground(dpy, gc, WhitePixel(dpy, screen));
    XSetBackground(dpy, gc, BlackPixel(dpy, screen));
    XSetLineAttributes(dpy, gc, REGION_WIN_BORDER, LineDoubleDash, 0, 0);
    XDrawRectangle(dpy, win, gc,
                   1, 1,
                   (s->width  + REGION_WIN_BORDER * 2) - 1 * 2 - 1,
                   (s->height + REGION_WIN_BORDER * 2) - 1 * 2 - 1);
    XFreeGC(dpy, gc);
}

/**
 * Initialize grabbing region window
 *
 * @param s x11_grab context
 */
static void
x11grab_region_win_init(struct x11_grab *s)
{
    Display *dpy = s->dpy;
    int screen;
    XSetWindowAttributes attribs;
    XRectangle rect;

    screen = DefaultScreen(dpy);
    attribs.override_redirect = True;
    s->region_win = XCreateWindow(dpy, RootWindow(dpy, screen),
                                  s->x_off  - REGION_WIN_BORDER,
                                  s->y_off  - REGION_WIN_BORDER,
                                  s->width  + REGION_WIN_BORDER * 2,
                                  s->height + REGION_WIN_BORDER * 2,
                                  0, CopyFromParent,
                                  InputOutput, CopyFromParent,
                                  CWOverrideRedirect, &attribs);
    rect.x = 0;
    rect.y = 0;
    rect.width  = s->width;
    rect.height = s->height;
    XShapeCombineRectangles(dpy, s->region_win,
                            ShapeBounding, REGION_WIN_BORDER, REGION_WIN_BORDER,
                            &rect, 1, ShapeSubtract, 0);
    XMapWindow(dpy, s->region_win);
    XSelectInput(dpy, s->region_win, ExposureMask | StructureNotifyMask);
    x11grab_draw_region_win(s);
}

Edouard Gomez's avatar
Edouard Gomez committed
145
/**
146
 * Initialize the x11 grab device demuxer (public device demuxer API).
Edouard Gomez's avatar
Edouard Gomez committed
147 148 149
 *
 * @param s1 Context from avformat core
 * @return <ul>
150
 *          <li>AVERROR(ENOMEM) no memory left</li>
151
 *          <li>AVERROR(EIO) other failure case</li>
Edouard Gomez's avatar
Edouard Gomez committed
152 153 154
 *          <li>0 success</li>
 *         </ul>
 */
155
static int
156
x11grab_read_header(AVFormatContext *s1)
157
{
158
    struct x11_grab *x11grab = s1->priv_data;
159 160
    Display *dpy;
    AVStream *st = NULL;
161
    enum PixelFormat input_pixfmt;
162
    XImage *image;
Edouard Gomez's avatar
Edouard Gomez committed
163 164
    int x_off = 0;
    int y_off = 0;
165
    int screen;
166
    int use_shm;
167
    char *param, *offset;
168
    int ret = 0;
169
    AVRational framerate;
170

171
    param = av_strdup(s1->filename);
172 173 174
    if (!param)
        goto out;

175 176 177
    offset = strchr(param, '+');
    if (offset) {
        sscanf(offset, "%d,%d", &x_off, &y_off);
178
        x11grab->draw_mouse = !strstr(offset, "nomouse");
179 180 181
        *offset= 0;
    }

182 183 184 185
    if ((ret = av_parse_video_size(&x11grab->width, &x11grab->height, x11grab->video_size)) < 0) {
        av_log(s1, AV_LOG_ERROR, "Couldn't parse video size.\n");
        goto out;
    }
186 187 188 189
    if ((ret = av_parse_video_rate(&framerate, x11grab->framerate)) < 0) {
        av_log(s1, AV_LOG_ERROR, "Could not parse framerate: %s.\n", x11grab->framerate);
        goto out;
    }
190 191
    av_log(s1, AV_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
           s1->filename, param, x_off, y_off, x11grab->width, x11grab->height);
192 193 194 195

    dpy = XOpenDisplay(param);
    if(!dpy) {
        av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
196 197
        ret = AVERROR(EIO);
        goto out;
198
    }
199

200
    st = avformat_new_stream(s1, NULL);
201
    if (!st) {
202 203
        ret = AVERROR(ENOMEM);
        goto out;
204
    }
205
    avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
206

207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    screen = DefaultScreen(dpy);

    if (x11grab->follow_mouse) {
        int screen_w, screen_h;
        Window w;

        screen_w = DisplayWidth(dpy, screen);
        screen_h = DisplayHeight(dpy, screen);
        XQueryPointer(dpy, RootWindow(dpy, screen), &w, &w, &x_off, &y_off, &ret, &ret, &ret);
        x_off -= x11grab->width / 2;
        y_off -= x11grab->height / 2;
        x_off = FFMIN(FFMAX(x_off, 0), screen_w - x11grab->width);
        y_off = FFMIN(FFMAX(y_off, 0), screen_h - x11grab->height);
        av_log(s1, AV_LOG_INFO, "followmouse is enabled, resetting grabbing region to x: %d y: %d\n", x_off, y_off);
    }

223
    use_shm = XShmQueryExtension(dpy);
Edouard Gomez's avatar
Edouard Gomez committed
224
    av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
225 226 227 228 229 230 231 232 233

    if(use_shm) {
        int scr = XDefaultScreen(dpy);
        image = XShmCreateImage(dpy,
                                DefaultVisual(dpy, scr),
                                DefaultDepth(dpy, scr),
                                ZPixmap,
                                NULL,
                                &x11grab->shminfo,
234
                                x11grab->width, x11grab->height);
235 236 237 238 239
        x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
                                        image->bytes_per_line * image->height,
                                        IPC_CREAT|0777);
        if (x11grab->shminfo.shmid == -1) {
            av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
240 241
            ret = AVERROR(ENOMEM);
            goto out;
242 243 244 245 246 247 248
        }
        x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
        x11grab->shminfo.readOnly = False;

        if (!XShmAttach(dpy, &x11grab->shminfo)) {
            av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
            /* needs some better error subroutine :) */
249 250
            ret = AVERROR(EIO);
            goto out;
251 252
        }
    } else {
253
        image = XGetImage(dpy, RootWindow(dpy, screen),
254
                          x_off,y_off,
255
                          x11grab->width, x11grab->height,
256 257 258 259 260
                          AllPlanes, ZPixmap);
    }

    switch (image->bits_per_pixel) {
    case 8:
Diego Biurrun's avatar
Diego Biurrun committed
261
        av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
262 263 264
        input_pixfmt = PIX_FMT_PAL8;
        break;
    case 16:
Edouard Gomez's avatar
Edouard Gomez committed
265 266 267
        if (       image->red_mask   == 0xf800 &&
                   image->green_mask == 0x07e0 &&
                   image->blue_mask  == 0x001f ) {
268 269
            av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
            input_pixfmt = PIX_FMT_RGB565;
Edouard Gomez's avatar
Edouard Gomez committed
270 271 272
        } else if (image->red_mask   == 0x7c00 &&
                   image->green_mask == 0x03e0 &&
                   image->blue_mask  == 0x001f ) {
273 274 275 276 277
            av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
            input_pixfmt = PIX_FMT_RGB555;
        } else {
            av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
            av_log(s1, AV_LOG_ERROR, "color masks: r 0x%.6lx g 0x%.6lx b 0x%.6lx\n", image->red_mask, image->green_mask, image->blue_mask);
278 279
            ret = AVERROR(EIO);
            goto out;
280 281 282
        }
        break;
    case 24:
Edouard Gomez's avatar
Edouard Gomez committed
283 284 285
        if (        image->red_mask   == 0xff0000 &&
                    image->green_mask == 0x00ff00 &&
                    image->blue_mask  == 0x0000ff ) {
286
            input_pixfmt = PIX_FMT_BGR24;
Edouard Gomez's avatar
Edouard Gomez committed
287 288 289
        } else if ( image->red_mask   == 0x0000ff &&
                    image->green_mask == 0x00ff00 &&
                    image->blue_mask  == 0xff0000 ) {
290 291 292 293
            input_pixfmt = PIX_FMT_RGB24;
        } else {
            av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
            av_log(s1, AV_LOG_ERROR, "color masks: r 0x%.6lx g 0x%.6lx b 0x%.6lx\n", image->red_mask, image->green_mask, image->blue_mask);
294 295
            ret = AVERROR(EIO);
            goto out;
296 297 298
        }
        break;
    case 32:
299
        input_pixfmt = PIX_FMT_RGB32;
300 301 302
        break;
    default:
        av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
303 304
        ret = AVERROR(EINVAL);
        goto out;
305 306
    }

307
    x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel/8;
308
    x11grab->dpy = dpy;
309 310
    x11grab->time_base  = (AVRational){framerate.den, framerate.num};
    x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base);
311 312 313 314 315
    x11grab->x_off = x_off;
    x11grab->y_off = y_off;
    x11grab->image = image;
    x11grab->use_shm = use_shm;

316
    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
317
    st->codec->codec_id = CODEC_ID_RAWVIDEO;
318 319
    st->codec->width  = x11grab->width;
    st->codec->height = x11grab->height;
320
    st->codec->pix_fmt = input_pixfmt;
321 322
    st->codec->time_base = x11grab->time_base;
    st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(x11grab->time_base) * 8;
323

324
out:
325
    av_free(param);
326
    return ret;
327 328
}

Edouard Gomez's avatar
Edouard Gomez committed
329
/**
330
 * Paint a mouse pointer in an X11 image.
Edouard Gomez's avatar
Edouard Gomez committed
331
 *
Diego Biurrun's avatar
Diego Biurrun committed
332
 * @param image image to paint the mouse pointer to
Edouard Gomez's avatar
Edouard Gomez committed
333 334 335
 * @param s context used to retrieve original grabbing rectangle
 *          coordinates
 */
336
static void
Roxis's avatar
Roxis committed
337
paint_mouse_pointer(XImage *image, struct x11_grab *s)
338
{
339 340 341 342
    int x_off = s->x_off;
    int y_off = s->y_off;
    int width = s->width;
    int height = s->height;
Roxis's avatar
Roxis committed
343 344 345 346 347
    Display *dpy = s->dpy;
    XFixesCursorImage *xcim;
    int x, y;
    int line, column;
    int to_line, to_column;
348 349 350 351 352 353 354 355 356 357
    int pixstride = image->bits_per_pixel >> 3;
    /* Warning: in its insanity, xlib provides unsigned image data through a
     * char* pointer, so we have to make it uint8_t to make things not break.
     * Anyone who performs further investigation of the xlib API likely risks
     * permanent brain damage. */
    uint8_t *pix = image->data;

    /* Code doesn't currently support 16-bit or PAL8 */
    if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32)
        return;
358

Carl Eugen Hoyos's avatar
Carl Eugen Hoyos committed
359
    xcim = XFixesGetCursorImage(dpy);
360

Roxis's avatar
Roxis committed
361 362 363 364 365 366 367 368
    x = xcim->x - xcim->xhot;
    y = xcim->y - xcim->yhot;

    to_line = FFMIN((y + xcim->height), (height + y_off));
    to_column = FFMIN((x + xcim->width), (width + x_off));

    for (line = FFMAX(y, y_off); line < to_line; line++) {
        for (column = FFMAX(x, x_off); column < to_column; column++) {
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
            int  xcim_addr = (line - y) * xcim->width + column - x;
            int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
            int r = (uint8_t)(xcim->pixels[xcim_addr] >>  0);
            int g = (uint8_t)(xcim->pixels[xcim_addr] >>  8);
            int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
            int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);

            if (a == 255) {
                pix[image_addr+0] = r;
                pix[image_addr+1] = g;
                pix[image_addr+2] = b;
            } else if (a) {
                /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
                pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
                pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
                pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
385
            }
386 387
        }
    }
Roxis's avatar
Roxis committed
388 389 390

    XFree(xcim);
    xcim = NULL;
391 392 393
}


Edouard Gomez's avatar
Edouard Gomez committed
394
/**
395
 * Read new data in the image structure.
Edouard Gomez's avatar
Edouard Gomez committed
396 397
 *
 * @param dpy X11 display to grab from
398
 * @param d
Edouard Gomez's avatar
Edouard Gomez committed
399 400 401 402
 * @param image Image where the grab will be put
 * @param x Top-Left grabbing rectangle horizontal coordinate
 * @param y Top-Left grabbing rectangle vertical coordinate
 * @return 0 if error, !0 if successful
403 404
 */
static int
Edouard Gomez's avatar
Edouard Gomez committed
405
xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
406
{
407 408 409 410 411
    xGetImageReply rep;
    xGetImageReq *req;
    long nbytes;

    if (!image) {
Edouard Gomez's avatar
Edouard Gomez committed
412
        return 0;
413 414 415 416 417 418 419 420 421 422 423 424 425 426
    }

    LockDisplay(dpy);
    GetReq(GetImage, req);

    /* First set up the standard stuff in the request */
    req->drawable = d;
    req->x = x;
    req->y = y;
    req->width = image->width;
    req->height = image->height;
    req->planeMask = (unsigned int)AllPlanes;
    req->format = ZPixmap;

Edouard Gomez's avatar
Edouard Gomez committed
427
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
428 429
        UnlockDisplay(dpy);
        SyncHandle();
Edouard Gomez's avatar
Edouard Gomez committed
430
        return 0;
431 432 433 434 435 436 437
    }

    nbytes = (long)rep.length << 2;
    _XReadPad(dpy, image->data, nbytes);

    UnlockDisplay(dpy);
    SyncHandle();
Edouard Gomez's avatar
Edouard Gomez committed
438
    return 1;
439 440
}

Edouard Gomez's avatar
Edouard Gomez committed
441
/**
442
 * Grab a frame from x11 (public device demuxer API).
Edouard Gomez's avatar
Edouard Gomez committed
443 444 445 446 447
 *
 * @param s1 Context from avformat core
 * @param pkt Packet holding the brabbed frame
 * @return frame size in bytes
 */
448 449
static int
x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
450
{
451
    struct x11_grab *s = s1->priv_data;
452 453 454 455 456
    Display *dpy = s->dpy;
    XImage *image = s->image;
    int x_off = s->x_off;
    int y_off = s->y_off;

457 458 459 460
    int screen;
    Window root;
    int follow_mouse = s->follow_mouse;

461 462 463 464
    int64_t curtime, delay;
    struct timespec ts;

    /* Calculate the time of the next frame */
465
    s->time_frame += INT64_C(1000000);
466 467 468 469

    /* wait based on the frame rate */
    for(;;) {
        curtime = av_gettime();
Edouard Gomez's avatar
Edouard Gomez committed
470
        delay = s->time_frame * av_q2d(s->time_base) - curtime;
471
        if (delay <= 0) {
472 473
            if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
                s->time_frame += INT64_C(1000000);
474 475 476 477 478 479 480 481
            }
            break;
        }
        ts.tv_sec = delay / 1000000;
        ts.tv_nsec = (delay % 1000000) * 1000;
        nanosleep(&ts, NULL);
    }

482 483 484
    av_init_packet(pkt);
    pkt->data = image->data;
    pkt->size = s->frame_size;
Edouard Gomez's avatar
Edouard Gomez committed
485
    pkt->pts = curtime;
486

487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
    screen = DefaultScreen(dpy);
    root = RootWindow(dpy, screen);
    if (follow_mouse) {
        int screen_w, screen_h;
        int pointer_x, pointer_y, _;
        Window w;

        screen_w = DisplayWidth(dpy, screen);
        screen_h = DisplayHeight(dpy, screen);
        XQueryPointer(dpy, root, &w, &w, &pointer_x, &pointer_y, &_, &_, &_);
        if (follow_mouse == -1) {
            // follow the mouse, put it at center of grabbing region
            x_off += pointer_x - s->width  / 2 - x_off;
            y_off += pointer_y - s->height / 2 - y_off;
        } else {
            // follow the mouse, but only move the grabbing region when mouse
            // reaches within certain pixels to the edge.
            if (pointer_x > x_off + s->width - follow_mouse) {
                x_off += pointer_x - (x_off + s->width - follow_mouse);
            } else if (pointer_x < x_off + follow_mouse)
                x_off -= (x_off + follow_mouse) - pointer_x;
            if (pointer_y > y_off + s->height - follow_mouse) {
                y_off += pointer_y - (y_off + s->height - follow_mouse);
            } else if (pointer_y < y_off + follow_mouse)
                y_off -= (y_off + follow_mouse) - pointer_y;
        }
        // adjust grabbing region position if it goes out of screen.
        s->x_off = x_off = FFMIN(FFMAX(x_off, 0), screen_w - s->width);
        s->y_off = y_off = FFMIN(FFMAX(y_off, 0), screen_h - s->height);
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532

        if (s->show_region && s->region_win)
            XMoveWindow(dpy, s->region_win,
                        s->x_off - REGION_WIN_BORDER,
                        s->y_off - REGION_WIN_BORDER);
    }

    if (s->show_region) {
        if (s->region_win) {
            XEvent evt;
            // clean up the events, and do the initinal draw or redraw.
            for (evt.type = NoEventMask; XCheckMaskEvent(dpy, ExposureMask | StructureNotifyMask, &evt); );
            if (evt.type)
                x11grab_draw_region_win(s);
        } else {
            x11grab_region_win_init(s);
        }
533 534
    }

535
    if(s->use_shm) {
536
        if (!XShmGetImage(dpy, root, image, x_off, y_off, AllPlanes)) {
537 538 539
            av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
        }
    } else {
540
        if (!xget_zpixmap(dpy, root, image, x_off, y_off)) {
541 542 543 544
            av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
        }
    }

545
    if (s->draw_mouse) {
Roxis's avatar
Roxis committed
546
        paint_mouse_pointer(image, s);
547 548 549
    }

    return s->frame_size;
550 551
}

Edouard Gomez's avatar
Edouard Gomez committed
552
/**
553
 * Close x11 frame grabber (public device demuxer API).
Edouard Gomez's avatar
Edouard Gomez committed
554 555 556 557
 *
 * @param s1 Context from avformat core
 * @return 0 success, !0 failure
 */
558 559
static int
x11grab_read_close(AVFormatContext *s1)
560
{
561
    struct x11_grab *x11grab = s1->priv_data;
562 563 564 565 566 567 568 569 570 571 572 573 574 575

    /* Detach cleanly from shared mem */
    if (x11grab->use_shm) {
        XShmDetach(x11grab->dpy, &x11grab->shminfo);
        shmdt(x11grab->shminfo.shmaddr);
        shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
    }

    /* Destroy X11 image */
    if (x11grab->image) {
        XDestroyImage(x11grab->image);
        x11grab->image = NULL;
    }

576 577 578 579
    if (x11grab->region_win) {
        XDestroyWindow(x11grab->dpy, x11grab->region_win);
    }

580 581 582
    /* Free X11 display */
    XCloseDisplay(x11grab->dpy);
    return 0;
583 584
}

585 586 587
#define OFFSET(x) offsetof(struct x11_grab, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
588 589 590
    { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), AV_OPT_TYPE_STRING, {.str = "vga"}, 0, 0, DEC },
    { "framerate", "", OFFSET(framerate), AV_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC },
    { "draw_mouse", "Draw the mouse pointer.", OFFSET(draw_mouse), AV_OPT_TYPE_INT, { 1 }, 0, 1, DEC },
591
    { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
592 593 594
      OFFSET(follow_mouse), AV_OPT_TYPE_INT, { 0 }, -1, INT_MAX, DEC, "follow_mouse" },
    { "centered", "Keep the mouse pointer at the center of grabbing region when following.", 0, AV_OPT_TYPE_CONST, { -1 }, INT_MIN, INT_MAX, DEC, "follow_mouse" },
    { "show_region", "Show the grabbing region.", OFFSET(show_region), AV_OPT_TYPE_INT, { 0 }, 0, 1, DEC },
595 596 597 598 599 600 601 602 603 604
    { NULL },
};

static const AVClass x11_class = {
    .class_name = "X11grab indev",
    .item_name  = av_default_item_name,
    .option     = options,
    .version    = LIBAVUTIL_VERSION_INT,
};

Edouard Gomez's avatar
Edouard Gomez committed
605
/** x11 grabber device demuxer declaration */
606 607 608 609 610 611 612 613 614
AVInputFormat ff_x11_grab_device_demuxer = {
    .name           = "x11grab",
    .long_name      = NULL_IF_CONFIG_SMALL("X11grab"),
    .priv_data_size = sizeof(struct x11_grab),
    .read_header    = x11grab_read_header,
    .read_packet    = x11grab_read_packet,
    .read_close     = x11grab_read_close,
    .flags          = AVFMT_NOFILE,
    .priv_class     = &x11_class,
615
};