Statistics
| Branch: | Revision:

ffmpeg / libavformat / x11grab.c @ 0d9f8633

History | View | Annotate | Download (16.7 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 along
28
 * with FFmpeg; if not, write to the Free Software Foundation, Inc.,
29
 * 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 "avformat.h"
39
#include <unistd.h>
40
#include <fcntl.h>
41
#include <sys/ioctl.h>
42
#include <sys/mman.h>
43
#include <sys/time.h>
44
#define _LINUX_TIME_H 1
45
#include <time.h>
46
#include <X11/X.h>
47
#include <X11/Xlib.h>
48
#include <X11/Xlibint.h>
49
#include <X11/Xproto.h>
50
#include <X11/Xutil.h>
51
#include <sys/ipc.h>
52
#include <sys/shm.h>
53
#include <X11/extensions/XShm.h>
54

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

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

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

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

    
100
    if (!ap->device) {
101
        av_log(s1, AV_LOG_ERROR, "AVParameters don't specify any device. Use -vd.\n");
102
        return AVERROR_IO;
103
    }
104

    
105
    param = strchr(ap->device, ':');
106
    if (!param) {
107
        av_free(st);
108
        return AVERROR_IO;
109
    }
110

    
111
    param = av_strdup(param);
112
    offset = strchr(param, '+');
113
    if (offset) {
114
        sscanf(offset, "%d,%d", &x_off, &y_off);
115
        *offset= 0;
116
    }
117

    
118
    av_log(s1, AV_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n", ap->device, param, x_off, y_off, ap->width, ap->height);
119

    
120
    dpy = XOpenDisplay(param);
121
    if(!dpy) {
122
        av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
123
        return AVERROR_IO;
124
    }
125

    
126
    if (!ap || ap->width <= 0 || ap->height <= 0 || ap->time_base.den <= 0) {
127
        av_log(s1, AV_LOG_ERROR, "AVParameters don't have any video size. Use -s.\n");
128
        return AVERROR_IO;
129
    }
130

    
131
    st = av_new_stream(s1, 0);
132
    if (!st) {
133
        return -ENOMEM;
134
    }
135
    av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
136

    
137
    use_shm = XShmQueryExtension(dpy);
138
    av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
139

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

    
159
        if (!XShmAttach(dpy, &x11grab->shminfo)) {
160
            av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
161
            /* needs some better error subroutine :) */
162
            return AVERROR_IO;
163
        }
164
    } else {
165
        image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
166
                          x_off,y_off,
167
                          ap->width,ap->height,
168
                          AllPlanes, ZPixmap);
169
    }
170

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

    
231
    x11grab->frame_size = ap->width * ap->height * image->bits_per_pixel/8;
232
    x11grab->dpy = dpy;
233
    x11grab->width = ap->width;
234
    x11grab->height = ap->height;
235
    x11grab->time_base  = ap->time_base;
236
    x11grab->time_frame = av_gettime() / av_q2d(ap->time_base);
237
    x11grab->x_off = x_off;
238
    x11grab->y_off = y_off;
239
    x11grab->image = image;
240
    x11grab->use_shm = use_shm;
241
    x11grab->mouse_warning_shown = 0;
242

    
243
    st->codec->codec_type = CODEC_TYPE_VIDEO;
244
    st->codec->codec_id = CODEC_ID_RAWVIDEO;
245
    st->codec->width = ap->width;
246
    st->codec->height = ap->height;
247
    st->codec->pix_fmt = input_pixfmt;
248
    st->codec->time_base = ap->time_base;
249
    st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(ap->time_base) * 8;
250

    
251
    return 0;
252
}
253

    
254
/**
255
 * Get pointer coordinates from X11.
256
 *
257
 * @param x Integer where horizontal coordinate will be returned
258
 * @param y Integer where vertical coordinate will be returned
259
 * @param dpy X11 display from where pointer coordinates are retrieved
260
 * @param s1 Context used for logging errors if necessary
261
 */
262
static void
263
get_pointer_coordinates(int *x, int *y, Display *dpy, AVFormatContext *s1)
264
{
265
    Window mrootwindow, childwindow;
266
    int dummy;
267

    
268
    mrootwindow = DefaultRootWindow(dpy);
269

    
270
    if (XQueryPointer(dpy, mrootwindow, &mrootwindow, &childwindow,
271
                      x, y, &dummy, &dummy, (unsigned int*)&dummy)) {
272
    } else {
273
        x11_grab_t *s = s1->priv_data;
274
        if (!s->mouse_warning_shown) {
275
            av_log(s1, AV_LOG_INFO, "couldn't find mouse pointer\n");
276
            s->mouse_warning_shown = 1;
277
        }
278
        *x = -1;
279
        *y = -1;
280
    }
281
}
282

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

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

    
331
    /* 16x20x1bpp bitmap for the white channel of the mouse pointer */
332
    static const uint16_t const mousePointerWhite[] =
333
        {
334
            0x0000, 0x0000, 0x0002, 0x0006, 0x000e,
335
            0x001e, 0x003e, 0x007e, 0x00fe, 0x01fe,
336
            0x003e, 0x0036, 0x0062, 0x0060, 0x00c0,
337
            0x00c0, 0x0180, 0x0180, 0x0000, 0x0000
338
        };
339

    
340
    int x_off = s->x_off;
341
    int y_off = s->y_off;
342
    int width = s->width;
343
    int height = s->height;
344

    
345
    if (   x - x_off >= 0 && x < width + x_off
346
        && y - y_off >= 0 && y < height + y_off) {
347
        uint8_t *im_data = (uint8_t*)image->data;
348
        int bytes_per_pixel;
349
        int line;
350
        int masks;
351

    
352
        /* Select correct masks and pixel size */
353
        if (image->bits_per_pixel == 8) {
354
            masks = 1;
355
        } else {
356
            masks = (image->red_mask|image->green_mask|image->blue_mask);
357
        }
358
        bytes_per_pixel = image->bits_per_pixel>>3;
359

    
360
        /* Shift to right line */
361
        im_data += image->bytes_per_line * (y - y_off);
362
        /* Shift to right pixel in the line */
363
        im_data += bytes_per_pixel * (x - x_off);
364

    
365
        /* Draw the cursor - proper loop */
366
        for (line = 0; line < FFMIN(20, (y_off + height) - y); line++) {
367
            uint8_t *cursor = im_data;
368
            int column;
369
            uint16_t bm_b;
370
            uint16_t bm_w;
371

    
372
            bm_b = mousePointerBlack[line];
373
            bm_w = mousePointerWhite[line];
374

    
375
            for (column = 0; column < FFMIN(16, (x_off + width) - x); column++) {
376
                apply_masks(cursor, ~(masks*(bm_b&1)), masks*(bm_w&1),
377
                            image->bits_per_pixel);
378
                cursor += bytes_per_pixel;
379
                bm_b >>= 1;
380
                bm_w >>= 1;
381
            }
382
            im_data += image->bytes_per_line;
383
        }
384
    }
385
}
386

    
387

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

    
405
    if (!image) {
406
        return 0;
407
    }
408

    
409
    LockDisplay(dpy);
410
    GetReq(GetImage, req);
411

    
412
    /* First set up the standard stuff in the request */
413
    req->drawable = d;
414
    req->x = x;
415
    req->y = y;
416
    req->width = image->width;
417
    req->height = image->height;
418
    req->planeMask = (unsigned int)AllPlanes;
419
    req->format = ZPixmap;
420

    
421
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
422
        UnlockDisplay(dpy);
423
        SyncHandle();
424
        return 0;
425
    }
426

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

    
430
    UnlockDisplay(dpy);
431
    SyncHandle();
432
    return 1;
433
}
434

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

    
451
    int64_t curtime, delay;
452
    struct timespec ts;
453

    
454
    /* Calculate the time of the next frame */
455
    s->time_frame += INT64_C(1000000);
456

    
457
    /* wait based on the frame rate */
458
    for(;;) {
459
        curtime = av_gettime();
460
        delay = s->time_frame * av_q2d(s->time_base) - curtime;
461
        if (delay <= 0) {
462
            if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
463
                s->time_frame += INT64_C(1000000);
464
            }
465
            break;
466
        }
467
        ts.tv_sec = delay / 1000000;
468
        ts.tv_nsec = (delay % 1000000) * 1000;
469
        nanosleep(&ts, NULL);
470
    }
471

    
472
    if (av_new_packet(pkt, s->frame_size) < 0) {
473
        return AVERROR_IO;
474
    }
475

    
476
    pkt->pts = curtime;
477

    
478
    if(s->use_shm) {
479
        if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) {
480
            av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
481
        }
482
    } else {
483
        if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) {
484
            av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
485
        }
486
    }
487

    
488
    {
489
        int pointer_x, pointer_y;
490
        get_pointer_coordinates(&pointer_x, &pointer_y, dpy, s1);
491
        paint_mouse_pointer(image, s, pointer_x, pointer_y);
492
    }
493

    
494

    
495
    /* XXX: avoid memcpy */
496
    memcpy(pkt->data, image->data, s->frame_size);
497
    return s->frame_size;
498
}
499

    
500
/**
501
 * Closes x11 frame grabber (public device demuxer API).
502
 *
503
 * @param s1 Context from avformat core
504
 * @return 0 success, !0 failure
505
 */
506
static int
507
x11grab_read_close(AVFormatContext *s1)
508
{
509
    x11_grab_t *x11grab = s1->priv_data;
510

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

    
518
    /* Destroy X11 image */
519
    if (x11grab->image) {
520
        XDestroyImage(x11grab->image);
521
        x11grab->image = NULL;
522
    }
523

    
524
    /* Free X11 display */
525
    XCloseDisplay(x11grab->dpy);
526
    return 0;
527
}
528

    
529
/** x11 grabber device demuxer declaration */
530
AVInputFormat x11_grab_device_demuxer =
531
{
532
    "x11grab",
533
    "X11grab",
534
    sizeof(x11_grab_t),
535
    NULL,
536
    x11grab_read_header,
537
    x11grab_read_packet,
538
    x11grab_read_close,
539
    .flags = AVFMT_NOFILE,
540
};