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 "libavutil/log.h"
00040 #include "libavutil/opt.h"
00041 #include "libavutil/parseutils.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 #include "avdevice.h"
00052
00056 struct x11_grab
00057 {
00058 const AVClass *class;
00059 int frame_size;
00060 AVRational time_base;
00061 int64_t time_frame;
00063 char *video_size;
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 nomouse;
00074 char *framerate;
00075 };
00076
00088 static int
00089 x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
00090 {
00091 struct x11_grab *x11grab = s1->priv_data;
00092 Display *dpy;
00093 AVStream *st = NULL;
00094 enum PixelFormat input_pixfmt;
00095 XImage *image;
00096 int x_off = 0;
00097 int y_off = 0;
00098 int use_shm;
00099 char *dpyname, *offset;
00100 int ret = 0;
00101 AVRational framerate;
00102
00103 dpyname = av_strdup(s1->filename);
00104 offset = strchr(dpyname, '+');
00105 if (offset) {
00106 sscanf(offset, "%d,%d", &x_off, &y_off);
00107 x11grab->nomouse= strstr(offset, "nomouse");
00108 *offset= 0;
00109 }
00110
00111 if ((ret = av_parse_video_size(&x11grab->width, &x11grab->height, x11grab->video_size)) < 0) {
00112 av_log(s1, AV_LOG_ERROR, "Couldn't parse video size.\n");
00113 goto out;
00114 }
00115 if ((ret = av_parse_video_rate(&framerate, x11grab->framerate)) < 0) {
00116 av_log(s1, AV_LOG_ERROR, "Could not parse framerate: %s.\n", x11grab->framerate);
00117 goto out;
00118 }
00119 #if FF_API_FORMAT_PARAMETERS
00120 if (ap->width > 0)
00121 x11grab->width = ap->width;
00122 if (ap->height > 0)
00123 x11grab->height = ap->height;
00124 if (ap->time_base.num)
00125 framerate = (AVRational){ap->time_base.den, ap->time_base.num};
00126 #endif
00127 av_log(s1, AV_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
00128 s1->filename, dpyname, x_off, y_off, x11grab->width, x11grab->height);
00129
00130 dpy = XOpenDisplay(dpyname);
00131 av_freep(&dpyname);
00132 if(!dpy) {
00133 av_log(s1, AV_LOG_ERROR, "Could not open X display.\n");
00134 ret = AVERROR(EIO);
00135 goto out;
00136 }
00137
00138 st = av_new_stream(s1, 0);
00139 if (!st) {
00140 ret = AVERROR(ENOMEM);
00141 goto out;
00142 }
00143 av_set_pts_info(st, 64, 1, 1000000);
00144
00145 use_shm = XShmQueryExtension(dpy);
00146 av_log(s1, AV_LOG_INFO, "shared memory extension%s found\n", use_shm ? "" : " not");
00147
00148 if(use_shm) {
00149 int scr = XDefaultScreen(dpy);
00150 image = XShmCreateImage(dpy,
00151 DefaultVisual(dpy, scr),
00152 DefaultDepth(dpy, scr),
00153 ZPixmap,
00154 NULL,
00155 &x11grab->shminfo,
00156 x11grab->width, x11grab->height);
00157 x11grab->shminfo.shmid = shmget(IPC_PRIVATE,
00158 image->bytes_per_line * image->height,
00159 IPC_CREAT|0777);
00160 if (x11grab->shminfo.shmid == -1) {
00161 av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n");
00162 ret = AVERROR(ENOMEM);
00163 goto out;
00164 }
00165 x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0);
00166 x11grab->shminfo.readOnly = False;
00167
00168 if (!XShmAttach(dpy, &x11grab->shminfo)) {
00169 av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
00170
00171 ret = AVERROR(EIO);
00172 goto out;
00173 }
00174 } else {
00175 image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)),
00176 x_off,y_off,
00177 x11grab->width, x11grab->height,
00178 AllPlanes, ZPixmap);
00179 }
00180
00181 switch (image->bits_per_pixel) {
00182 case 8:
00183 av_log (s1, AV_LOG_DEBUG, "8 bit palette\n");
00184 input_pixfmt = PIX_FMT_PAL8;
00185 break;
00186 case 16:
00187 if ( image->red_mask == 0xf800 &&
00188 image->green_mask == 0x07e0 &&
00189 image->blue_mask == 0x001f ) {
00190 av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n");
00191 input_pixfmt = PIX_FMT_RGB565;
00192 } else if (image->red_mask == 0x7c00 &&
00193 image->green_mask == 0x03e0 &&
00194 image->blue_mask == 0x001f ) {
00195 av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n");
00196 input_pixfmt = PIX_FMT_RGB555;
00197 } else {
00198 av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00199 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);
00200 ret = AVERROR(EIO);
00201 goto out;
00202 }
00203 break;
00204 case 24:
00205 if ( image->red_mask == 0xff0000 &&
00206 image->green_mask == 0x00ff00 &&
00207 image->blue_mask == 0x0000ff ) {
00208 input_pixfmt = PIX_FMT_BGR24;
00209 } else if ( image->red_mask == 0x0000ff &&
00210 image->green_mask == 0x00ff00 &&
00211 image->blue_mask == 0xff0000 ) {
00212 input_pixfmt = PIX_FMT_RGB24;
00213 } else {
00214 av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
00215 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);
00216 ret = AVERROR(EIO);
00217 goto out;
00218 }
00219 break;
00220 case 32:
00221 #if 0
00222 GetColorInfo (image, &c_info);
00223 if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) {
00224
00225
00226
00227
00228
00229
00230 input_pixfmt = PIX_FMT_ARGB32;
00231 } else {
00232 av_log(s1, AV_LOG_ERROR,"image depth %i not supported ... aborting\n", image->bits_per_pixel);
00233 return AVERROR(EIO);
00234 }
00235 #endif
00236 input_pixfmt = PIX_FMT_RGB32;
00237 break;
00238 default:
00239 av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
00240 ret = AVERROR(EINVAL);
00241 goto out;
00242 }
00243
00244 x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel/8;
00245 x11grab->dpy = dpy;
00246 x11grab->time_base = (AVRational){framerate.den, framerate.num};
00247 x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base);
00248 x11grab->x_off = x_off;
00249 x11grab->y_off = y_off;
00250 x11grab->image = image;
00251 x11grab->use_shm = use_shm;
00252
00253 st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
00254 st->codec->codec_id = CODEC_ID_RAWVIDEO;
00255 st->codec->width = x11grab->width;
00256 st->codec->height = x11grab->height;
00257 st->codec->pix_fmt = input_pixfmt;
00258 st->codec->time_base = x11grab->time_base;
00259 st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(x11grab->time_base) * 8;
00260
00261 out:
00262 return ret;
00263 }
00264
00272 static void
00273 paint_mouse_pointer(XImage *image, struct x11_grab *s)
00274 {
00275 int x_off = s->x_off;
00276 int y_off = s->y_off;
00277 int width = s->width;
00278 int height = s->height;
00279 Display *dpy = s->dpy;
00280 XFixesCursorImage *xcim;
00281 int x, y;
00282 int line, column;
00283 int to_line, to_column;
00284 int pixstride = image->bits_per_pixel >> 3;
00285
00286
00287
00288
00289 uint8_t *pix = image->data;
00290
00291
00292 if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32)
00293 return;
00294
00295 xcim = XFixesGetCursorImage(dpy);
00296
00297 x = xcim->x - xcim->xhot;
00298 y = xcim->y - xcim->yhot;
00299
00300 to_line = FFMIN((y + xcim->height), (height + y_off));
00301 to_column = FFMIN((x + xcim->width), (width + x_off));
00302
00303 for (line = FFMAX(y, y_off); line < to_line; line++) {
00304 for (column = FFMAX(x, x_off); column < to_column; column++) {
00305 int xcim_addr = (line - y) * xcim->width + column - x;
00306 int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
00307 int r = (uint8_t)(xcim->pixels[xcim_addr] >> 0);
00308 int g = (uint8_t)(xcim->pixels[xcim_addr] >> 8);
00309 int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
00310 int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);
00311
00312 if (a == 255) {
00313 pix[image_addr+0] = r;
00314 pix[image_addr+1] = g;
00315 pix[image_addr+2] = b;
00316 } else if (a) {
00317
00318 pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
00319 pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
00320 pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
00321 }
00322 }
00323 }
00324
00325 XFree(xcim);
00326 xcim = NULL;
00327 }
00328
00329
00340 static int
00341 xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y)
00342 {
00343 xGetImageReply rep;
00344 xGetImageReq *req;
00345 long nbytes;
00346
00347 if (!image) {
00348 return 0;
00349 }
00350
00351 LockDisplay(dpy);
00352 GetReq(GetImage, req);
00353
00354
00355 req->drawable = d;
00356 req->x = x;
00357 req->y = y;
00358 req->width = image->width;
00359 req->height = image->height;
00360 req->planeMask = (unsigned int)AllPlanes;
00361 req->format = ZPixmap;
00362
00363 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) {
00364 UnlockDisplay(dpy);
00365 SyncHandle();
00366 return 0;
00367 }
00368
00369 nbytes = (long)rep.length << 2;
00370 _XReadPad(dpy, image->data, nbytes);
00371
00372 UnlockDisplay(dpy);
00373 SyncHandle();
00374 return 1;
00375 }
00376
00384 static int
00385 x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt)
00386 {
00387 struct x11_grab *s = s1->priv_data;
00388 Display *dpy = s->dpy;
00389 XImage *image = s->image;
00390 int x_off = s->x_off;
00391 int y_off = s->y_off;
00392
00393 int64_t curtime, delay;
00394 struct timespec ts;
00395
00396
00397 s->time_frame += INT64_C(1000000);
00398
00399
00400 for(;;) {
00401 curtime = av_gettime();
00402 delay = s->time_frame * av_q2d(s->time_base) - curtime;
00403 if (delay <= 0) {
00404 if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) {
00405 s->time_frame += INT64_C(1000000);
00406 }
00407 break;
00408 }
00409 ts.tv_sec = delay / 1000000;
00410 ts.tv_nsec = (delay % 1000000) * 1000;
00411 nanosleep(&ts, NULL);
00412 }
00413
00414 av_init_packet(pkt);
00415 pkt->data = image->data;
00416 pkt->size = s->frame_size;
00417 pkt->pts = curtime;
00418
00419 if(s->use_shm) {
00420 if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) {
00421 av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n");
00422 }
00423 } else {
00424 if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) {
00425 av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n");
00426 }
00427 }
00428
00429 if(!s->nomouse){
00430 paint_mouse_pointer(image, s);
00431 }
00432
00433 return s->frame_size;
00434 }
00435
00442 static int
00443 x11grab_read_close(AVFormatContext *s1)
00444 {
00445 struct x11_grab *x11grab = s1->priv_data;
00446
00447
00448 if (x11grab->use_shm) {
00449 XShmDetach(x11grab->dpy, &x11grab->shminfo);
00450 shmdt(x11grab->shminfo.shmaddr);
00451 shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL);
00452 }
00453
00454
00455 if (x11grab->image) {
00456 XDestroyImage(x11grab->image);
00457 x11grab->image = NULL;
00458 }
00459
00460
00461 XCloseDisplay(x11grab->dpy);
00462 return 0;
00463 }
00464
00465 #define OFFSET(x) offsetof(struct x11_grab, x)
00466 #define DEC AV_OPT_FLAG_DECODING_PARAM
00467 static const AVOption options[] = {
00468 { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), FF_OPT_TYPE_STRING, {.str = "vga"}, 0, 0, DEC },
00469 { "framerate", "", OFFSET(framerate), FF_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC },
00470 { NULL },
00471 };
00472
00473 static const AVClass x11_class = {
00474 .class_name = "X11grab indev",
00475 .item_name = av_default_item_name,
00476 .option = options,
00477 .version = LIBAVUTIL_VERSION_INT,
00478 };
00479
00481 AVInputFormat ff_x11_grab_device_demuxer =
00482 {
00483 "x11grab",
00484 NULL_IF_CONFIG_SMALL("X11grab"),
00485 sizeof(struct x11_grab),
00486 NULL,
00487 x11grab_read_header,
00488 x11grab_read_packet,
00489 x11grab_read_close,
00490 .flags = AVFMT_NOFILE,
00491 .priv_class = &x11_class,
00492 };