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
Edouard Gomez's avatar
Edouard Gomez committed
34 35 36 37
 * X11 frame device demuxer by Clemens Fruhwirth <clemens@endorphin.org>
 * and Edouard Gomez <ed.gomez@free.fr>.
 */

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

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

65
    char *video_size;        /**< String describing video size, set by a private option. */
Edouard Gomez's avatar
Edouard Gomez committed
66 67 68 69 70 71 72 73 74
    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 */
75
    int  draw_mouse;         /**< Set by a private option. */
76
    int  follow_mouse;       /**< Set by a private option. */
77
    int  show_region;        /**< set by a private option. */
78
    char *framerate;         /**< Set by a private option. */
79 80

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

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
#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
144
/**
145
 * Initialize the x11 grab device demuxer (public device demuxer API).
Edouard Gomez's avatar
Edouard Gomez committed
146 147 148 149
 *
 * @param s1 Context from avformat core
 * @param ap Parameters 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 156
static int
x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
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
    offset = strchr(param, '+');
    if (offset) {
        sscanf(offset, "%d,%d", &x_off, &y_off);
175
        x11grab->draw_mouse = !strstr(offset, "nomouse");
176 177 178
        *offset= 0;
    }

179 180 181 182
    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;
    }
183 184 185 186
    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;
    }
187 188
    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);
189 190 191 192

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

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

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
    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);
    }

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

    if(use_shm) {
        int scr = XDefaultScreen(dpy);
        image = XShmCreateImage(dpy,
                                DefaultVisual(dpy, scr),
                                DefaultDepth(dpy, scr),
                                ZPixmap,
                                NULL,
                                &x11grab->shminfo,
231
                                x11grab->width, x11grab->height);
232 233 234 235 236
        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");
237 238
            ret = AVERROR(ENOMEM);
            goto out;
239 240 241 242 243 244 245
        }
        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 :) */
246 247
            ret = AVERROR(EIO);
            goto out;
248 249
        }
    } else {
250
        image = XGetImage(dpy, RootWindow(dpy, screen),
251
                          x_off,y_off,
252
                          x11grab->width, x11grab->height,
253 254 255 256 257
                          AllPlanes, ZPixmap);
    }

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

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

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

321 322
out:
    return ret;
323 324
}

Edouard Gomez's avatar
Edouard Gomez committed
325
/**
326
 * Paint a mouse pointer in an X11 image.
Edouard Gomez's avatar
Edouard Gomez committed
327
 *
Diego Biurrun's avatar
Diego Biurrun committed
328
 * @param image image to paint the mouse pointer to
Edouard Gomez's avatar
Edouard Gomez committed
329 330 331
 * @param s context used to retrieve original grabbing rectangle
 *          coordinates
 */
332
static void
Roxis's avatar
Roxis committed
333
paint_mouse_pointer(XImage *image, struct x11_grab *s)
334
{
335 336 337 338
    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
339 340 341 342 343
    Display *dpy = s->dpy;
    XFixesCursorImage *xcim;
    int x, y;
    int line, column;
    int to_line, to_column;
344 345 346 347 348 349 350 351 352 353
    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;
354

Carl Eugen Hoyos's avatar
Carl Eugen Hoyos committed
355
    xcim = XFixesGetCursorImage(dpy);
356

Roxis's avatar
Roxis committed
357 358 359 360 361 362 363 364
    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++) {
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
            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;
381
            }
382 383
        }
    }
Roxis's avatar
Roxis committed
384 385 386

    XFree(xcim);
    xcim = NULL;
387 388 389
}


Edouard Gomez's avatar
Edouard Gomez committed
390
/**
391
 * Read new data in the image structure.
Edouard Gomez's avatar
Edouard Gomez committed
392 393
 *
 * @param dpy X11 display to grab from
394
 * @param d
Edouard Gomez's avatar
Edouard Gomez committed
395 396 397 398
 * @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
399 400
 */
static int
Edouard Gomez's avatar
Edouard Gomez committed
401
xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
402
{
403 404 405 406 407
    xGetImageReply rep;
    xGetImageReq *req;
    long nbytes;

    if (!image) {
Edouard Gomez's avatar
Edouard Gomez committed
408
        return 0;
409 410 411 412 413 414 415 416 417 418 419 420 421 422
    }

    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
423
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
424 425
        UnlockDisplay(dpy);
        SyncHandle();
Edouard Gomez's avatar
Edouard Gomez committed
426
        return 0;
427 428 429 430 431 432 433
    }

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

    UnlockDisplay(dpy);
    SyncHandle();
Edouard Gomez's avatar
Edouard Gomez committed
434
    return 1;
435 436
}

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

453 454 455 456
    int screen;
    Window root;
    int follow_mouse = s->follow_mouse;

457 458 459 460
    int64_t curtime, delay;
    struct timespec ts;

    /* Calculate the time of the next frame */
461
    s->time_frame += INT64_C(1000000);
462 463 464 465

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

478 479 480
    av_init_packet(pkt);
    pkt->data = image->data;
    pkt->size = s->frame_size;
Edouard Gomez's avatar
Edouard Gomez committed
481
    pkt->pts = curtime;
482

483 484 485 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
    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);
512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528

        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);
        }
529 530
    }

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

541
    if (s->draw_mouse) {
Roxis's avatar
Roxis committed
542
        paint_mouse_pointer(image, s);
543 544 545
    }

    return s->frame_size;
546 547
}

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

    /* 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;
    }

572 573 574 575
    if (x11grab->region_win) {
        XDestroyWindow(x11grab->dpy, x11grab->region_win);
    }

576 577 578
    /* Free X11 display */
    XCloseDisplay(x11grab->dpy);
    return 0;
579 580
}

581 582 583
#define OFFSET(x) offsetof(struct x11_grab, x)
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
584 585 586
    { "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 },
587
    { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
588 589 590
      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 },
591 592 593 594 595 596 597 598 599 600
    { 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
601
/** x11 grabber device demuxer declaration */
602 603 604 605 606 607 608 609 610
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,
611
};