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 #define _XOPEN_SOURCE 600
00039
00040 #include "config.h"
00041 #include "libavformat/avformat.h"
00042 #include <time.h>
00043 #include <X11/X.h>
00044 #include <X11/Xlib.h>
00045 #include <X11/Xlibint.h>
00046 #include <X11/Xproto.h>
00047 #include <X11/Xutil.h>
00048 #include <sys/shm.h>
00049 #include <X11/extensions/XShm.h>
00050 #include <X11/extensions/Xfixes.h>
00051
00055 struct x11_grab
00056 {
00057 int frame_size;
00058 AVRational time_base;
00059 int64_t time_frame;
00061 int height;
00062 int width;
00063 int x_off;
00064 int y_off;
00066 Display *dpy;
00067 XImage *image;
00068 int use_shm;
00069 XShmSegmentInfo shminfo;
00070 int nomouse;
00071 };
00072
00084 static int
00085 x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
00086 {
00087 struct x11_grab *x11grab = s1->priv_data;
00088 Display *dpy;
00089 AVStream *st = NULL;
00090 enum PixelFormat input_pixfmt;
00091 XImage *image;
00092 int x_off = 0;
00093 int y_off = 0;
00094 int use_shm;
00095 char *param, *offset;
00096
00097 param = av_strdup(s1->filename);
00098 offset = strchr(param, '+');
00099 if (offset) {
00100 sscanf(offset, "%d,%d", &x_off, &y_off);
00101 x11grab->nomouse= strstr(offset, "nomouse");
00102 *offset= 0;
00103 }
00104
00105 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);
00106
00107 dpy = XOpenDisplay(param);
00108 if(!dpy) {
00109 av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
00110 return AVERROR(EIO);
00111 }
00112
00113 if (ap->width <= 0 || ap->height <= 0 || ap->time_base.den <= 0) {
00114 av_log(s1, AV_LOG_ERROR, "AVParameters don't have video size and/or rate. Use -s and -r.\n");
00115 return AVERROR(EIO);
00116 }
00117
00118 st = av_new_stream(s1, 0);
00119 if (!st) {
00120 return AVERROR(ENOMEM);
00121 }
00122 av_set_pts_info(st, 64, 1, 1000000);
00123
00124 use_shm = XShmQueryExtension(dpy);
00125 av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not");
00126
00127 if(use_shm) {
00128 int scr = XDefaultScreen(dpy);
00129 image = XShmCreateImage(dpy,
00130 DefaultVisual(dpy, scr),
00131 DefaultDepth(dpy, scr),
00132 ZPixmap,
00133 NULL,
00134 &x11grab->shminfo,
00135 ap->width, ap->height);
00136 x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
00137 image->bytes_per_line * image->height,
00138 IPC_CREAT|0777);
00139 if (x11grab->shminfo.shmid == -1) {
00140 av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
00141 return AVERROR(ENOMEM);
00142 }
00143 x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
00144 x11grab->shminfo.readOnly = False;
00145
00146 if (!XShmAttach(dpy, &x11grab->shminfo)) {
00147 av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
00148
00149 return AVERROR(EIO);
00150 }
00151 } else {
00152 image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
00153 x_off,y_off,
00154 ap->width,ap->height,
00155 AllPlanes, ZPixmap);
00156 }
00157
00158 switch (image->bits_per_pixel) {
00159 case 8:
00160 av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
00161 input_pixfmt = PIX_FMT_PAL8;
00162 break;
00163 case 16:
00164 if ( image->red_mask == 0xf800 &&
00165 image->green_mask == 0x07e0 &&
00166 image->blue_mask == 0x001f ) {
00167 av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
00168 input_pixfmt = PIX_FMT_RGB565;
00169 } else if (image->red_mask == 0x7c00 &&
00170 image->green_mask == 0x03e0 &&
00171 image->blue_mask == 0x001f ) {
00172 av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
00173 input_pixfmt = PIX_FMT_RGB555;
00174 } else {
00175 av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00176 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);
00177 return AVERROR(EIO);
00178 }
00179 break;
00180 case 24:
00181 if ( image->red_mask == 0xff0000 &&
00182 image->green_mask == 0x00ff00 &&
00183 image->blue_mask == 0x0000ff ) {
00184 input_pixfmt = PIX_FMT_BGR24;
00185 } else if ( image->red_mask == 0x0000ff &&
00186 image->green_mask == 0x00ff00 &&
00187 image->blue_mask == 0xff0000 ) {
00188 input_pixfmt = PIX_FMT_RGB24;
00189 } else {
00190 av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00191 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);
00192 return AVERROR(EIO);
00193 }
00194 break;
00195 case 32:
00196 #if 0
00197 GetColorInfo (image, &c_info);
00198 if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) {
00199
00200
00201
00202
00203
00204
00205 input_pixfmt = PIX_FMT_ARGB32;
00206 } else {
00207 av_log(s1, AV_LOG_ERROR,"image depth %i not supported ... aborting\n", image->bits_per_pixel);
00208 return AVERROR(EIO);
00209 }
00210 #endif
00211 input_pixfmt = PIX_FMT_RGB32;
00212 break;
00213 default:
00214 av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
00215 return -1;
00216 }
00217
00218 x11grab->frame_size = ap->width * ap->height * image->bits_per_pixel/8;
00219 x11grab->dpy = dpy;
00220 x11grab->width = ap->width;
00221 x11grab->height = ap->height;
00222 x11grab->time_base = ap->time_base;
00223 x11grab->time_frame = av_gettime() / av_q2d(ap->time_base);
00224 x11grab->x_off = x_off;
00225 x11grab->y_off = y_off;
00226 x11grab->image = image;
00227 x11grab->use_shm = use_shm;
00228
00229 st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00230 st->codec->codec_id = CODEC_ID_RAWVIDEO;
00231 st->codec->width = ap->width;
00232 st->codec->height = ap->height;
00233 st->codec->pix_fmt = input_pixfmt;
00234 st->codec->time_base = ap->time_base;
00235 st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(ap->time_base) * 8;
00236
00237 return 0;
00238 }
00239
00247 static void
00248 paint_mouse_pointer(XImage *image, struct x11_grab *s)
00249 {
00250 int x_off = s->x_off;
00251 int y_off = s->y_off;
00252 int width = s->width;
00253 int height = s->height;
00254 Display *dpy = s->dpy;
00255 XFixesCursorImage *xcim;
00256 int x, y;
00257 int line, column;
00258 int to_line, to_column;
00259 int pixstride = image->bits_per_pixel >> 3;
00260
00261
00262
00263
00264 uint8_t *pix = image->data;
00265
00266
00267 if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32)
00268 return;
00269
00270 xcim = XFixesGetCursorImage(dpy);
00271
00272 x = xcim->x - xcim->xhot;
00273 y = xcim->y - xcim->yhot;
00274
00275 to_line = FFMIN((y + xcim->height), (height + y_off));
00276 to_column = FFMIN((x + xcim->width), (width + x_off));
00277
00278 for (line = FFMAX(y, y_off); line < to_line; line++) {
00279 for (column = FFMAX(x, x_off); column < to_column; column++) {
00280 int xcim_addr = (line - y) * xcim->width + column - x;
00281 int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
00282 int r = (uint8_t)(xcim->pixels[xcim_addr] >> 0);
00283 int g = (uint8_t)(xcim->pixels[xcim_addr] >> 8);
00284 int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
00285 int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);
00286
00287 if (a == 255) {
00288 pix[image_addr+0] = r;
00289 pix[image_addr+1] = g;
00290 pix[image_addr+2] = b;
00291 } else if (a) {
00292
00293 pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
00294 pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
00295 pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
00296 }
00297 }
00298 }
00299
00300 XFree(xcim);
00301 xcim = NULL;
00302 }
00303
00304
00315 static int
00316 xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
00317 {
00318 xGetImageReply rep;
00319 xGetImageReq *req;
00320 long nbytes;
00321
00322 if (!image) {
00323 return 0;
00324 }
00325
00326 LockDisplay(dpy);
00327 GetReq(GetImage, req);
00328
00329
00330 req->drawable = d;
00331 req->x = x;
00332 req->y = y;
00333 req->width = image->width;
00334 req->height = image->height;
00335 req->planeMask = (unsigned int)AllPlanes;
00336 req->format = ZPixmap;
00337
00338 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
00339 UnlockDisplay(dpy);
00340 SyncHandle();
00341 return 0;
00342 }
00343
00344 nbytes = (long)rep.length << 2;
00345 _XReadPad(dpy, image->data, nbytes);
00346
00347 UnlockDisplay(dpy);
00348 SyncHandle();
00349 return 1;
00350 }
00351
00359 static int
00360 x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
00361 {
00362 struct x11_grab *s = s1->priv_data;
00363 Display *dpy = s->dpy;
00364 XImage *image = s->image;
00365 int x_off = s->x_off;
00366 int y_off = s->y_off;
00367
00368 int64_t curtime, delay;
00369 struct timespec ts;
00370
00371
00372 s->time_frame += INT64_C(1000000);
00373
00374
00375 for(;;) {
00376 curtime = av_gettime();
00377 delay = s->time_frame * av_q2d(s->time_base) - curtime;
00378 if (delay <= 0) {
00379 if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
00380 s->time_frame += INT64_C(1000000);
00381 }
00382 break;
00383 }
00384 ts.tv_sec = delay / 1000000;
00385 ts.tv_nsec = (delay % 1000000) * 1000;
00386 nanosleep(&ts, NULL);
00387 }
00388
00389 if (av_new_packet(pkt, s->frame_size) < 0) {
00390 return AVERROR(EIO);
00391 }
00392
00393 pkt->pts = curtime;
00394
00395 if(s->use_shm) {
00396 if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) {
00397 av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
00398 }
00399 } else {
00400 if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) {
00401 av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
00402 }
00403 }
00404
00405 if(!s->nomouse){
00406 paint_mouse_pointer(image, s);
00407 }
00408
00409
00410
00411 memcpy(pkt->data, image->data, s->frame_size);
00412 return s->frame_size;
00413 }
00414
00421 static int
00422 x11grab_read_close(AVFormatContext *s1)
00423 {
00424 struct x11_grab *x11grab = s1->priv_data;
00425
00426
00427 if (x11grab->use_shm) {
00428 XShmDetach(x11grab->dpy, &x11grab->shminfo);
00429 shmdt(x11grab->shminfo.shmaddr);
00430 shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
00431 }
00432
00433
00434 if (x11grab->image) {
00435 XDestroyImage(x11grab->image);
00436 x11grab->image = NULL;
00437 }
00438
00439
00440 XCloseDisplay(x11grab->dpy);
00441 return 0;
00442 }
00443
00445 AVInputFormat ff_x11_grab_device_demuxer =
00446 {
00447 "x11grab",
00448 NULL_IF_CONFIG_SMALL("X11grab"),
00449 sizeof(struct x11_grab),
00450 NULL,
00451 x11grab_read_header,
00452 x11grab_read_packet,
00453 x11grab_read_close,
00454 .flags = AVFMT_NOFILE,
00455 };