~ubuntu-branches/debian/sid/mplayer/sid

« back to all changes in this revision

Viewing changes to libavformat/x11grab.c

  • Committer: Bazaar Package Importer
  • Author(s): A Mennucc1
  • Date: 2009-03-23 10:05:45 UTC
  • mfrom: (4.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20090323100545-x8h79obawnnte7kk
Tags: 1.0~rc2+svn20090303-5
debian/control : move docbook-xml,docbook-xsl,xsltproc from 
Build-Depends-Indep to Build-Depends, since they are needed to run
configure

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 "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>AVERROR(ENOMEM) no memory left</li>
83
 
 *          <li>AVERROR(EIO) 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
 
    param = av_strdup(s1->filename);
101
 
    offset = strchr(param, '+');
102
 
    if (offset) {
103
 
        sscanf(offset, "%d,%d", &x_off, &y_off);
104
 
        *offset= 0;
105
 
    }
106
 
 
107
 
    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);
108
 
 
109
 
    dpy = XOpenDisplay(param);
110
 
    if(!dpy) {
111
 
        av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
112
 
        return AVERROR(EIO);
113
 
    }
114
 
 
115
 
    if (!ap || ap->width <= 0 || ap->height <= 0 || ap->time_base.den <= 0) {
116
 
        av_log(s1, AV_LOG_ERROR, "AVParameters don't have any video size. Use -s.\n");
117
 
        return AVERROR(EIO);
118
 
    }
119
 
 
120
 
    st = av_new_stream(s1, 0);
121
 
    if (!st) {
122
 
        return AVERROR(ENOMEM);
123
 
    }
124
 
    av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */
125
 
 
126
 
    use_shm = XShmQueryExtension(dpy);
127
 
    av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
128
 
 
129
 
    if(use_shm) {
130
 
        int scr = XDefaultScreen(dpy);
131
 
        image = XShmCreateImage(dpy,
132
 
                                DefaultVisual(dpy, scr),
133
 
                                DefaultDepth(dpy, scr),
134
 
                                ZPixmap,
135
 
                                NULL,
136
 
                                &x11grab->shminfo,
137
 
                                ap->width, ap->height);
138
 
        x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
139
 
                                        image->bytes_per_line * image->height,
140
 
                                        IPC_CREAT|0777);
141
 
        if (x11grab->shminfo.shmid == -1) {
142
 
            av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
143
 
            return AVERROR(ENOMEM);
144
 
        }
145
 
        x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
146
 
        x11grab->shminfo.readOnly = False;
147
 
 
148
 
        if (!XShmAttach(dpy, &x11grab->shminfo)) {
149
 
            av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
150
 
            /* needs some better error subroutine :) */
151
 
            return AVERROR(EIO);
152
 
        }
153
 
    } else {
154
 
        image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
155
 
                          x_off,y_off,
156
 
                          ap->width,ap->height,
157
 
                          AllPlanes, ZPixmap);
158
 
    }
159
 
 
160
 
    switch (image->bits_per_pixel) {
161
 
    case 8:
162
 
        av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
163
 
        input_pixfmt = PIX_FMT_PAL8;
164
 
        break;
165
 
    case 16:
166
 
        if (       image->red_mask   == 0xf800 &&
167
 
                   image->green_mask == 0x07e0 &&
168
 
                   image->blue_mask  == 0x001f ) {
169
 
            av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
170
 
            input_pixfmt = PIX_FMT_RGB565;
171
 
        } else if (image->red_mask   == 0x7c00 &&
172
 
                   image->green_mask == 0x03e0 &&
173
 
                   image->blue_mask  == 0x001f ) {
174
 
            av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
175
 
            input_pixfmt = PIX_FMT_RGB555;
176
 
        } else {
177
 
            av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
178
 
            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);
179
 
            return AVERROR(EIO);
180
 
        }
181
 
        break;
182
 
    case 24:
183
 
        if (        image->red_mask   == 0xff0000 &&
184
 
                    image->green_mask == 0x00ff00 &&
185
 
                    image->blue_mask  == 0x0000ff ) {
186
 
            input_pixfmt = PIX_FMT_BGR24;
187
 
        } else if ( image->red_mask   == 0x0000ff &&
188
 
                    image->green_mask == 0x00ff00 &&
189
 
                    image->blue_mask  == 0xff0000 ) {
190
 
            input_pixfmt = PIX_FMT_RGB24;
191
 
        } else {
192
 
            av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
193
 
            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);
194
 
            return AVERROR(EIO);
195
 
        }
196
 
        break;
197
 
    case 32:
198
 
#if 0
199
 
        GetColorInfo (image, &c_info);
200
 
        if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) {
201
 
            /* byte order is relevant here, not endianness
202
 
             * endianness is handled by avcodec, but atm no such thing
203
 
             * as having ABGR, instead of ARGB in a word. Since we
204
 
             * need this for Solaris/SPARC, but need to do the conversion
205
 
             * for every frame we do it outside of this loop, cf. below
206
 
             * this matches both ARGB32 and ABGR32 */
207
 
            input_pixfmt = PIX_FMT_ARGB32;
208
 
        }  else {
209
 
            av_log(s1, AV_LOG_ERROR,"image depth %i not supported ... aborting\n", image->bits_per_pixel);
210
 
            return AVERROR(EIO);
211
 
        }
212
 
#endif
213
 
        input_pixfmt = PIX_FMT_RGB32;
214
 
        break;
215
 
    default:
216
 
        av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
217
 
        return -1;
218
 
    }
219
 
 
220
 
    x11grab->frame_size = ap->width * ap->height * image->bits_per_pixel/8;
221
 
    x11grab->dpy = dpy;
222
 
    x11grab->width = ap->width;
223
 
    x11grab->height = ap->height;
224
 
    x11grab->time_base  = ap->time_base;
225
 
    x11grab->time_frame = av_gettime() / av_q2d(ap->time_base);
226
 
    x11grab->x_off = x_off;
227
 
    x11grab->y_off = y_off;
228
 
    x11grab->image = image;
229
 
    x11grab->use_shm = use_shm;
230
 
    x11grab->mouse_warning_shown = 0;
231
 
 
232
 
    st->codec->codec_type = CODEC_TYPE_VIDEO;
233
 
    st->codec->codec_id = CODEC_ID_RAWVIDEO;
234
 
    st->codec->width = ap->width;
235
 
    st->codec->height = ap->height;
236
 
    st->codec->pix_fmt = input_pixfmt;
237
 
    st->codec->time_base = ap->time_base;
238
 
    st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(ap->time_base) * 8;
239
 
 
240
 
    return 0;
241
 
}
242
 
 
243
 
/**
244
 
 * Get pointer coordinates from X11.
245
 
 *
246
 
 * @param x Integer where horizontal coordinate will be returned
247
 
 * @param y Integer where vertical coordinate will be returned
248
 
 * @param dpy X11 display from where pointer coordinates are retrieved
249
 
 * @param s1 Context used for logging errors if necessary
250
 
 */
251
 
static void
252
 
get_pointer_coordinates(int *x, int *y, Display *dpy, AVFormatContext *s1)
253
 
{
254
 
    Window mrootwindow, childwindow;
255
 
    int dummy;
256
 
 
257
 
    mrootwindow = DefaultRootWindow(dpy);
258
 
 
259
 
    if (XQueryPointer(dpy, mrootwindow, &mrootwindow, &childwindow,
260
 
                      x, y, &dummy, &dummy, (unsigned int*)&dummy)) {
261
 
    } else {
262
 
        x11_grab_t *s = s1->priv_data;
263
 
        if (!s->mouse_warning_shown) {
264
 
            av_log(s1, AV_LOG_INFO, "couldn't find mouse pointer\n");
265
 
            s->mouse_warning_shown = 1;
266
 
        }
267
 
        *x = -1;
268
 
        *y = -1;
269
 
    }
270
 
}
271
 
 
272
 
/**
273
 
 * Mouse painting helper function that applies an 'and' and 'or' mask pair to
274
 
 * '*dst' pixel. It actually draws a mouse pointer pixel to grabbed frame.
275
 
 *
276
 
 * @param dst Destination pixel
277
 
 * @param and Part of the mask that must be applied using a bitwise 'and'
278
 
 *            operator
279
 
 * @param or  Part of the mask that must be applied using a bitwise 'or'
280
 
 *            operator
281
 
 * @param bits_per_pixel Bits per pixel used in the grabbed image
282
 
 */
283
 
static void inline
284
 
apply_masks(uint8_t *dst, int and, int or, int bits_per_pixel)
285
 
{
286
 
    switch (bits_per_pixel) {
287
 
    case 32:
288
 
        *(uint32_t*)dst = (*(uint32_t*)dst & and) | or;
289
 
        break;
290
 
    case 16:
291
 
        *(uint16_t*)dst = (*(uint16_t*)dst & and) | or;
292
 
        break;
293
 
    case 8:
294
 
        *dst = !!or;
295
 
        break;
296
 
    }
297
 
}
298
 
 
299
 
/**
300
 
 * Paints a mouse pointer in an X11 image.
301
 
 *
302
 
 * @param image Image where to paint the mouse pointer
303
 
 * @param s context used to retrieve original grabbing rectangle
304
 
 *          coordinates
305
 
 * @param x Mouse pointer coordinate
306
 
 * @param y Mouse pointer coordinate
307
 
 */
308
 
static void
309
 
paint_mouse_pointer(XImage *image, x11_grab_t *s, int x, int y)
310
 
{
311
 
    /* 16x20x1bpp bitmap for the black channel of the mouse pointer */
312
 
    static const uint16_t const mousePointerBlack[] =
313
 
        {
314
 
            0x0000, 0x0003, 0x0005, 0x0009, 0x0011,
315
 
            0x0021, 0x0041, 0x0081, 0x0101, 0x0201,
316
 
            0x03c1, 0x0049, 0x0095, 0x0093, 0x0120,
317
 
            0x0120, 0x0240, 0x0240, 0x0380, 0x0000
318
 
        };
319
 
 
320
 
    /* 16x20x1bpp bitmap for the white channel of the mouse pointer */
321
 
    static const uint16_t const mousePointerWhite[] =
322
 
        {
323
 
            0x0000, 0x0000, 0x0002, 0x0006, 0x000e,
324
 
            0x001e, 0x003e, 0x007e, 0x00fe, 0x01fe,
325
 
            0x003e, 0x0036, 0x0062, 0x0060, 0x00c0,
326
 
            0x00c0, 0x0180, 0x0180, 0x0000, 0x0000
327
 
        };
328
 
 
329
 
    int x_off = s->x_off;
330
 
    int y_off = s->y_off;
331
 
    int width = s->width;
332
 
    int height = s->height;
333
 
 
334
 
    if (   x - x_off >= 0 && x < width + x_off
335
 
        && y - y_off >= 0 && y < height + y_off) {
336
 
        uint8_t *im_data = (uint8_t*)image->data;
337
 
        int bytes_per_pixel;
338
 
        int line;
339
 
        int masks;
340
 
 
341
 
        /* Select correct masks and pixel size */
342
 
        if (image->bits_per_pixel == 8) {
343
 
            masks = 1;
344
 
        } else {
345
 
            masks = (image->red_mask|image->green_mask|image->blue_mask);
346
 
        }
347
 
        bytes_per_pixel = image->bits_per_pixel>>3;
348
 
 
349
 
        /* Shift to right line */
350
 
        im_data += image->bytes_per_line * (y - y_off);
351
 
        /* Shift to right pixel in the line */
352
 
        im_data += bytes_per_pixel * (x - x_off);
353
 
 
354
 
        /* Draw the cursor - proper loop */
355
 
        for (line = 0; line < FFMIN(20, (y_off + height) - y); line++) {
356
 
            uint8_t *cursor = im_data;
357
 
            int column;
358
 
            uint16_t bm_b;
359
 
            uint16_t bm_w;
360
 
 
361
 
            bm_b = mousePointerBlack[line];
362
 
            bm_w = mousePointerWhite[line];
363
 
 
364
 
            for (column = 0; column < FFMIN(16, (x_off + width) - x); column++) {
365
 
                apply_masks(cursor, ~(masks*(bm_b&1)), masks*(bm_w&1),
366
 
                            image->bits_per_pixel);
367
 
                cursor += bytes_per_pixel;
368
 
                bm_b >>= 1;
369
 
                bm_w >>= 1;
370
 
            }
371
 
            im_data += image->bytes_per_line;
372
 
        }
373
 
    }
374
 
}
375
 
 
376
 
 
377
 
/**
378
 
 * Reads new data in the image structure.
379
 
 *
380
 
 * @param dpy X11 display to grab from
381
 
 * @param d
382
 
 * @param image Image where the grab will be put
383
 
 * @param x Top-Left grabbing rectangle horizontal coordinate
384
 
 * @param y Top-Left grabbing rectangle vertical coordinate
385
 
 * @return 0 if error, !0 if successful
386
 
 */
387
 
static int
388
 
xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
389
 
{
390
 
    xGetImageReply rep;
391
 
    xGetImageReq *req;
392
 
    long nbytes;
393
 
 
394
 
    if (!image) {
395
 
        return 0;
396
 
    }
397
 
 
398
 
    LockDisplay(dpy);
399
 
    GetReq(GetImage, req);
400
 
 
401
 
    /* First set up the standard stuff in the request */
402
 
    req->drawable = d;
403
 
    req->x = x;
404
 
    req->y = y;
405
 
    req->width = image->width;
406
 
    req->height = image->height;
407
 
    req->planeMask = (unsigned int)AllPlanes;
408
 
    req->format = ZPixmap;
409
 
 
410
 
    if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
411
 
        UnlockDisplay(dpy);
412
 
        SyncHandle();
413
 
        return 0;
414
 
    }
415
 
 
416
 
    nbytes = (long)rep.length << 2;
417
 
    _XReadPad(dpy, image->data, nbytes);
418
 
 
419
 
    UnlockDisplay(dpy);
420
 
    SyncHandle();
421
 
    return 1;
422
 
}
423
 
 
424
 
/**
425
 
 * Grabs a frame from x11 (public device demuxer API).
426
 
 *
427
 
 * @param s1 Context from avformat core
428
 
 * @param pkt Packet holding the brabbed frame
429
 
 * @return frame size in bytes
430
 
 */
431
 
static int
432
 
x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
433
 
{
434
 
    x11_grab_t *s = s1->priv_data;
435
 
    Display *dpy = s->dpy;
436
 
    XImage *image = s->image;
437
 
    int x_off = s->x_off;
438
 
    int y_off = s->y_off;
439
 
 
440
 
    int64_t curtime, delay;
441
 
    struct timespec ts;
442
 
 
443
 
    /* Calculate the time of the next frame */
444
 
    s->time_frame += INT64_C(1000000);
445
 
 
446
 
    /* wait based on the frame rate */
447
 
    for(;;) {
448
 
        curtime = av_gettime();
449
 
        delay = s->time_frame * av_q2d(s->time_base) - curtime;
450
 
        if (delay <= 0) {
451
 
            if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
452
 
                s->time_frame += INT64_C(1000000);
453
 
            }
454
 
            break;
455
 
        }
456
 
        ts.tv_sec = delay / 1000000;
457
 
        ts.tv_nsec = (delay % 1000000) * 1000;
458
 
        nanosleep(&ts, NULL);
459
 
    }
460
 
 
461
 
    if (av_new_packet(pkt, s->frame_size) < 0) {
462
 
        return AVERROR(EIO);
463
 
    }
464
 
 
465
 
    pkt->pts = curtime;
466
 
 
467
 
    if(s->use_shm) {
468
 
        if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) {
469
 
            av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
470
 
        }
471
 
    } else {
472
 
        if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) {
473
 
            av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
474
 
        }
475
 
    }
476
 
 
477
 
    {
478
 
        int pointer_x, pointer_y;
479
 
        get_pointer_coordinates(&pointer_x, &pointer_y, dpy, s1);
480
 
        paint_mouse_pointer(image, s, pointer_x, pointer_y);
481
 
    }
482
 
 
483
 
 
484
 
    /* XXX: avoid memcpy */
485
 
    memcpy(pkt->data, image->data, s->frame_size);
486
 
    return s->frame_size;
487
 
}
488
 
 
489
 
/**
490
 
 * Closes x11 frame grabber (public device demuxer API).
491
 
 *
492
 
 * @param s1 Context from avformat core
493
 
 * @return 0 success, !0 failure
494
 
 */
495
 
static int
496
 
x11grab_read_close(AVFormatContext *s1)
497
 
{
498
 
    x11_grab_t *x11grab = s1->priv_data;
499
 
 
500
 
    /* Detach cleanly from shared mem */
501
 
    if (x11grab->use_shm) {
502
 
        XShmDetach(x11grab->dpy, &x11grab->shminfo);
503
 
        shmdt(x11grab->shminfo.shmaddr);
504
 
        shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
505
 
    }
506
 
 
507
 
    /* Destroy X11 image */
508
 
    if (x11grab->image) {
509
 
        XDestroyImage(x11grab->image);
510
 
        x11grab->image = NULL;
511
 
    }
512
 
 
513
 
    /* Free X11 display */
514
 
    XCloseDisplay(x11grab->dpy);
515
 
    return 0;
516
 
}
517
 
 
518
 
/** x11 grabber device demuxer declaration */
519
 
AVInputFormat x11_grab_device_demuxer =
520
 
{
521
 
    "x11grab",
522
 
    "X11grab",
523
 
    sizeof(x11_grab_t),
524
 
    NULL,
525
 
    x11grab_read_header,
526
 
    x11grab_read_packet,
527
 
    x11grab_read_close,
528
 
    .flags = AVFMT_NOFILE,
529
 
};