00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00038 #include "config.h"
00039 #include "libavformat/avformat.h"
00040 #include <unistd.h>
00041 #include <fcntl.h>
00042 #include <sys/ioctl.h>
00043 #include <sys/time.h>
00044 #define _LINUX_TIME_H 1
00045 #include <time.h>
00046 #include <X11/X.h>
00047 #include <X11/Xlib.h>
00048 #include <X11/Xlibint.h>
00049 #include <X11/Xproto.h>
00050 #include <X11/Xutil.h>
00051 #include <sys/ipc.h>
00052 #include <sys/shm.h>
00053 #include <X11/extensions/XShm.h>
00054
00058 struct x11_grab
00059 {
00060 int frame_size;
00061 AVRational time_base;
00062 int64_t time_frame;
00064 int height;
00065 int width;
00066 int x_off;
00067 int y_off;
00069 Display *dpy;
00070 XImage *image;
00071 int use_shm;
00072 XShmSegmentInfo shminfo;
00073 int mouse_warning_shown;
00074 };
00075
00087 static int
00088 x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
00089 {
00090 struct x11_grab *x11grab = s1->priv_data;
00091 Display *dpy;
00092 AVStream *st = NULL;
00093 int input_pixfmt;
00094 XImage *image;
00095 int x_off = 0;
00096 int y_off = 0;
00097 int use_shm;
00098 char *param, *offset;
00099
00100 param = av_strdup(s1->filename);
00101 offset = strchr(param, '+');
00102 if (offset) {
00103 sscanf(offset, "%d,%d", &x_off, &y_off);
00104 *offset= 0;
00105 }
00106
00107 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);
00108
00109 dpy = XOpenDisplay(param);
00110 if(!dpy) {
00111 av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
00112 return AVERROR(EIO);
00113 }
00114
00115 if (!ap || ap->width <= 0 || ap->height <= 0 || ap->time_base.den <= 0) {
00116 av_log(s1, AV_LOG_ERROR, "AVParameters don't have video size and/or rate. Use -s and -r.\n");
00117 return AVERROR(EIO);
00118 }
00119
00120 st = av_new_stream(s1, 0);
00121 if (!st) {
00122 return AVERROR(ENOMEM);
00123 }
00124 av_set_pts_info(st, 64, 1, 1000000);
00125
00126 use_shm = XShmQueryExtension(dpy);
00127 av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
00128
00129 if(use_shm) {
00130 int scr = XDefaultScreen(dpy);
00131 image = XShmCreateImage(dpy,
00132 DefaultVisual(dpy, scr),
00133 DefaultDepth(dpy, scr),
00134 ZPixmap,
00135 NULL,
00136 &x11grab->shminfo,
00137 ap->width, ap->height);
00138 x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
00139 image->bytes_per_line * image->height,
00140 IPC_CREAT|0777);
00141 if (x11grab->shminfo.shmid == -1) {
00142 av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
00143 return AVERROR(ENOMEM);
00144 }
00145 x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
00146 x11grab->shminfo.readOnly = False;
00147
00148 if (!XShmAttach(dpy, &x11grab->shminfo)) {
00149 av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
00150
00151 return AVERROR(EIO);
00152 }
00153 } else {
00154 image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
00155 x_off,y_off,
00156 ap->width,ap->height,
00157 AllPlanes, ZPixmap);
00158 }
00159
00160 switch (image->bits_per_pixel) {
00161 case 8:
00162 av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
00163 input_pixfmt = PIX_FMT_PAL8;
00164 break;
00165 case 16:
00166 if ( image->red_mask == 0xf800 &&
00167 image->green_mask == 0x07e0 &&
00168 image->blue_mask == 0x001f ) {
00169 av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
00170 input_pixfmt = PIX_FMT_RGB565;
00171 } else if (image->red_mask == 0x7c00 &&
00172 image->green_mask == 0x03e0 &&
00173 image->blue_mask == 0x001f ) {
00174 av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
00175 input_pixfmt = PIX_FMT_RGB555;
00176 } else {
00177 av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00178 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);
00179 return AVERROR(EIO);
00180 }
00181 break;
00182 case 24:
00183 if ( image->red_mask == 0xff0000 &&
00184 image->green_mask == 0x00ff00 &&
00185 image->blue_mask == 0x0000ff ) {
00186 input_pixfmt = PIX_FMT_BGR24;
00187 } else if ( image->red_mask == 0x0000ff &&
00188 image->green_mask == 0x00ff00 &&
00189 image->blue_mask == 0xff0000 ) {
00190 input_pixfmt = PIX_FMT_RGB24;
00191 } else {
00192 av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00193 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);
00194 return AVERROR(EIO);
00195 }
00196 break;
00197 case 32:
00198 #if 0
00199 GetColorInfo (image, &c_info);
00200 if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) {
00201
00202
00203
00204
00205
00206
00207 input_pixfmt = PIX_FMT_ARGB32;
00208 } else {
00209 av_log(s1, AV_LOG_ERROR,"image depth %i not supported ... aborting\n", image->bits_per_pixel);
00210 return AVERROR(EIO);
00211 }
00212 #endif
00213 input_pixfmt = PIX_FMT_RGB32;
00214 break;
00215 default:
00216 av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
00217 return -1;
00218 }
00219
00220 x11grab->frame_size = ap->width * ap->height * image->bits_per_pixel/8;
00221 x11grab->dpy = dpy;
00222 x11grab->width = ap->width;
00223 x11grab->height = ap->height;
00224 x11grab->time_base = ap->time_base;
00225 x11grab->time_frame = av_gettime() / av_q2d(ap->time_base);
00226 x11grab->x_off = x_off;
00227 x11grab->y_off = y_off;
00228 x11grab->image = image;
00229 x11grab->use_shm = use_shm;
00230 x11grab->mouse_warning_shown = 0;
00231
00232 st->codec->codec_type = CODEC_TYPE_VIDEO;
00233 st->codec->codec_id = CODEC_ID_RAWVIDEO;
00234 st->codec->width = ap->width;
00235 st->codec->height = ap->height;
00236 st->codec->pix_fmt = input_pixfmt;
00237 st->codec->time_base = ap->time_base;
00238 st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(ap->time_base) * 8;
00239
00240 return 0;
00241 }
00242
00251 static void
00252 get_pointer_coordinates(int *x, int *y, Display *dpy, AVFormatContext *s1)
00253 {
00254 Window mrootwindow, childwindow;
00255 int dummy;
00256
00257 mrootwindow = DefaultRootWindow(dpy);
00258
00259 if (XQueryPointer(dpy, mrootwindow, &mrootwindow, &childwindow,
00260 x, y, &dummy, &dummy, (unsigned int*)&dummy)) {
00261 } else {
00262 struct x11_grab *s = s1->priv_data;
00263 if (!s->mouse_warning_shown) {
00264 av_log(s1, AV_LOG_INFO, "couldn't find mouse pointer\n");
00265 s->mouse_warning_shown = 1;
00266 }
00267 *x = -1;
00268 *y = -1;
00269 }
00270 }
00271
00283 static void inline
00284 apply_masks(uint8_t *dst, int and, int or, int bits_per_pixel)
00285 {
00286 switch (bits_per_pixel) {
00287 case 32:
00288 *(uint32_t*)dst = (*(uint32_t*)dst & and) | or;
00289 break;
00290 case 16:
00291 *(uint16_t*)dst = (*(uint16_t*)dst & and) | or;
00292 break;
00293 case 8:
00294 *dst = !!or;
00295 break;
00296 }
00297 }
00298
00308 static void
00309 paint_mouse_pointer(XImage *image, struct x11_grab *s, int x, int y)
00310 {
00311
00312 static const uint16_t const mousePointerBlack[] =
00313 {
00314 0x0000, 0x0003, 0x0005, 0x0009, 0x0011,
00315 0x0021, 0x0041, 0x0081, 0x0101, 0x0201,
00316 0x03c1, 0x0049, 0x0095, 0x0093, 0x0120,
00317 0x0120, 0x0240, 0x0240, 0x0380, 0x0000
00318 };
00319
00320
00321 static const uint16_t const mousePointerWhite[] =
00322 {
00323 0x0000, 0x0000, 0x0002, 0x0006, 0x000e,
00324 0x001e, 0x003e, 0x007e, 0x00fe, 0x01fe,
00325 0x003e, 0x0036, 0x0062, 0x0060, 0x00c0,
00326 0x00c0, 0x0180, 0x0180, 0x0000, 0x0000
00327 };
00328
00329 int x_off = s->x_off;
00330 int y_off = s->y_off;
00331 int width = s->width;
00332 int height = s->height;
00333
00334 if ( x - x_off >= 0 && x < width + x_off
00335 && y - y_off >= 0 && y < height + y_off) {
00336 uint8_t *im_data = (uint8_t*)image->data;
00337 int bytes_per_pixel;
00338 int line;
00339 int masks;
00340
00341
00342 if (image->bits_per_pixel == 8) {
00343 masks = 1;
00344 } else {
00345 masks = (image->red_mask|image->green_mask|image->blue_mask);
00346 }
00347 bytes_per_pixel = image->bits_per_pixel>>3;
00348
00349
00350 im_data += image->bytes_per_line * (y - y_off);
00351
00352 im_data += bytes_per_pixel * (x - x_off);
00353
00354
00355 for (line = 0; line < FFMIN(20, (y_off + height) - y); line++) {
00356 uint8_t *cursor = im_data;
00357 int column;
00358 uint16_t bm_b;
00359 uint16_t bm_w;
00360
00361 bm_b = mousePointerBlack[line];
00362 bm_w = mousePointerWhite[line];
00363
00364 for (column = 0; column < FFMIN(16, (x_off + width) - x); column++) {
00365 apply_masks(cursor, ~(masks*(bm_b&1)), masks*(bm_w&1),
00366 image->bits_per_pixel);
00367 cursor += bytes_per_pixel;
00368 bm_b >>= 1;
00369 bm_w >>= 1;
00370 }
00371 im_data += image->bytes_per_line;
00372 }
00373 }
00374 }
00375
00376
00387 static int
00388 xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
00389 {
00390 xGetImageReply rep;
00391 xGetImageReq *req;
00392 long nbytes;
00393
00394 if (!image) {
00395 return 0;
00396 }
00397
00398 LockDisplay(dpy);
00399 GetReq(GetImage, req);
00400
00401
00402 req->drawable = d;
00403 req->x = x;
00404 req->y = y;
00405 req->width = image->width;
00406 req->height = image->height;
00407 req->planeMask = (unsigned int)AllPlanes;
00408 req->format = ZPixmap;
00409
00410 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
00411 UnlockDisplay(dpy);
00412 SyncHandle();
00413 return 0;
00414 }
00415
00416 nbytes = (long)rep.length << 2;
00417 _XReadPad(dpy, image->data, nbytes);
00418
00419 UnlockDisplay(dpy);
00420 SyncHandle();
00421 return 1;
00422 }
00423
00431 static int
00432 x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
00433 {
00434 struct x11_grab *s = s1->priv_data;
00435 Display *dpy = s->dpy;
00436 XImage *image = s->image;
00437 int x_off = s->x_off;
00438 int y_off = s->y_off;
00439
00440 int64_t curtime, delay;
00441 struct timespec ts;
00442
00443
00444 s->time_frame += INT64_C(1000000);
00445
00446
00447 for(;;) {
00448 curtime = av_gettime();
00449 delay = s->time_frame * av_q2d(s->time_base) - curtime;
00450 if (delay <= 0) {
00451 if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
00452 s->time_frame += INT64_C(1000000);
00453 }
00454 break;
00455 }
00456 ts.tv_sec = delay / 1000000;
00457 ts.tv_nsec = (delay % 1000000) * 1000;
00458 nanosleep(&ts, NULL);
00459 }
00460
00461 if (av_new_packet(pkt, s->frame_size) < 0) {
00462 return AVERROR(EIO);
00463 }
00464
00465 pkt->pts = curtime;
00466
00467 if(s->use_shm) {
00468 if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) {
00469 av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
00470 }
00471 } else {
00472 if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) {
00473 av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
00474 }
00475 }
00476
00477 {
00478 int pointer_x, pointer_y;
00479 get_pointer_coordinates(&pointer_x, &pointer_y, dpy, s1);
00480 paint_mouse_pointer(image, s, pointer_x, pointer_y);
00481 }
00482
00483
00484
00485 memcpy(pkt->data, image->data, s->frame_size);
00486 return s->frame_size;
00487 }
00488
00495 static int
00496 x11grab_read_close(AVFormatContext *s1)
00497 {
00498 struct x11_grab *x11grab = s1->priv_data;
00499
00500
00501 if (x11grab->use_shm) {
00502 XShmDetach(x11grab->dpy, &x11grab->shminfo);
00503 shmdt(x11grab->shminfo.shmaddr);
00504 shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
00505 }
00506
00507
00508 if (x11grab->image) {
00509 XDestroyImage(x11grab->image);
00510 x11grab->image = NULL;
00511 }
00512
00513
00514 XCloseDisplay(x11grab->dpy);
00515 return 0;
00516 }
00517
00519 AVInputFormat x11_grab_device_demuxer =
00520 {
00521 "x11grab",
00522 NULL_IF_CONFIG_SMALL("X11grab"),
00523 sizeof(struct x11_grab),
00524 NULL,
00525 x11grab_read_header,
00526 x11grab_read_packet,
00527 x11grab_read_close,
00528 .flags = AVFMT_NOFILE,
00529 };