x11grab.c 22.9 KB
Newer Older
1 2
/*
 * X11 video grab interface
3
 *
4
 * This file is part of FFmpeg.
5
 *
6 7 8
 * FFmpeg integration:
 * 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
 * FFmpeg 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
 * FFmpeg 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 28 29
 * You should have received a copy of the GNU General Public License
 * along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
30
 */
31

E
Edouard Gomez 已提交
32
/**
33
 * @file
D
Diego Biurrun 已提交
34 35 36
 * X11 frame device demuxer
 * @author Clemens Fruhwirth <clemens@endorphin.org>
 * @author Edouard Gomez <ed.gomez@free.fr>
E
Edouard Gomez 已提交
37 38
 */

39
#include "config.h"
L
Luca Barbato 已提交
40

41
#include <time.h>
L
Luca Barbato 已提交
42 43
#include <sys/shm.h>

I
Isaac Dooley 已提交
44
#include <X11/cursorfont.h>
45 46 47 48
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/Xproto.h>
49
#include <X11/Xutil.h>
L
Luca Barbato 已提交
50

Y
Yu-Jie Lin 已提交
51
#include <X11/extensions/shape.h>
R
Roxis 已提交
52
#include <X11/extensions/Xfixes.h>
L
Luca Barbato 已提交
53
#include <X11/extensions/XShm.h>
54

55
#include "libavutil/internal.h"
L
Luca Barbato 已提交
56 57 58 59 60 61 62
#include "libavutil/log.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/time.h"

#include "libavformat/internal.h"

63 64
#include "avdevice.h"

L
Luca Barbato 已提交
65
/** X11 device demuxer context */
66
typedef struct X11GrabContext {
67
    const AVClass *class;    /**< Class for private options. */
E
Edouard Gomez 已提交
68 69 70 71 72
    int frame_size;          /**< Size in bytes of a grabbed frame */
    AVRational time_base;    /**< Time base */
    int64_t time_frame;      /**< Current time */

    int width;               /**< Width of the grab frame */
73
    int height;              /**< Height of the grab frame */
E
Edouard Gomez 已提交
74 75 76 77 78 79 80
    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 */
L
Luca Barbato 已提交
81 82 83
    int draw_mouse;          /**< Set by a private option. */
    int follow_mouse;        /**< Set by a private option. */
    int show_region;         /**< set by a private option. */
84
    AVRational framerate;    /**< Set by a private option. */
85 86
    int palette_changed;
    uint32_t palette[256];
Y
Yu-Jie Lin 已提交
87

88
    Cursor c;
Y
Yu-Jie Lin 已提交
89
    Window region_win;       /**< This is used by show_region option. */
90
} X11GrabContext;
E
Edouard Gomez 已提交
91

Y
Yu-Jie Lin 已提交
92
#define REGION_WIN_BORDER 3
L
Luca Barbato 已提交
93

Y
Yu-Jie Lin 已提交
94 95 96
/**
 * Draw grabbing region window
 *
97
 * @param s x11grab context
Y
Yu-Jie Lin 已提交
98
 */
99
static void x11grab_draw_region_win(X11GrabContext *s)
Y
Yu-Jie Lin 已提交
100 101
{
    Display *dpy = s->dpy;
L
Luca Barbato 已提交
102
    Window win   = s->region_win;
103 104
    int screen = DefaultScreen(dpy);
    GC gc = XCreateGC(dpy, win, 0, 0);
Y
Yu-Jie Lin 已提交
105 106 107 108

    XSetForeground(dpy, gc, WhitePixel(dpy, screen));
    XSetBackground(dpy, gc, BlackPixel(dpy, screen));
    XSetLineAttributes(dpy, gc, REGION_WIN_BORDER, LineDoubleDash, 0, 0);
L
Luca Barbato 已提交
109
    XDrawRectangle(dpy, win, gc, 1, 1,
Y
Yu-Jie Lin 已提交
110 111 112 113 114 115 116 117
                   (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
 *
118
 * @param s x11grab context
Y
Yu-Jie Lin 已提交
119
 */
120
static void x11grab_region_win_init(X11GrabContext *s)
Y
Yu-Jie Lin 已提交
121 122 123
{
    Display *dpy = s->dpy;
    XRectangle rect;
124 125
    XSetWindowAttributes attribs = { .override_redirect = True };
    int screen = DefaultScreen(dpy);
Y
Yu-Jie Lin 已提交
126 127 128 129 130 131 132 133 134

    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);
L
Luca Barbato 已提交
135 136
    rect.x      = 0;
    rect.y      = 0;
Y
Yu-Jie Lin 已提交
137 138 139 140 141 142 143 144 145 146
    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);
}

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
static int setup_shm(AVFormatContext *s, Display *dpy, XImage **image)
{
    X11GrabContext *g = s->priv_data;
    int scr           = XDefaultScreen(dpy);
    XImage *img       = XShmCreateImage(dpy, DefaultVisual(dpy, scr),
                                        DefaultDepth(dpy, scr), ZPixmap, NULL,
                                        &g->shminfo, g->width, g->height);

    g->shminfo.shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height,
                              IPC_CREAT | 0777);

    if (g->shminfo.shmid == -1) {
        av_log(s, AV_LOG_ERROR, "Cannot get shared memory!\n");
        return AVERROR(ENOMEM);
    }

    g->shminfo.shmaddr  = img->data = shmat(g->shminfo.shmid, 0, 0);
    g->shminfo.readOnly = False;

    if (!XShmAttach(dpy, &g->shminfo)) {
        av_log(s, AV_LOG_ERROR, "Failed to attach shared memory!\n");
        /* needs some better error subroutine :) */
        return AVERROR(EIO);
    }

    *image = img;
    return 0;
}

176 177 178 179 180 181 182 183 184 185 186 187 188
static int setup_mouse(Display *dpy, int screen)
{
    int ev_ret, ev_err;

    if (XFixesQueryExtension(dpy, &ev_ret, &ev_err)) {
        Window root = RootWindow(dpy, screen);
        XFixesSelectCursorInput(dpy, root, XFixesDisplayCursorNotifyMask);
        return 0;
    }

    return AVERROR(ENOSYS);
}

189 190 191 192 193 194 195 196 197
static int pixfmt_from_image(AVFormatContext *s, XImage *image, int *pix_fmt)
{
    av_log(s, AV_LOG_DEBUG,
           "Image r 0x%.6lx g 0x%.6lx b 0x%.6lx and depth %i\n",
           image->red_mask,
           image->green_mask,
           image->blue_mask,
           image->bits_per_pixel);

198 199
    *pix_fmt = AV_PIX_FMT_NONE;

200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    switch (image->bits_per_pixel) {
    case 8:
        *pix_fmt =  AV_PIX_FMT_PAL8;
        break;
    case 16:
        if (image->red_mask   == 0xf800 &&
            image->green_mask == 0x07e0 &&
            image->blue_mask  == 0x001f) {
            *pix_fmt = AV_PIX_FMT_RGB565;
        } else if (image->red_mask   == 0x7c00 &&
                   image->green_mask == 0x03e0 &&
                   image->blue_mask  == 0x001f) {
            *pix_fmt = AV_PIX_FMT_RGB555;
        }
        break;
    case 24:
        if (image->red_mask   == 0xff0000 &&
            image->green_mask == 0x00ff00 &&
            image->blue_mask  == 0x0000ff) {
            *pix_fmt = AV_PIX_FMT_BGR24;
        } else if (image->red_mask   == 0x0000ff &&
                   image->green_mask == 0x00ff00 &&
                   image->blue_mask  == 0xff0000) {
            *pix_fmt = AV_PIX_FMT_RGB24;
        }
        break;
    case 32:
227 228 229 230 231
        if (image->red_mask   == 0xff0000 &&
            image->green_mask == 0x00ff00 &&
            image->blue_mask  == 0x0000ff ) {
            *pix_fmt = AV_PIX_FMT_0RGB32;
        }
232
        break;
233 234
    }
    if (*pix_fmt == AV_PIX_FMT_NONE) {
235 236 237 238 239 240 241 242 243 244 245 246 247 248
        av_log(s, AV_LOG_ERROR,
               "XImages with RGB mask 0x%.6lx 0x%.6lx 0x%.6lx and depth %i "
               "are currently not supported.\n",
               image->red_mask,
               image->green_mask,
               image->blue_mask,
               image->bits_per_pixel);

        return AVERROR_PATCHWELCOME;
    }

    return 0;
}

E
Edouard Gomez 已提交
249
/**
250
 * Initialize the x11 grab device demuxer (public device demuxer API).
E
Edouard Gomez 已提交
251 252 253
 *
 * @param s1 Context from avformat core
 * @return <ul>
254
 *          <li>AVERROR(ENOMEM) no memory left</li>
255
 *          <li>AVERROR(EIO) other failure case</li>
E
Edouard Gomez 已提交
256 257 258
 *          <li>0 success</li>
 *         </ul>
 */
L
Luca Barbato 已提交
259
static int x11grab_read_header(AVFormatContext *s1)
260
{
261
    X11GrabContext *x11grab = s1->priv_data;
262 263 264
    Display *dpy;
    AVStream *st = NULL;
    XImage *image;
265
    int x_off = 0, y_off = 0, ret = 0, screen, use_shm = 0;
266
    char *dpyname, *offset;
267 268 269
    Colormap color_map;
    XColor color[256];
    int i;
270

271
    dpyname = av_strdup(s1->filename);
272
    if (!dpyname)
273 274
        goto out;

275
    offset = strchr(dpyname, '+');
276 277
    if (offset) {
        sscanf(offset, "%d,%d", &x_off, &y_off);
278 279 280 281 282 283
        if (strstr(offset, "nomouse")) {
            av_log(s1, AV_LOG_WARNING,
                   "'nomouse' specification in argument is deprecated: "
                   "use 'draw_mouse' option with value 0 instead\n");
            x11grab->draw_mouse = 0;
        }
L
Luca Barbato 已提交
284
        *offset = 0;
285 286
    }

L
Luca Barbato 已提交
287 288
    av_log(s1, AV_LOG_INFO,
           "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
289
           s1->filename, dpyname, x_off, y_off, x11grab->width, x11grab->height);
290

291 292
    dpy = XOpenDisplay(dpyname);
    av_freep(&dpyname);
L
Luca Barbato 已提交
293
    if (!dpy) {
294
        av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
295 296
        ret = AVERROR(EIO);
        goto out;
297
    }
298

299
    st = avformat_new_stream(s1, NULL);
300
    if (!st) {
301 302
        ret = AVERROR(ENOMEM);
        goto out;
303
    }
304
    avpriv_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
305

Y
Yu-Jie Lin 已提交
306 307 308 309 310 311 312 313
    screen = DefaultScreen(dpy);

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

        screen_w = DisplayWidth(dpy, screen);
        screen_h = DisplayHeight(dpy, screen);
L
Luca Barbato 已提交
314 315
        XQueryPointer(dpy, RootWindow(dpy, screen), &w, &w, &x_off, &y_off,
                      &ret, &ret, &ret);
Y
Yu-Jie Lin 已提交
316 317
        x_off -= x11grab->width / 2;
        y_off -= x11grab->height / 2;
318 319
        x_off = av_clip(x_off, 0, screen_w - x11grab->width);
        y_off = av_clip(y_off, 0, screen_h - x11grab->height);
L
Luca Barbato 已提交
320 321 322
        av_log(s1, AV_LOG_INFO,
               "followmouse is enabled, resetting grabbing region to x: %d y: %d\n",
               x_off, y_off);
Y
Yu-Jie Lin 已提交
323 324
    }

325
    if (x11grab->use_shm) {
326
        use_shm = XShmQueryExtension(dpy);
327
        av_log(s1, AV_LOG_INFO,
328
               "shared memory extension %sfound\n", use_shm ? "" : "not ");
329
    }
330

331 332 333 334 335 336
    if (use_shm && setup_shm(s1, dpy, &image) < 0) {
        av_log(s1, AV_LOG_WARNING, "Falling back to XGetImage\n");
        use_shm = 0;
    }

    if (!use_shm) {
Y
Yu-Jie Lin 已提交
337
        image = XGetImage(dpy, RootWindow(dpy, screen),
L
Luca Barbato 已提交
338
                          x_off, y_off,
339
                          x11grab->width, x11grab->height,
340 341 342
                          AllPlanes, ZPixmap);
    }

343 344 345 346 347 348
    if (x11grab->draw_mouse && setup_mouse(dpy, screen) < 0) {
        av_log(s1, AV_LOG_WARNING,
               "XFixes not available, cannot draw the mouse cursor\n");
        x11grab->draw_mouse = 0;
    }

L
Luca Barbato 已提交
349 350
    x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel / 8;
    x11grab->dpy        = dpy;
351
    x11grab->time_base  = av_inv_q(x11grab->framerate);
352
    x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base);
L
Luca Barbato 已提交
353 354 355 356
    x11grab->x_off      = x_off;
    x11grab->y_off      = y_off;
    x11grab->image      = image;
    x11grab->use_shm    = use_shm;
357

358 359 360 361
    ret = pixfmt_from_image(s1, image, &st->codec->pix_fmt);
    if (ret < 0)
        goto out;

362
    if (st->codec->pix_fmt == AV_PIX_FMT_PAL8) {
363 364 365 366 367 368 369 370 371
        color_map = DefaultColormap(dpy, screen);
        for (i = 0; i < 256; ++i)
            color[i].pixel = i;
        XQueryColors(dpy, color_map, color, 256);
        for (i = 0; i < 256; ++i)
            x11grab->palette[i] = (color[i].red   & 0xFF00) << 8 |
                                  (color[i].green & 0xFF00)      |
                                  (color[i].blue  & 0xFF00) >> 8;
        x11grab->palette_changed = 1;
372 373 374
    }


375
    st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
L
Luca Barbato 已提交
376 377 378 379 380
    st->codec->codec_id   = AV_CODEC_ID_RAWVIDEO;
    st->codec->width      = x11grab->width;
    st->codec->height     = x11grab->height;
    st->codec->time_base  = x11grab->time_base;
    st->codec->bit_rate   = x11grab->frame_size * 1 / av_q2d(x11grab->time_base) * 8;
381

382
out:
M
Michael Niedermayer 已提交
383
    av_free(dpyname);
384
    return ret;
385 386
}

E
Edouard Gomez 已提交
387
/**
388
 * Paint a mouse pointer in an X11 image.
E
Edouard Gomez 已提交
389
 *
D
Diego Biurrun 已提交
390
 * @param image image to paint the mouse pointer to
E
Edouard Gomez 已提交
391 392 393
 * @param s context used to retrieve original grabbing rectangle
 *          coordinates
 */
394
static void paint_mouse_pointer(XImage *image, AVFormatContext *s1)
395
{
396
    X11GrabContext *s = s1->priv_data;
L
Luca Barbato 已提交
397 398 399 400
    int x_off    = s->x_off;
    int y_off    = s->y_off;
    int width    = s->width;
    int height   = s->height;
R
Roxis 已提交
401 402 403 404 405
    Display *dpy = s->dpy;
    XFixesCursorImage *xcim;
    int x, y;
    int line, column;
    int to_line, to_column;
406 407 408 409 410 411
    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;
412
    Window root;
413
    XSetWindowAttributes attr;
414 415 416 417

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

419
    if (!s->c)
420
        s->c = XCreateFontCursor(dpy, XC_left_ptr);
421
    root = DefaultRootWindow(dpy);
422
    attr.cursor = s->c;
423
    XChangeWindowAttributes(dpy, root, CWCursor, &attr);
I
Isaac Dooley 已提交
424

C
Carl Eugen Hoyos 已提交
425
    xcim = XFixesGetCursorImage(dpy);
426 427
    if (!xcim) {
        av_log(s1, AV_LOG_WARNING,
428
               "XFixesGetCursorImage failed\n");
429 430
        return;
    }
431

R
Roxis 已提交
432 433 434
    x = xcim->x - xcim->xhot;
    y = xcim->y - xcim->yhot;

L
Luca Barbato 已提交
435 436
    to_line   = FFMIN((y + xcim->height), (height + y_off));
    to_column = FFMIN((x + xcim->width),  (width  + x_off));
R
Roxis 已提交
437 438 439

    for (line = FFMAX(y, y_off); line < to_line; line++) {
        for (column = FFMAX(x, x_off); column < to_column; column++) {
L
Luca Barbato 已提交
440 441 442 443 444 445
            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);
446 447

            if (a == 255) {
L
Luca Barbato 已提交
448 449 450
                pix[image_addr + 0] = r;
                pix[image_addr + 1] = g;
                pix[image_addr + 2] = b;
451 452
            } else if (a) {
                /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
L
Luca Barbato 已提交
453 454 455
                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;
456
            }
457 458
        }
    }
R
Roxis 已提交
459 460 461

    XFree(xcim);
    xcim = NULL;
462 463
}

E
Edouard Gomez 已提交
464
/**
465
 * Read new data in the image structure.
E
Edouard Gomez 已提交
466 467
 *
 * @param dpy X11 display to grab from
468
 * @param d
E
Edouard Gomez 已提交
469 470 471 472
 * @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
473
 */
L
Luca Barbato 已提交
474
static int xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
475
{
476 477 478 479
    xGetImageReply rep;
    xGetImageReq *req;
    long nbytes;

L
Luca Barbato 已提交
480
    if (!image)
E
Edouard Gomez 已提交
481
        return 0;
482 483 484 485 486

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

    /* First set up the standard stuff in the request */
L
Luca Barbato 已提交
487 488 489 490 491
    req->drawable  = d;
    req->x         = x;
    req->y         = y;
    req->width     = image->width;
    req->height    = image->height;
492
    req->planeMask = (unsigned int)AllPlanes;
L
Luca Barbato 已提交
493
    req->format    = ZPixmap;
494

E
Edouard Gomez 已提交
495
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
496 497
        UnlockDisplay(dpy);
        SyncHandle();
E
Edouard Gomez 已提交
498
        return 0;
499 500 501 502 503 504 505
    }

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

    UnlockDisplay(dpy);
    SyncHandle();
E
Edouard Gomez 已提交
506
    return 1;
507 508
}

E
Edouard Gomez 已提交
509
/**
510
 * Grab a frame from x11 (public device demuxer API).
E
Edouard Gomez 已提交
511 512 513 514 515
 *
 * @param s1 Context from avformat core
 * @param pkt Packet holding the brabbed frame
 * @return frame size in bytes
 */
L
Luca Barbato 已提交
516
static int x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
517
{
518
    X11GrabContext *s = s1->priv_data;
L
Luca Barbato 已提交
519 520 521 522 523
    Display *dpy      = s->dpy;
    XImage *image     = s->image;
    int x_off         = s->x_off;
    int y_off         = s->y_off;
    int follow_mouse  = s->follow_mouse;
524 525
    int screen, pointer_x, pointer_y, _, same_screen = 1;
    Window w, root;
526 527 528 529
    int64_t curtime, delay;
    struct timespec ts;

    /* Calculate the time of the next frame */
530
    s->time_frame += INT64_C(1000000);
531 532

    /* wait based on the frame rate */
L
Luca Barbato 已提交
533
    for (;;) {
534
        curtime = av_gettime();
L
Luca Barbato 已提交
535
        delay   = s->time_frame * av_q2d(s->time_base) - curtime;
536
        if (delay <= 0) {
L
Luca Barbato 已提交
537
            if (delay < INT64_C(-1000000) * av_q2d(s->time_base))
538
                s->time_frame += INT64_C(1000000);
539 540
            break;
        }
L
Luca Barbato 已提交
541
        ts.tv_sec  = delay / 1000000;
542 543 544 545
        ts.tv_nsec = (delay % 1000000) * 1000;
        nanosleep(&ts, NULL);
    }

546 547 548
    av_init_packet(pkt);
    pkt->data = image->data;
    pkt->size = s->frame_size;
L
Luca Barbato 已提交
549
    pkt->pts  = curtime;
550 551 552 553 554 555 556 557 558 559
    if (s->palette_changed) {
        uint8_t *pal = av_packet_new_side_data(pkt, AV_PKT_DATA_PALETTE,
                                               AVPALETTE_SIZE);
        if (!pal) {
            av_log(s, AV_LOG_ERROR, "Cannot append palette to packet\n");
        } else {
            memcpy(pal, s->palette, AVPALETTE_SIZE);
            s->palette_changed = 0;
        }
    }
560

Y
Yu-Jie Lin 已提交
561
    screen = DefaultScreen(dpy);
L
Luca Barbato 已提交
562
    root   = RootWindow(dpy, screen);
563 564 565 566 567 568

    if (follow_mouse || s->draw_mouse)
        same_screen = XQueryPointer(dpy, root, &w, &w,
                                    &pointer_x, &pointer_y, &_, &_, &_);

    if (follow_mouse && same_screen) {
Y
Yu-Jie Lin 已提交
569 570 571 572 573 574
        int screen_w, screen_h;

        screen_w = DisplayWidth(dpy, screen);
        screen_h = DisplayHeight(dpy, screen);
        if (follow_mouse == -1) {
            // follow the mouse, put it at center of grabbing region
L
Luca Barbato 已提交
575
            x_off += pointer_x - s->width / 2 - x_off;
Y
Yu-Jie Lin 已提交
576 577 578 579
            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.
L
Luca Barbato 已提交
580
            if (pointer_x > x_off + s->width - follow_mouse)
Y
Yu-Jie Lin 已提交
581
                x_off += pointer_x - (x_off + s->width - follow_mouse);
L
Luca Barbato 已提交
582
            else if (pointer_x < x_off + follow_mouse)
Y
Yu-Jie Lin 已提交
583
                x_off -= (x_off + follow_mouse) - pointer_x;
L
Luca Barbato 已提交
584
            if (pointer_y > y_off + s->height - follow_mouse)
Y
Yu-Jie Lin 已提交
585
                y_off += pointer_y - (y_off + s->height - follow_mouse);
L
Luca Barbato 已提交
586
            else if (pointer_y < y_off + follow_mouse)
Y
Yu-Jie Lin 已提交
587 588 589
                y_off -= (y_off + follow_mouse) - pointer_y;
        }
        // adjust grabbing region position if it goes out of screen.
590 591
        s->x_off = x_off = av_clip(x_off, 0, screen_w - s->width);
        s->y_off = y_off = av_clip(y_off, 0, screen_h - s->height);
Y
Yu-Jie Lin 已提交
592 593 594 595 596 597 598

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

599
    if (s->show_region && same_screen) {
Y
Yu-Jie Lin 已提交
600
        if (s->region_win) {
601 602 603 604
            XEvent evt = { .type = NoEventMask };
            // Clean up the events, and do the initial draw or redraw.
            while (XCheckMaskEvent(dpy, ExposureMask | StructureNotifyMask,
                                   &evt))
L
Luca Barbato 已提交
605
                ;
Y
Yu-Jie Lin 已提交
606 607 608 609 610
            if (evt.type)
                x11grab_draw_region_win(s);
        } else {
            x11grab_region_win_init(s);
        }
Y
Yu-Jie Lin 已提交
611 612
    }

L
Luca Barbato 已提交
613 614 615
    if (s->use_shm) {
        if (!XShmGetImage(dpy, root, image, x_off, y_off, AllPlanes))
            av_log(s1, AV_LOG_INFO, "XShmGetImage() failed\n");
616
    } else {
L
Luca Barbato 已提交
617 618
        if (!xget_zpixmap(dpy, root, image, x_off, y_off))
            av_log(s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
619 620
    }

621
    if (s->draw_mouse && same_screen)
622
        paint_mouse_pointer(image, s1);
623 624

    return s->frame_size;
625 626
}

E
Edouard Gomez 已提交
627
/**
628
 * Close x11 frame grabber (public device demuxer API).
E
Edouard Gomez 已提交
629 630 631 632
 *
 * @param s1 Context from avformat core
 * @return 0 success, !0 failure
 */
L
Luca Barbato 已提交
633
static int x11grab_read_close(AVFormatContext *s1)
634
{
635
    X11GrabContext *x11grab = s1->priv_data;
636 637 638 639 640 641 642 643 644 645 646 647 648 649

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

L
Luca Barbato 已提交
650
    if (x11grab->region_win)
Y
Yu-Jie Lin 已提交
651 652
        XDestroyWindow(x11grab->dpy, x11grab->region_win);

653 654 655
    /* Free X11 display */
    XCloseDisplay(x11grab->dpy);
    return 0;
656 657
}

658
#define OFFSET(x) offsetof(X11GrabContext, x)
659 660
#define DEC AV_OPT_FLAG_DECODING_PARAM
static const AVOption options[] = {
C
Christian Hujer 已提交
661 662
    { "grab_x", "Initial x coordinate.", OFFSET(x_off), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, DEC },
    { "grab_y", "Initial y coordinate.", OFFSET(y_off), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, DEC },
663
    { "draw_mouse", "draw the mouse pointer", OFFSET(draw_mouse), AV_OPT_TYPE_INT, {.i64 = 1}, 0, 1, DEC },
664 665

    { "follow_mouse", "move the grabbing region when the mouse pointer reaches within specified amount of pixels to the edge of region",
666
      OFFSET(follow_mouse), AV_OPT_TYPE_INT, {.i64 = 0}, -1, INT_MAX, DEC, "follow_mouse" },
667
    { "centered",     "keep the mouse pointer at the center of grabbing region when following",
668
      0, AV_OPT_TYPE_CONST, {.i64 = -1}, INT_MIN, INT_MAX, DEC, "follow_mouse" },
669

670
    { "framerate",  "set video frame rate",      OFFSET(framerate),   AV_OPT_TYPE_VIDEO_RATE, {.str = "ntsc"}, 0, 0, DEC },
671
    { "show_region", "show the grabbing region", OFFSET(show_region), AV_OPT_TYPE_INT,        {.i64 = 0}, 0, 1, DEC },
672
    { "video_size",  "set video frame size",     OFFSET(width),       AV_OPT_TYPE_IMAGE_SIZE, {.str = "vga"}, 0, 0, DEC },
673
    { "use_shm",     "use MIT-SHM extension",    OFFSET(use_shm),     AV_OPT_TYPE_INT,        {.i64 = 1}, 0, 1, DEC },
674 675 676 677 678 679 680 681
    { NULL },
};

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

E
Edouard Gomez 已提交
685
/** x11 grabber device demuxer declaration */
686
AVInputFormat ff_x11grab_demuxer = {
687 688
    .name           = "x11grab",
    .long_name      = NULL_IF_CONFIG_SMALL("X11grab"),
689
    .priv_data_size = sizeof(X11GrabContext),
690 691 692 693 694
    .read_header    = x11grab_read_header,
    .read_packet    = x11grab_read_packet,
    .read_close     = x11grab_read_close,
    .flags          = AVFMT_NOFILE,
    .priv_class     = &x11_class,
695
};