Statistics
| Branch: | Revision:

ffmpeg / libavdevice / x11grab.c @ b0067549

History | View | Annotate | Download (16.5 KB)

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

    
32
/**
33
 * @file x11grab.c
34
 * X11 frame device demuxer by Clemens Fruhwirth <clemens@endorphin.org>
35
 * and Edouard Gomez <ed.gomez@free.fr>.
36
 */
37

    
38
#include "config.h"
39
#include "avformat.h"
40
#include <unistd.h>
41
#include <fcntl.h>
42
#include <sys/ioctl.h>
43
#ifdef HAVE_SYS_MMAN_H
44
#include <sys/mman.h>
45
#endif
46
#include <sys/time.h>
47
#define _LINUX_TIME_H 1
48
#include <time.h>
49
#include <X11/X.h>
50
#include <X11/Xlib.h>
51
#include <X11/Xlibint.h>
52
#include <X11/Xproto.h>
53
#include <X11/Xutil.h>
54
#include <sys/ipc.h>
55
#include <sys/shm.h>
56
#include <X11/extensions/XShm.h>
57

    
58
/**
59
 * X11 Device Demuxer context
60
 */
61
typedef struct x11_grab_s
62
{
63
    int frame_size;          /**< Size in bytes of a grabbed frame */
64
    AVRational time_base;    /**< Time base */
65
    int64_t time_frame;      /**< Current time */
66

    
67
    int height;              /**< Height of the grab frame */
68
    int width;               /**< Width of the grab frame */
69
    int x_off;               /**< Horizontal top-left corner coordinate */
70
    int y_off;               /**< Vertical top-left corner coordinate */
71

    
72
    Display *dpy;            /**< X11 display from which x11grab grabs frames */
73
    XImage *image;           /**< X11 image holding the grab */
74
    int use_shm;             /**< !0 when using XShm extension */
75
    XShmSegmentInfo shminfo; /**< When using XShm, keeps track of XShm infos */
76
    int mouse_warning_shown;
77
} x11_grab_t;
78

    
79
/**
80
 * Initializes the x11 grab device demuxer (public device demuxer API).
81
 *
82
 * @param s1 Context from avformat core
83
 * @param ap Parameters from avformat core
84
 * @return <ul>
85
 *          <li>AVERROR(ENOMEM) no memory left</li>
86
 *          <li>AVERROR(EIO) other failure case</li>
87
 *          <li>0 success</li>
88
 *         </ul>
89
 */
90
static int
91
x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
92
{
93
    x11_grab_t *x11grab = s1->priv_data;
94
    Display *dpy;
95
    AVStream *st = NULL;
96
    int input_pixfmt;
97
    XImage *image;
98
    int x_off = 0;
99
    int y_off = 0;
100
    int use_shm;
101
    char *param, *offset;
102

    
103
    param = av_strdup(s1->filename);
104
    offset = strchr(param, '+');
105
    if (offset) {
106
        sscanf(offset, "%d,%d", &x_off, &y_off);
107
        *offset= 0;
108
    }
109

    
110
    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, ap->width, ap->height);
111

    
112
    dpy = XOpenDisplay(param);
113
    if(!dpy) {
114
        av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
115
        return AVERROR(EIO);
116
    }
117

    
118
    if (!ap || ap->width <= 0 || ap->height <= 0 || ap->time_base.den <= 0) {
119
        av_log(s1, AV_LOG_ERROR, "AVParameters don't have video size and/or rate. Use -s and -r.\n");
120
        return AVERROR(EIO);
121
    }
122

    
123
    st = av_new_stream(s1, 0);
124
    if (!st) {
125
        return AVERROR(ENOMEM);
126
    }
127
    av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
128

    
129
    use_shm = XShmQueryExtension(dpy);
130
    av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
131

    
132
    if(use_shm) {
133
        int scr = XDefaultScreen(dpy);
134
        image = XShmCreateImage(dpy,
135
                                DefaultVisual(dpy, scr),
136
                                DefaultDepth(dpy, scr),
137
                                ZPixmap,
138
                                NULL,
139
                                &x11grab->shminfo,
140
                                ap->width, ap->height);
141
        x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
142
                                        image->bytes_per_line * image->height,
143
                                        IPC_CREAT|0777);
144
        if (x11grab->shminfo.shmid == -1) {
145
            av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
146
            return AVERROR(ENOMEM);
147
        }
148
        x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
149
        x11grab->shminfo.readOnly = False;
150

    
151
        if (!XShmAttach(dpy, &x11grab->shminfo)) {
152
            av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
153
            /* needs some better error subroutine :) */
154
            return AVERROR(EIO);
155
        }
156
    } else {
157
        image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
158
                          x_off,y_off,
159
                          ap->width,ap->height,
160
                          AllPlanes, ZPixmap);
161
    }
162

    
163
    switch (image->bits_per_pixel) {
164
    case 8:
165
        av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
166
        input_pixfmt = PIX_FMT_PAL8;
167
        break;
168
    case 16:
169
        if (       image->red_mask   == 0xf800 &&
170
                   image->green_mask == 0x07e0 &&
171
                   image->blue_mask  == 0x001f ) {
172
            av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
173
            input_pixfmt = PIX_FMT_RGB565;
174
        } else if (image->red_mask   == 0x7c00 &&
175
                   image->green_mask == 0x03e0 &&
176
                   image->blue_mask  == 0x001f ) {
177
            av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
178
            input_pixfmt = PIX_FMT_RGB555;
179
        } else {
180
            av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
181
            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);
182
            return AVERROR(EIO);
183
        }
184
        break;
185
    case 24:
186
        if (        image->red_mask   == 0xff0000 &&
187
                    image->green_mask == 0x00ff00 &&
188
                    image->blue_mask  == 0x0000ff ) {
189
            input_pixfmt = PIX_FMT_BGR24;
190
        } else if ( image->red_mask   == 0x0000ff &&
191
                    image->green_mask == 0x00ff00 &&
192
                    image->blue_mask  == 0xff0000 ) {
193
            input_pixfmt = PIX_FMT_RGB24;
194
        } else {
195
            av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
196
            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);
197
            return AVERROR(EIO);
198
        }
199
        break;
200
    case 32:
201
#if 0
202
        GetColorInfo (image, &c_info);
203
        if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) {
204
            /* byte order is relevant here, not endianness
205
             * endianness is handled by avcodec, but atm no such thing
206
             * as having ABGR, instead of ARGB in a word. Since we
207
             * need this for Solaris/SPARC, but need to do the conversion
208
             * for every frame we do it outside of this loop, cf. below
209
             * this matches both ARGB32 and ABGR32 */
210
            input_pixfmt = PIX_FMT_ARGB32;
211
        }  else {
212
            av_log(s1, AV_LOG_ERROR,"image depth %i not supported ... aborting\n", image->bits_per_pixel);
213
            return AVERROR(EIO);
214
        }
215
#endif
216
        input_pixfmt = PIX_FMT_RGB32;
217
        break;
218
    default:
219
        av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
220
        return -1;
221
    }
222

    
223
    x11grab->frame_size = ap->width * ap->height * image->bits_per_pixel/8;
224
    x11grab->dpy = dpy;
225
    x11grab->width = ap->width;
226
    x11grab->height = ap->height;
227
    x11grab->time_base  = ap->time_base;
228
    x11grab->time_frame = av_gettime() / av_q2d(ap->time_base);
229
    x11grab->x_off = x_off;
230
    x11grab->y_off = y_off;
231
    x11grab->image = image;
232
    x11grab->use_shm = use_shm;
233
    x11grab->mouse_warning_shown = 0;
234

    
235
    st->codec->codec_type = CODEC_TYPE_VIDEO;
236
    st->codec->codec_id = CODEC_ID_RAWVIDEO;
237
    st->codec->width = ap->width;
238
    st->codec->height = ap->height;
239
    st->codec->pix_fmt = input_pixfmt;
240
    st->codec->time_base = ap->time_base;
241
    st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(ap->time_base) * 8;
242

    
243
    return 0;
244
}
245

    
246
/**
247
 * Get pointer coordinates from X11.
248
 *
249
 * @param x Integer where horizontal coordinate will be returned
250
 * @param y Integer where vertical coordinate will be returned
251
 * @param dpy X11 display from where pointer coordinates are retrieved
252
 * @param s1 Context used for logging errors if necessary
253
 */
254
static void
255
get_pointer_coordinates(int *x, int *y, Display *dpy, AVFormatContext *s1)
256
{
257
    Window mrootwindow, childwindow;
258
    int dummy;
259

    
260
    mrootwindow = DefaultRootWindow(dpy);
261

    
262
    if (XQueryPointer(dpy, mrootwindow, &mrootwindow, &childwindow,
263
                      x, y, &dummy, &dummy, (unsigned int*)&dummy)) {
264
    } else {
265
        x11_grab_t *s = s1->priv_data;
266
        if (!s->mouse_warning_shown) {
267
            av_log(s1, AV_LOG_INFO, "couldn't find mouse pointer\n");
268
            s->mouse_warning_shown = 1;
269
        }
270
        *x = -1;
271
        *y = -1;
272
    }
273
}
274

    
275
/**
276
 * Mouse painting helper function that applies an 'and' and 'or' mask pair to
277
 * '*dst' pixel. It actually draws a mouse pointer pixel to grabbed frame.
278
 *
279
 * @param dst Destination pixel
280
 * @param and Part of the mask that must be applied using a bitwise 'and'
281
 *            operator
282
 * @param or  Part of the mask that must be applied using a bitwise 'or'
283
 *            operator
284
 * @param bits_per_pixel Bits per pixel used in the grabbed image
285
 */
286
static void inline
287
apply_masks(uint8_t *dst, int and, int or, int bits_per_pixel)
288
{
289
    switch (bits_per_pixel) {
290
    case 32:
291
        *(uint32_t*)dst = (*(uint32_t*)dst & and) | or;
292
        break;
293
    case 16:
294
        *(uint16_t*)dst = (*(uint16_t*)dst & and) | or;
295
        break;
296
    case 8:
297
        *dst = !!or;
298
        break;
299
    }
300
}
301

    
302
/**
303
 * Paints a mouse pointer in an X11 image.
304
 *
305
 * @param image image to paint the mouse pointer to
306
 * @param s context used to retrieve original grabbing rectangle
307
 *          coordinates
308
 * @param x Mouse pointer coordinate
309
 * @param y Mouse pointer coordinate
310
 */
311
static void
312
paint_mouse_pointer(XImage *image, x11_grab_t *s, int x, int y)
313
{
314
    /* 16x20x1bpp bitmap for the black channel of the mouse pointer */
315
    static const uint16_t const mousePointerBlack[] =
316
        {
317
            0x0000, 0x0003, 0x0005, 0x0009, 0x0011,
318
            0x0021, 0x0041, 0x0081, 0x0101, 0x0201,
319
            0x03c1, 0x0049, 0x0095, 0x0093, 0x0120,
320
            0x0120, 0x0240, 0x0240, 0x0380, 0x0000
321
        };
322

    
323
    /* 16x20x1bpp bitmap for the white channel of the mouse pointer */
324
    static const uint16_t const mousePointerWhite[] =
325
        {
326
            0x0000, 0x0000, 0x0002, 0x0006, 0x000e,
327
            0x001e, 0x003e, 0x007e, 0x00fe, 0x01fe,
328
            0x003e, 0x0036, 0x0062, 0x0060, 0x00c0,
329
            0x00c0, 0x0180, 0x0180, 0x0000, 0x0000
330
        };
331

    
332
    int x_off = s->x_off;
333
    int y_off = s->y_off;
334
    int width = s->width;
335
    int height = s->height;
336

    
337
    if (   x - x_off >= 0 && x < width + x_off
338
        && y - y_off >= 0 && y < height + y_off) {
339
        uint8_t *im_data = (uint8_t*)image->data;
340
        int bytes_per_pixel;
341
        int line;
342
        int masks;
343

    
344
        /* Select correct masks and pixel size */
345
        if (image->bits_per_pixel == 8) {
346
            masks = 1;
347
        } else {
348
            masks = (image->red_mask|image->green_mask|image->blue_mask);
349
        }
350
        bytes_per_pixel = image->bits_per_pixel>>3;
351

    
352
        /* Shift to right line */
353
        im_data += image->bytes_per_line * (y - y_off);
354
        /* Shift to right pixel in the line */
355
        im_data += bytes_per_pixel * (x - x_off);
356

    
357
        /* Draw the cursor - proper loop */
358
        for (line = 0; line < FFMIN(20, (y_off + height) - y); line++) {
359
            uint8_t *cursor = im_data;
360
            int column;
361
            uint16_t bm_b;
362
            uint16_t bm_w;
363

    
364
            bm_b = mousePointerBlack[line];
365
            bm_w = mousePointerWhite[line];
366

    
367
            for (column = 0; column < FFMIN(16, (x_off + width) - x); column++) {
368
                apply_masks(cursor, ~(masks*(bm_b&1)), masks*(bm_w&1),
369
                            image->bits_per_pixel);
370
                cursor += bytes_per_pixel;
371
                bm_b >>= 1;
372
                bm_w >>= 1;
373
            }
374
            im_data += image->bytes_per_line;
375
        }
376
    }
377
}
378

    
379

    
380
/**
381
 * Reads new data in the image structure.
382
 *
383
 * @param dpy X11 display to grab from
384
 * @param d
385
 * @param image Image where the grab will be put
386
 * @param x Top-Left grabbing rectangle horizontal coordinate
387
 * @param y Top-Left grabbing rectangle vertical coordinate
388
 * @return 0 if error, !0 if successful
389
 */
390
static int
391
xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
392
{
393
    xGetImageReply rep;
394
    xGetImageReq *req;
395
    long nbytes;
396

    
397
    if (!image) {
398
        return 0;
399
    }
400

    
401
    LockDisplay(dpy);
402
    GetReq(GetImage, req);
403

    
404
    /* First set up the standard stuff in the request */
405
    req->drawable = d;
406
    req->x = x;
407
    req->y = y;
408
    req->width = image->width;
409
    req->height = image->height;
410
    req->planeMask = (unsigned int)AllPlanes;
411
    req->format = ZPixmap;
412

    
413
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
414
        UnlockDisplay(dpy);
415
        SyncHandle();
416
        return 0;
417
    }
418

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

    
422
    UnlockDisplay(dpy);
423
    SyncHandle();
424
    return 1;
425
}
426

    
427
/**
428
 * Grabs a frame from x11 (public device demuxer API).
429
 *
430
 * @param s1 Context from avformat core
431
 * @param pkt Packet holding the brabbed frame
432
 * @return frame size in bytes
433
 */
434
static int
435
x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
436
{
437
    x11_grab_t *s = s1->priv_data;
438
    Display *dpy = s->dpy;
439
    XImage *image = s->image;
440
    int x_off = s->x_off;
441
    int y_off = s->y_off;
442

    
443
    int64_t curtime, delay;
444
    struct timespec ts;
445

    
446
    /* Calculate the time of the next frame */
447
    s->time_frame += INT64_C(1000000);
448

    
449
    /* wait based on the frame rate */
450
    for(;;) {
451
        curtime = av_gettime();
452
        delay = s->time_frame * av_q2d(s->time_base) - curtime;
453
        if (delay <= 0) {
454
            if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
455
                s->time_frame += INT64_C(1000000);
456
            }
457
            break;
458
        }
459
        ts.tv_sec = delay / 1000000;
460
        ts.tv_nsec = (delay % 1000000) * 1000;
461
        nanosleep(&ts, NULL);
462
    }
463

    
464
    if (av_new_packet(pkt, s->frame_size) < 0) {
465
        return AVERROR(EIO);
466
    }
467

    
468
    pkt->pts = curtime;
469

    
470
    if(s->use_shm) {
471
        if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) {
472
            av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
473
        }
474
    } else {
475
        if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) {
476
            av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
477
        }
478
    }
479

    
480
    {
481
        int pointer_x, pointer_y;
482
        get_pointer_coordinates(&pointer_x, &pointer_y, dpy, s1);
483
        paint_mouse_pointer(image, s, pointer_x, pointer_y);
484
    }
485

    
486

    
487
    /* XXX: avoid memcpy */
488
    memcpy(pkt->data, image->data, s->frame_size);
489
    return s->frame_size;
490
}
491

    
492
/**
493
 * Closes x11 frame grabber (public device demuxer API).
494
 *
495
 * @param s1 Context from avformat core
496
 * @return 0 success, !0 failure
497
 */
498
static int
499
x11grab_read_close(AVFormatContext *s1)
500
{
501
    x11_grab_t *x11grab = s1->priv_data;
502

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

    
510
    /* Destroy X11 image */
511
    if (x11grab->image) {
512
        XDestroyImage(x11grab->image);
513
        x11grab->image = NULL;
514
    }
515

    
516
    /* Free X11 display */
517
    XCloseDisplay(x11grab->dpy);
518
    return 0;
519
}
520

    
521
/** x11 grabber device demuxer declaration */
522
AVInputFormat x11_grab_device_demuxer =
523
{
524
    "x11grab",
525
    "X11grab",
526
    sizeof(x11_grab_t),
527
    NULL,
528
    x11grab_read_header,
529
    x11grab_read_packet,
530
    x11grab_read_close,
531
    .flags = AVFMT_NOFILE,
532
};