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 x11grab {
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
#define REGION_WIN_BORDER 3
/**
 * Draw grabbing region window
 *
87
 * @param s x11grab context
88 89
 */
static void
90
x11grab_draw_region_win(struct x11grab *s)
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
{
    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
 *
112
 * @param s x11grab context
113 114
 */
static void
115
x11grab_region_win_init(struct x11grab *s)
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
{
    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
 *
 * @param s1 Context from avformat core
 * @return <ul>
149
 *          <li>AVERROR(ENOMEM) no memory left</li>
150
 *          <li>AVERROR(EIO) other failure case</li>
Edouard Gomez's avatar
Edouard Gomez committed
151 152 153
 *          <li>0 success</li>
 *         </ul>
 */
154
static int
155
x11grab_read_header(AVFormatContext *s1)
156
{
157
    struct x11grab *x11grab = s1->priv_data;
158 159
    Display *dpy;
    AVStream *st = NULL;
160
    enum PixelFormat input_pixfmt;
161
    XImage *image;
Edouard Gomez's avatar
Edouard Gomez committed
162 163
    int x_off = 0;
    int y_off = 0;
164
    int screen;
165
    int use_shm;
166
    char *param, *offset;
167
    int ret = 0;
168
    AVRational framerate;
169

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

481 482 483
    av_init_packet(pkt);
    pkt->data = image->data;
    pkt->size = s->frame_size;
Edouard Gomez's avatar
Edouard Gomez committed
484
    pkt->pts = curtime;
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 512 513 514
    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);
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531

        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);
        }
532 533
    }

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

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

    return s->frame_size;
549 550
}

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

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

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

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

584
#define OFFSET(x) offsetof(struct x11grab, x)
585 586
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
587 588 589
    { "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 },
590
    { "follow_mouse", "Move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region.",
591 592 593
      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 },
594 595 596 597 598 599 600 601 602 603
    { 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
604
/** x11 grabber device demuxer declaration */
605
AVInputFormat ff_x11grab_demuxer = {
606 607
    .name           = "x11grab",
    .long_name      = NULL_IF_CONFIG_SMALL("X11grab"),
608
    .priv_data_size = sizeof(struct x11grab),
609 610 611 612 613
    .read_header    = x11grab_read_header,
    .read_packet    = x11grab_read_packet,
    .read_close     = x11grab_read_close,
    .flags          = AVFMT_NOFILE,
    .priv_class     = &x11_class,
614
};