Commit | Line | Data |
---|---|---|
f58f4ce1 GP |
1 | /* |
2 | * X11 video grab interface | |
2909f177 | 3 | * |
ef84bf04 | 4 | * This file is part of FFmpeg. |
f58f4ce1 | 5 | * |
ef84bf04 EG |
6 | * FFmpeg integration: |
7 | * Copyright (C) 2006 Clemens Fruhwirth <clemens@endorphin.org> | |
8 | * Edouard Gomez <ed.gomez@free.fr> | |
f58f4ce1 | 9 | * |
ef84bf04 | 10 | * This file contains code from grab.c: |
aac105fc | 11 | * Copyright (c) 2000-2001 Fabrice Bellard |
f58f4ce1 GP |
12 | * |
13 | * This file contains code from the xvidcap project: | |
ef84bf04 EG |
14 | * Copyright (C) 1997-1998 Rasca, Berlin |
15 | * 2003-2004 Karl H. Beckers, Frankfurt | |
f58f4ce1 | 16 | * |
ef84bf04 | 17 | * FFmpeg is free software; you can redistribute it and/or modify |
f58f4ce1 GP |
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 | * | |
ef84bf04 | 22 | * FFmpeg is distributed in the hope that it will be useful, |
f58f4ce1 GP |
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 | * | |
e5a389a1 DB |
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 | |
f58f4ce1 | 30 | */ |
2909f177 | 31 | |
6e71a18e EG |
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 | ||
f58f4ce1 GP |
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> | |
ef84bf04 | 50 | #include <X11/Xutil.h> |
f58f4ce1 GP |
51 | #include <sys/ipc.h> |
52 | #include <sys/shm.h> | |
53 | #include <X11/extensions/XShm.h> | |
54 | ||
6e71a18e EG |
55 | /** |
56 | * X11 Device Demuxer context | |
57 | */ | |
58 | typedef struct x11_grab_s | |
2909f177 | 59 | { |
6e71a18e EG |
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 */ | |
ca454440 | 73 | int mouse_warning_shown; |
6e71a18e EG |
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> | |
5632e568 | 82 | * <li>AVERROR(ENOMEM) no memory left</li> |
6f3e0b21 | 83 | * <li>AVERROR(EIO) other failure case</li> |
6e71a18e EG |
84 | * <li>0 success</li> |
85 | * </ul> | |
86 | */ | |
2909f177 EG |
87 | static int |
88 | x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap) | |
f58f4ce1 | 89 | { |
6e71a18e | 90 | x11_grab_t *x11grab = s1->priv_data; |
aac105fc EG |
91 | Display *dpy; |
92 | AVStream *st = NULL; | |
aac105fc EG |
93 | int input_pixfmt; |
94 | XImage *image; | |
6e71a18e EG |
95 | int x_off = 0; |
96 | int y_off = 0; | |
aac105fc | 97 | int use_shm; |
0cd4faf9 | 98 | char *param, *offset; |
aac105fc | 99 | |
cc58300e | 100 | param = av_strdup(s1->filename); |
0cd4faf9 PI |
101 | offset = strchr(param, '+'); |
102 | if (offset) { | |
103 | sscanf(offset, "%d,%d", &x_off, &y_off); | |
104 | *offset= 0; | |
105 | } | |
106 | ||
cc58300e | 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); |
0cd4faf9 PI |
108 | |
109 | dpy = XOpenDisplay(param); | |
110 | if(!dpy) { | |
111 | av_log(s1, AV_LOG_ERROR, "Could not open X display.\n"); | |
6f3e0b21 | 112 | return AVERROR(EIO); |
0cd4faf9 | 113 | } |
aac105fc EG |
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"); | |
6f3e0b21 | 117 | return AVERROR(EIO); |
aac105fc EG |
118 | } |
119 | ||
aac105fc EG |
120 | st = av_new_stream(s1, 0); |
121 | if (!st) { | |
8fa36ae0 | 122 | return AVERROR(ENOMEM); |
aac105fc EG |
123 | } |
124 | av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ | |
125 | ||
126 | use_shm = XShmQueryExtension(dpy); | |
6e71a18e | 127 | av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not"); |
aac105fc EG |
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"); | |
8fa36ae0 | 143 | return AVERROR(ENOMEM); |
aac105fc EG |
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 :) */ | |
6f3e0b21 | 151 | return AVERROR(EIO); |
aac105fc EG |
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: | |
6187b8bd | 162 | av_log (s1, AV_LOG_DEBUG, "8 bit palette\n"); |
aac105fc EG |
163 | input_pixfmt = PIX_FMT_PAL8; |
164 | break; | |
165 | case 16: | |
6e71a18e EG |
166 | if ( image->red_mask == 0xf800 && |
167 | image->green_mask == 0x07e0 && | |
168 | image->blue_mask == 0x001f ) { | |
aac105fc EG |
169 | av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n"); |
170 | input_pixfmt = PIX_FMT_RGB565; | |
6e71a18e EG |
171 | } else if (image->red_mask == 0x7c00 && |
172 | image->green_mask == 0x03e0 && | |
173 | image->blue_mask == 0x001f ) { | |
aac105fc EG |
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); | |
6f3e0b21 | 179 | return AVERROR(EIO); |
aac105fc EG |
180 | } |
181 | break; | |
182 | case 24: | |
6e71a18e EG |
183 | if ( image->red_mask == 0xff0000 && |
184 | image->green_mask == 0x00ff00 && | |
185 | image->blue_mask == 0x0000ff ) { | |
aac105fc | 186 | input_pixfmt = PIX_FMT_BGR24; |
6e71a18e EG |
187 | } else if ( image->red_mask == 0x0000ff && |
188 | image->green_mask == 0x00ff00 && | |
189 | image->blue_mask == 0xff0000 ) { | |
aac105fc EG |
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); | |
6f3e0b21 | 194 | return AVERROR(EIO); |
aac105fc EG |
195 | } |
196 | break; | |
197 | case 32: | |
f58f4ce1 | 198 | #if 0 |
aac105fc | 199 | GetColorInfo (image, &c_info); |
6e71a18e | 200 | if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) { |
aac105fc EG |
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); | |
6f3e0b21 | 210 | return AVERROR(EIO); |
aac105fc | 211 | } |
f58f4ce1 | 212 | #endif |
71e445fc | 213 | input_pixfmt = PIX_FMT_RGB32; |
aac105fc EG |
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 | ||
6e71a18e | 220 | x11grab->frame_size = ap->width * ap->height * image->bits_per_pixel/8; |
aac105fc EG |
221 | x11grab->dpy = dpy; |
222 | x11grab->width = ap->width; | |
223 | x11grab->height = ap->height; | |
6e71a18e EG |
224 | x11grab->time_base = ap->time_base; |
225 | x11grab->time_frame = av_gettime() / av_q2d(ap->time_base); | |
aac105fc EG |
226 | x11grab->x_off = x_off; |
227 | x11grab->y_off = y_off; | |
228 | x11grab->image = image; | |
229 | x11grab->use_shm = use_shm; | |
ca454440 | 230 | x11grab->mouse_warning_shown = 0; |
aac105fc EG |
231 | |
232 | st->codec->codec_type = CODEC_TYPE_VIDEO; | |
233 | st->codec->codec_id = CODEC_ID_RAWVIDEO; | |
6e71a18e EG |
234 | st->codec->width = ap->width; |
235 | st->codec->height = ap->height; | |
aac105fc | 236 | st->codec->pix_fmt = input_pixfmt; |
6e71a18e EG |
237 | st->codec->time_base = ap->time_base; |
238 | st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(ap->time_base) * 8; | |
aac105fc EG |
239 | |
240 | return 0; | |
f58f4ce1 GP |
241 | } |
242 | ||
6e71a18e EG |
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 | */ | |
2909f177 | 251 | static void |
6e71a18e | 252 | get_pointer_coordinates(int *x, int *y, Display *dpy, AVFormatContext *s1) |
2909f177 | 253 | { |
aac105fc EG |
254 | Window mrootwindow, childwindow; |
255 | int dummy; | |
aac105fc EG |
256 | |
257 | mrootwindow = DefaultRootWindow(dpy); | |
258 | ||
259 | if (XQueryPointer(dpy, mrootwindow, &mrootwindow, &childwindow, | |
260 | x, y, &dummy, &dummy, (unsigned int*)&dummy)) { | |
261 | } else { | |
ca454440 PI |
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 | } | |
aac105fc EG |
267 | *x = -1; |
268 | *y = -1; | |
269 | } | |
f58f4ce1 GP |
270 | } |
271 | ||
6e71a18e EG |
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 | */ | |
0235a311 EG |
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: | |
6e71a18e | 294 | *dst = !!or; |
0235a311 EG |
295 | break; |
296 | } | |
297 | } | |
65a1c656 | 298 | |
6e71a18e EG |
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 | */ | |
2909f177 | 308 | static void |
6e71a18e | 309 | paint_mouse_pointer(XImage *image, x11_grab_t *s, int x, int y) |
2909f177 | 310 | { |
6e71a18e | 311 | /* 16x20x1bpp bitmap for the black channel of the mouse pointer */ |
aac105fc EG |
312 | static const uint16_t const mousePointerBlack[] = |
313 | { | |
bd839338 EG |
314 | 0x0000, 0x0003, 0x0005, 0x0009, 0x0011, |
315 | 0x0021, 0x0041, 0x0081, 0x0101, 0x0201, | |
316 | 0x03c1, 0x0049, 0x0095, 0x0093, 0x0120, | |
317 | 0x0120, 0x0240, 0x0240, 0x0380, 0x0000 | |
aac105fc EG |
318 | }; |
319 | ||
6e71a18e | 320 | /* 16x20x1bpp bitmap for the white channel of the mouse pointer */ |
aac105fc EG |
321 | static const uint16_t const mousePointerWhite[] = |
322 | { | |
bd839338 EG |
323 | 0x0000, 0x0000, 0x0002, 0x0006, 0x000e, |
324 | 0x001e, 0x003e, 0x007e, 0x00fe, 0x01fe, | |
325 | 0x003e, 0x0036, 0x0062, 0x0060, 0x00c0, | |
326 | 0x00c0, 0x0180, 0x0180, 0x0000, 0x0000 | |
aac105fc EG |
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 | ||
6e71a18e EG |
334 | if ( x - x_off >= 0 && x < width + x_off |
335 | && y - y_off >= 0 && y < height + y_off) { | |
aac105fc | 336 | uint8_t *im_data = (uint8_t*)image->data; |
6e71a18e EG |
337 | int bytes_per_pixel; |
338 | int line; | |
0235a311 | 339 | int masks; |
aac105fc | 340 | |
0235a311 | 341 | /* Select correct masks and pixel size */ |
6e71a18e | 342 | if (image->bits_per_pixel == 8) { |
0235a311 | 343 | masks = 1; |
6e71a18e EG |
344 | } else { |
345 | masks = (image->red_mask|image->green_mask|image->blue_mask); | |
0235a311 | 346 | } |
6e71a18e | 347 | bytes_per_pixel = image->bits_per_pixel>>3; |
0235a311 EG |
348 | |
349 | /* Shift to right line */ | |
6e71a18e EG |
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); | |
0235a311 EG |
353 | |
354 | /* Draw the cursor - proper loop */ | |
6e71a18e | 355 | for (line = 0; line < FFMIN(20, (y_off + height) - y); line++) { |
0235a311 | 356 | uint8_t *cursor = im_data; |
6e71a18e | 357 | int column; |
0235a311 EG |
358 | uint16_t bm_b; |
359 | uint16_t bm_w; | |
360 | ||
6e71a18e EG |
361 | bm_b = mousePointerBlack[line]; |
362 | bm_w = mousePointerWhite[line]; | |
0235a311 | 363 | |
6e71a18e EG |
364 | for (column = 0; column < FFMIN(16, (x_off + width) - x); column++) { |
365 | apply_masks(cursor, ~(masks*(bm_b&1)), masks*(bm_w&1), | |
0235a311 | 366 | image->bits_per_pixel); |
6e71a18e | 367 | cursor += bytes_per_pixel; |
0235a311 EG |
368 | bm_b >>= 1; |
369 | bm_w >>= 1; | |
370 | } | |
371 | im_data += image->bytes_per_line; | |
aac105fc EG |
372 | } |
373 | } | |
f58f4ce1 GP |
374 | } |
375 | ||
376 | ||
6e71a18e EG |
377 | /** |
378 | * Reads new data in the image structure. | |
379 | * | |
380 | * @param dpy X11 display to grab from | |
05f3b6ab | 381 | * @param d |
6e71a18e EG |
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 | |
f58f4ce1 GP |
386 | */ |
387 | static int | |
6e71a18e | 388 | xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y) |
f58f4ce1 | 389 | { |
aac105fc EG |
390 | xGetImageReply rep; |
391 | xGetImageReq *req; | |
392 | long nbytes; | |
393 | ||
394 | if (!image) { | |
6e71a18e | 395 | return 0; |
aac105fc EG |
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 | ||
6e71a18e | 410 | if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) { |
aac105fc EG |
411 | UnlockDisplay(dpy); |
412 | SyncHandle(); | |
6e71a18e | 413 | return 0; |
aac105fc EG |
414 | } |
415 | ||
416 | nbytes = (long)rep.length << 2; | |
417 | _XReadPad(dpy, image->data, nbytes); | |
418 | ||
419 | UnlockDisplay(dpy); | |
420 | SyncHandle(); | |
6e71a18e | 421 | return 1; |
f58f4ce1 GP |
422 | } |
423 | ||
6e71a18e EG |
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 | */ | |
0235a311 EG |
431 | static int |
432 | x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt) | |
f58f4ce1 | 433 | { |
6e71a18e | 434 | x11_grab_t *s = s1->priv_data; |
aac105fc EG |
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 */ | |
ba78f9d4 | 444 | s->time_frame += INT64_C(1000000); |
aac105fc EG |
445 | |
446 | /* wait based on the frame rate */ | |
447 | for(;;) { | |
448 | curtime = av_gettime(); | |
6e71a18e | 449 | delay = s->time_frame * av_q2d(s->time_base) - curtime; |
aac105fc | 450 | if (delay <= 0) { |
ba78f9d4 BC |
451 | if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) { |
452 | s->time_frame += INT64_C(1000000); | |
aac105fc EG |
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) { | |
6f3e0b21 | 462 | return AVERROR(EIO); |
aac105fc EG |
463 | } |
464 | ||
6e71a18e | 465 | pkt->pts = curtime; |
aac105fc EG |
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 { | |
6e71a18e | 472 | if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) { |
aac105fc EG |
473 | av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n"); |
474 | } | |
475 | } | |
476 | ||
477 | { | |
478 | int pointer_x, pointer_y; | |
6e71a18e EG |
479 | get_pointer_coordinates(&pointer_x, &pointer_y, dpy, s1); |
480 | paint_mouse_pointer(image, s, pointer_x, pointer_y); | |
aac105fc EG |
481 | } |
482 | ||
483 | ||
484 | /* XXX: avoid memcpy */ | |
485 | memcpy(pkt->data, image->data, s->frame_size); | |
486 | return s->frame_size; | |
f58f4ce1 GP |
487 | } |
488 | ||
6e71a18e EG |
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 | */ | |
0235a311 EG |
495 | static int |
496 | x11grab_read_close(AVFormatContext *s1) | |
f58f4ce1 | 497 | { |
6e71a18e | 498 | x11_grab_t *x11grab = s1->priv_data; |
aac105fc EG |
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; | |
f58f4ce1 GP |
516 | } |
517 | ||
6e71a18e | 518 | /** x11 grabber device demuxer declaration */ |
2909f177 EG |
519 | AVInputFormat x11_grab_device_demuxer = |
520 | { | |
aac105fc EG |
521 | "x11grab", |
522 | "X11grab", | |
6e71a18e | 523 | sizeof(x11_grab_t), |
aac105fc EG |
524 | NULL, |
525 | x11grab_read_header, | |
526 | x11grab_read_packet, | |
527 | x11grab_read_close, | |
528 | .flags = AVFMT_NOFILE, | |
f58f4ce1 | 529 | }; |