comparison x11grab.c @ 1571:1db03d7eebe1 libavformat

Original X11 device demuxer patch from Clemens Fruhwirth - Build system integration is equivalent to RFC #6 patch - Same rule applies to ffmpeg.c/allformats.[c|h] - x11grab.c is from Clemens Fruhwirth except the x11 grab structure for libavformat registration is renamed to match build system integration.
author gpoirier
date Tue, 12 Dec 2006 22:31:46 +0000
parents
children fd6931b2b8ce
comparison
equal deleted inserted replaced
1570:cffb8f8e1ed6 1571:1db03d7eebe1
1 /*
2 * X11 video grab interface
3 * Copyright (c) 2006 Clemens Fruhwirth
4 *
5 * A quick note on licensing. This file is a mixture of LGPL code
6 * (ffmpeg) and GPL code (xvidcap). The result is a file that must
7 * abid both licenses. As they are compatible and GPL is more
8 * strict, this code has an "effective" GPL license.
9 *
10 * This file contains code from grab.c:
11 * Copyright (c) 2000,2001 Fabrice Bellard
12 *
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Lesser General Public
15 * License as published by the Free Software Foundation; either
16 * version 2 of the License, or (at your option) any later version.
17 *
18 * This library is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * Lesser General Public License for more details.
22 *
23 * You should have received a copy of the GNU Lesser General Public
24 * License along with this library; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 *
27 * This file contains code from the xvidcap project:
28 * Copyright (C) 1997-98 Rasca, Berlin
29 * Copyright (C) 2003,04 Karl H. Beckers, Frankfurt
30 *
31 * This program is free software; you can redistribute it and/or modify
32 * it under the terms of the GNU General Public License as published by
33 * the Free Software Foundation; either version 2 of the License, or
34 * (at your option) any later version.
35 *
36 * This program is distributed in the hope that it will be useful,
37 * but WITHOUT ANY WARRANTY; without even the implied warranty of
38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 * GNU General Public License for more details.
40 *
41 * You should have received a copy of the GNU General Public License
42 * along with this program; if not, write to the Free Software
43 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
44 */
45 #include "avformat.h"
46 #include <unistd.h>
47 #include <fcntl.h>
48 #include <sys/ioctl.h>
49 #include <sys/mman.h>
50 #include <sys/time.h>
51 #define _LINUX_TIME_H 1
52 #include <time.h>
53 #include <X11/X.h>
54 #include <X11/Xlib.h>
55 #include <X11/Xlibint.h>
56 #include <X11/Xproto.h>
57 #include <X11/Xutil.h>
58 #include <sys/ipc.h>
59 #include <sys/shm.h>
60 #include <X11/extensions/XShm.h>
61
62
63 typedef struct {
64 Display *dpy;
65 int frame_format;
66 int frame_size;
67 int frame_rate;
68 int frame_rate_base;
69 int64_t time_frame;
70
71 int height;
72 int width;
73 int x_off;
74 int y_off;
75 XImage *image;
76 int use_shm;
77 XShmSegmentInfo shminfo;
78 int mouse_wanted;
79 } X11Grab;
80
81 static int x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
82 {
83 X11Grab *x11grab=s1->priv_data;
84 Display *dpy;
85 AVStream *st;
86 int width, height;
87 int frame_rate, frame_rate_base, frame_size;
88 int input_pixfmt;
89 XImage *image;
90 int x_off=0; int y_off = 0;
91 char *device_parsing = strdup(ap->device);
92 int use_shm;
93
94 dpy = XOpenDisplay(NULL);
95
96 if(!dpy)
97 goto fail;
98
99 sscanf(device_parsing,"x11:%d,%d",&x_off, &y_off);
100 fprintf(stderr,"device: %s -> x: %d y: %d width: %d height: %d\n", ap->device, x_off, y_off, ap->width, ap->height);
101
102 if (!ap || ap->width <= 0 || ap->height <= 0 || ap->time_base.den <= 0) {
103 fprintf(stderr,"x11grab: AVParameters don't have any video size. Use -s.\n");
104 return AVERROR_IO;
105 }
106 width = ap->width;
107 height = ap->height;
108 frame_rate = ap->time_base.den;
109 frame_rate_base = ap->time_base.num;
110
111 st = av_new_stream(s1, 0);
112 if (!st)
113 return -ENOMEM;
114 av_set_pts_info(st, 48, 1, 1000000); /* 48 bits pts in us */
115
116 use_shm = XShmQueryExtension(dpy);
117 fprintf(stderr,"x11grab: shared memory extension %s\n",use_shm?"found":"not found");
118 if(use_shm) {
119 int scr = XDefaultScreen(dpy);
120 image = XShmCreateImage(dpy,
121 DefaultVisual(dpy,scr),
122 DefaultDepth(dpy,scr),
123 ZPixmap, NULL,
124 &x11grab->shminfo, ap->width, ap->height);
125 x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
126 image->bytes_per_line * image->height, IPC_CREAT|0777);
127 if (x11grab->shminfo.shmid == -1) {
128 fprintf(stderr,"Fatal: Can't get shared memory!\n");
129 return -ENOMEM;
130 }
131 x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
132 x11grab->shminfo.readOnly = False;
133
134 if (XShmAttach(dpy, &x11grab->shminfo) == 0) {
135 fprintf(stderr,"Fatal: Failed to attach shared memory!\n");
136 /* needs some better error subroutine :) */
137 return AVERROR_IO;
138 }
139 }
140 else {
141 image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
142 x_off,y_off,
143 ap->width,ap->height,
144 AllPlanes, ZPixmap);
145 }
146
147 switch (image->bits_per_pixel) {
148 case 8:
149 fprintf (stderr, "x11grab: 8 bit pallete\n");
150 input_pixfmt = PIX_FMT_PAL8;
151 break;
152 case 16:
153 if ( image->red_mask == 0xF800 && image->green_mask == 0x07E0
154 && image->blue_mask == 0x1F ) {
155 fprintf (stderr, "x11grab: 16 bit RGB565\n");
156 input_pixfmt = PIX_FMT_RGB565;
157 } else if ( image->red_mask == 0x7C00 &&
158 image->green_mask == 0x03E0 &&
159 image->blue_mask == 0x1F ) {
160 fprintf (stderr, "x11grab: 16 bit RGB555\n");
161 input_pixfmt = PIX_FMT_RGB555;
162 } else {
163 fprintf (stderr, "x11grab: RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
164 fprintf (stderr, "x11grab: color masks: r 0x%.6X g 0x%.6X b 0x%.6X\n", image->red_mask, image->green_mask, image->blue_mask);
165 return AVERROR_IO;
166 }
167 break;
168 case 24:
169 if ( image->red_mask == 0xFF0000 &&
170 image->green_mask == 0xFF00
171 && image->blue_mask == 0xFF ) {
172 input_pixfmt = PIX_FMT_BGR24;
173 } else if ( image->red_mask == 0xFF && image->green_mask == 0xFF00
174 && image->blue_mask == 0xFF0000 ) {
175 input_pixfmt = PIX_FMT_RGB24;
176 } else {
177 fprintf (stderr, "xtoffmpeg.XImageToFFMPEG(): rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
178 fprintf (stderr, "xtoffmpeg.XImageToFFMPEG(): color masks: r 0x%.6X g 0x%.6X b 0x%.6X\n", image->red_mask, image->green_mask, image->blue_mask);
179 return AVERROR_IO;
180 }
181 break;
182 case 32:
183 #if 0
184 GetColorInfo (image, &c_info);
185 if ( c_info.alpha_mask == 0xFF000000 && image->green_mask == 0xFF00 ) {
186 // byte order is relevant here, not endianness
187 // endianness is handled by avcodec, but atm no such thing
188 // as having ABGR, instead of ARGB in a word. Since we
189 // need this for Solaris/SPARC, but need to do the conversion
190 // for every frame we do it outside of this loop, cf. below
191 // this matches both ARGB32 and ABGR32
192 input_pixfmt = PIX_FMT_ARGB32;
193 } else {
194 fprintf (stderr, "xtoffmpeg.XImageToFFMPEG(): image depth %i not supported ... aborting\n", image->bits_per_pixel);
195 return AVERROR_IO;
196 }
197 #endif
198 input_pixfmt = PIX_FMT_RGBA32;
199 break;
200 default:
201 fprintf (stderr, "xtoffmpeg.XImageToFFMPEG(): image depth %i not supported ... aborting\n", image->bits_per_pixel);
202 return -1;
203 }
204
205 frame_size = width * height * image->bits_per_pixel/8;
206 x11grab->frame_size = frame_size;
207 x11grab->dpy = dpy;
208 x11grab->width = ap->width;
209 x11grab->height = ap->height;
210 x11grab->frame_rate = frame_rate;
211 x11grab->frame_rate_base = frame_rate_base;
212 x11grab->time_frame = av_gettime() * frame_rate / frame_rate_base;
213 x11grab->x_off = x_off;
214 x11grab->y_off = y_off;
215 x11grab->image = image;
216 x11grab->use_shm = use_shm;
217 x11grab->mouse_wanted = 1;
218
219 st->codec->codec_type = CODEC_TYPE_VIDEO;
220 st->codec->codec_id = CODEC_ID_RAWVIDEO;
221 st->codec->width = width;
222 st->codec->height = height;
223 st->codec->pix_fmt = input_pixfmt;
224 st->codec->time_base.den = frame_rate;
225 st->codec->time_base.num = frame_rate_base;
226 st->codec->bit_rate = frame_size * 1/av_q2d(st->codec->time_base) * 8;
227
228 return 0;
229 fail:
230 av_free(st);
231 av_free(device_parsing);
232 return AVERROR_IO;
233 }
234
235 uint16_t mousePointerBlack[] = { 0, 49152, 40960, 36864, 34816, 33792, 33280, 33024, 32896, 32832,
236 33728, 37376, 43264, 51456, 1152, 1152, 576, 576, 448, 0 };
237 uint16_t mousePointerWhite[] = { 0, 0, 16384, 24576, 28672, 30720, 31744, 32256, 32512, 32640, 31744,
238 27648, 17920, 1536, 768, 768, 384, 384, 0, 0 };
239
240 static void getCurrentPointer(X11Grab *s, int *x, int *y) {
241 Window mrootwindow, childwindow;
242 int dummy;
243 Display *dpy = s->dpy;
244
245 mrootwindow = DefaultRootWindow(dpy);
246
247 if (XQueryPointer(dpy, mrootwindow, &mrootwindow, &childwindow,
248 x, y, &dummy, &dummy, &dummy)) {
249 } else {
250 fprintf(stderr,"couldn't find mouse pointer\n");
251 *x = -1;
252 *y = -1;
253 }
254 }
255
256 static void paintMousePointer(X11Grab *s, int *x, int *y, XImage *image) {
257 int x_off = s->x_off;
258 int y_off = s->y_off;
259 int width = s->width;
260 int height = s->height;
261
262 if ((*x - x_off) >= 0 &&
263 *x < (width + x_off) &&
264 (*y - y_off) >= 0 &&
265 *y < (height + y_off) ) {
266 int line;
267 uint8_t *im_data = image->data;
268
269 im_data += (image->bytes_per_line * (*y - y_off)); // shift to right line
270 im_data += (image->bits_per_pixel / 8 * (*x - x_off)); // shift to right pixel
271
272 switch(image->bits_per_pixel) {
273 case 32:
274 {
275 uint32_t *cursor;
276 int width_cursor;
277 uint16_t bm_b, bm_w, mask;
278
279 for (line = 0; line < 20; line++ ) {
280 if (s->mouse_wanted == 1) {
281 bm_b = mousePointerBlack[line];
282 bm_w = mousePointerWhite[line];
283 } else {
284 bm_b = mousePointerWhite[line];
285 bm_w = mousePointerBlack[line];
286 }
287 mask = ( 0x0001 << 15 );
288
289 for (cursor = (uint32_t*) im_data, width_cursor = 0;
290 ((width_cursor + *x) < (width + x_off) && width_cursor < 16);
291 cursor++, width_cursor++) {
292 // Boolean pointer_b_bit, pointer_w_bit;
293 // pointer_b_bit = ( ( bm_b & mask ) > 0 );
294 // pointer_w_bit = ( ( bm_w & mask ) > 0 );
295 // printf("%i ", pointer_b_bit, pointer_w_bit );
296
297 if ( ( bm_b & mask ) > 0 ) {
298 *cursor &= ((~ image->red_mask) & (~ image->green_mask) & (~image->blue_mask ));
299 } else if ( ( bm_w & mask ) > 0 ) {
300 *cursor |= (image->red_mask | image->green_mask | image->blue_mask );
301 }
302 mask >>= 1;
303 }
304 // printf("\n");
305 im_data += image->bytes_per_line;
306 }
307 }
308 break;
309 #if 0
310 case 24: // not sure this can occur at all ..........
311 fprintf(stderr,"input image bits_per_pixel %i not implemented with mouse pointer capture ... aborting!\n",
312 image->bits_per_pixel);
313 fprintf(stderr,"Please file a bug at http://www.sourceforge.net/projects/xvidcap/\n");
314 exit(1);
315 break;
316 case 16: {
317 uint16_t *cursor;
318 int width;
319 uint16_t bm_b, bm_w, mask;
320
321 for (line = 0; line < 16; line++ ) {
322 if (mjob->mouseWanted == 1) {
323 bm_b = mousePointerBlack[line];
324 bm_w = mousePointerWhite[line];
325 } else {
326 bm_b = mousePointerWhite[line];
327 bm_w = mousePointerBlack[line];
328 }
329 mask = ( 0x0001 << 15 );
330
331 for (cursor = (uint16_t*) im_data, width = 0;
332 ((width + *x) < (mjob->area->width + mjob->area->x)&&width < 6);
333 cursor++, width++) {
334 // Boolean pointer_b_bit, pointer_w_bit;
335
336 // pointer_b_bit = ( ( bm_b & mask ) > 0 );
337 // pointer_w_bit = ( ( bm_w & mask ) > 0 );
338 // printf("%i ", pointer_b_bit, pointer_w_bit );
339
340 if ( ( bm_b & mask ) > 0 ) {
341 *cursor &= ((~ image->red_mask) & (~ image->green_mask) & (~
342 image->blue_mask ));
343 } else if ( ( bm_w & mask ) > 0 ) {
344 *cursor |= (image->red_mask | image->green_mask | image->blue_mask );
345 }
346 mask >>= 1;
347
348 }
349 // printf("\n");
350
351 im_data += image->bytes_per_line;
352 }
353 }
354 break;
355 case 8: {
356 uint8_t *cursor;
357 int width;
358 uint16_t bm_b, bm_w, mask;
359
360 for (line = 0; line < 16; line++ ) {
361 if (mjob->mouseWanted == 1) {
362 bm_b = mousePointerBlack[line];
363 bm_w = mousePointerWhite[line];
364 } else {
365 bm_b = mousePointerWhite[line];
366 bm_w = mousePointerBlack[line];
367 }
368 mask = ( 0x0001 << 15 );
369
370 for (cursor = im_data, width = 0;
371 ((width + *x) < (mjob->area->width + mjob->area->x)&&width < 6);
372 cursor++, width++) {
373 // Boolean pointer_b_bit, pointer_w_bit;
374
375 // pointer_b_bit = ( ( bm_b & mask ) > 0 );
376 // pointer_w_bit = ( ( bm_w & mask ) > 0 );
377 // printf("%i ", pointer_b_bit, pointer_w_bit );
378
379 if ( ( bm_b & mask ) > 0 ) {
380 *cursor = 0;
381 } else if ( ( bm_w & mask ) > 0 ) {
382 *cursor = 1;
383 }
384 mask >>= 1;
385
386 }
387 // printf("\n");
388
389 im_data += image->bytes_per_line;
390 }
391 }
392 break;
393 default:
394 fprintf(stderr,"input image bits_per_pixel %i not supported with mouse pointer capture ... aborting!\n",
395 image->bits_per_pixel);
396 exit(1);
397
398 #endif
399 }
400 }
401 }
402
403
404 /*
405 * just read new data in the image structure, the image
406 * structure inclusive the data area must be allocated before
407 */
408 static int
409 XGetZPixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
410 {
411 xGetImageReply rep;
412 xGetImageReq *req;
413 long nbytes;
414
415 if (!image)
416 return (False);
417 LockDisplay(dpy);
418 GetReq(GetImage, req);
419 /*
420 * first set up the standard stuff in the request
421 */
422 req->drawable = d;
423 req->x = x;
424 req->y = y;
425 req->width = image->width;
426 req->height = image->height;
427 req->planeMask = AllPlanes;
428 req->format = ZPixmap;
429
430 if (_XReply(dpy, (xReply *) &rep, 0, xFalse) == 0 ||
431 rep.length == 0) {
432 UnlockDisplay(dpy);
433 SyncHandle();
434 return (False);
435 }
436
437 nbytes = (long)rep.length << 2;
438 _XReadPad(dpy, image->data, nbytes);
439
440 UnlockDisplay(dpy);
441 SyncHandle();
442 return (True);
443 }
444
445 static int
446 x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
447 {
448 X11Grab *s = s1->priv_data;
449 Display *dpy = s->dpy;
450 XImage *image = s->image;
451 int x_off = s->x_off;
452 int y_off = s->y_off;
453
454 int64_t curtime, delay;
455 struct timespec ts;
456
457 /* Calculate the time of the next frame */
458 s->time_frame += int64_t_C(1000000);
459
460 /* wait based on the frame rate */
461 for(;;) {
462 curtime = av_gettime();
463 delay = s->time_frame * s->frame_rate_base / s->frame_rate - curtime;
464 if (delay <= 0) {
465 if (delay < int64_t_C(-1000000) * s->frame_rate_base / s->frame_rate) {
466 /* printf("grabbing is %d frames late (dropping)\n", (int) -(delay / 16666)); */
467 s->time_frame += int64_t_C(1000000);
468 }
469 break;
470 }
471 ts.tv_sec = delay / 1000000;
472 ts.tv_nsec = (delay % 1000000) * 1000;
473 nanosleep(&ts, NULL);
474 }
475
476 if (av_new_packet(pkt, s->frame_size) < 0)
477 return AVERROR_IO;
478
479 pkt->pts = curtime & ((1LL << 48) - 1);
480
481 if(s->use_shm) {
482 if (!XShmGetImage(dpy,
483 RootWindow(dpy, DefaultScreen(dpy)),
484 image, x_off, y_off, AllPlanes)) {
485 fprintf(stderr,"XShmGetImage() failed\n");
486 }
487 }
488 else {
489 XGetZPixmap(dpy,
490 RootWindow(dpy, DefaultScreen(dpy)),
491 image, x_off, y_off);
492 };
493 {
494 int pointer_x, pointer_y;
495 getCurrentPointer(s, &pointer_x, &pointer_y);
496 paintMousePointer(s, &pointer_x, &pointer_y, image);
497 }
498
499 #warning FIXME - avoid memcpy
500 memcpy(pkt->data, image->data, s->frame_size);
501 return s->frame_size;
502 }
503
504 static int
505 x11grab_read_close(AVFormatContext *s1)
506 {
507 X11Grab *x11grab = s1->priv_data;
508
509 XCloseDisplay(x11grab->dpy);
510 return 0;
511 }
512
513 AVInputFormat x11_grab_device_demuxer = {
514 "x11grab",
515 "X11grab",
516 sizeof(X11Grab),
517 NULL,
518 x11grab_read_header,
519 x11grab_read_packet,
520 x11grab_read_close,
521 .flags = AVFMT_NOFILE,
522 };