Libav 0.7.1
|
00001 /* 00002 * X11 video grab interface 00003 * 00004 * This file is part of Libav. 00005 * 00006 * Libav integration: 00007 * Copyright (C) 2006 Clemens Fruhwirth <clemens@endorphin.org> 00008 * Edouard Gomez <ed.gomez@free.fr> 00009 * 00010 * This file contains code from grab.c: 00011 * Copyright (c) 2000-2001 Fabrice Bellard 00012 * 00013 * This file contains code from the xvidcap project: 00014 * Copyright (C) 1997-1998 Rasca, Berlin 00015 * 2003-2004 Karl H. Beckers, Frankfurt 00016 * 00017 * Libav is free software; you can redistribute it and/or modify 00018 * it under the terms of the GNU General Public License as published by 00019 * the Free Software Foundation; either version 2 of the License, or 00020 * (at your option) any later version. 00021 * 00022 * Libav is distributed in the hope that it will be useful, 00023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00025 * GNU General Public License for more details. 00026 * 00027 * You should have received a copy of the GNU General Public License 00028 * along with Libav; if not, write to the Free Software 00029 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00030 */ 00031 00038 #include "config.h" 00039 #include "libavformat/avformat.h" 00040 #include "libavutil/log.h" 00041 #include "libavutil/opt.h" 00042 #include "libavutil/parseutils.h" 00043 #include <time.h> 00044 #include <X11/X.h> 00045 #include <X11/Xlib.h> 00046 #include <X11/Xlibint.h> 00047 #include <X11/Xproto.h> 00048 #include <X11/Xutil.h> 00049 #include <sys/shm.h> 00050 #include <X11/extensions/XShm.h> 00051 #include <X11/extensions/Xfixes.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 *param, *offset; 00100 int ret = 0; 00101 AVRational framerate; 00102 00103 param = av_strdup(s1->filename); 00104 offset = strchr(param, '+'); 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, param, x_off, y_off, x11grab->width, x11grab->height); 00129 00130 dpy = XOpenDisplay(param); 00131 if(!dpy) { 00132 av_log(s1, AV_LOG_ERROR, "Could not open X display.\n"); 00133 ret = AVERROR(EIO); 00134 goto out; 00135 } 00136 00137 st = av_new_stream(s1, 0); 00138 if (!st) { 00139 ret = AVERROR(ENOMEM); 00140 goto out; 00141 } 00142 av_set_pts_info(st, 64, 1, 1000000); /* 64 bits pts in us */ 00143 00144 use_shm = XShmQueryExtension(dpy); 00145 av_log(s1, AV_LOG_INFO, "shared memory extension %s found\n", use_shm ? "" : "not"); 00146 00147 if(use_shm) { 00148 int scr = XDefaultScreen(dpy); 00149 image = XShmCreateImage(dpy, 00150 DefaultVisual(dpy, scr), 00151 DefaultDepth(dpy, scr), 00152 ZPixmap, 00153 NULL, 00154 &x11grab->shminfo, 00155 x11grab->width, x11grab->height); 00156 x11grab->shminfo.shmid = shmget(IPC_PRIVATE, 00157 image->bytes_per_line * image->height, 00158 IPC_CREAT|0777); 00159 if (x11grab->shminfo.shmid == -1) { 00160 av_log(s1, AV_LOG_ERROR, "Fatal: Can't get shared memory!\n"); 00161 ret = AVERROR(ENOMEM); 00162 goto out; 00163 } 00164 x11grab->shminfo.shmaddr = image->data = shmat(x11grab->shminfo.shmid, 0, 0); 00165 x11grab->shminfo.readOnly = False; 00166 00167 if (!XShmAttach(dpy, &x11grab->shminfo)) { 00168 av_log(s1, AV_LOG_ERROR, "Fatal: Failed to attach shared memory!\n"); 00169 /* needs some better error subroutine :) */ 00170 ret = AVERROR(EIO); 00171 goto out; 00172 } 00173 } else { 00174 image = XGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), 00175 x_off,y_off, 00176 x11grab->width, x11grab->height, 00177 AllPlanes, ZPixmap); 00178 } 00179 00180 switch (image->bits_per_pixel) { 00181 case 8: 00182 av_log (s1, AV_LOG_DEBUG, "8 bit palette\n"); 00183 input_pixfmt = PIX_FMT_PAL8; 00184 break; 00185 case 16: 00186 if ( image->red_mask == 0xf800 && 00187 image->green_mask == 0x07e0 && 00188 image->blue_mask == 0x001f ) { 00189 av_log (s1, AV_LOG_DEBUG, "16 bit RGB565\n"); 00190 input_pixfmt = PIX_FMT_RGB565; 00191 } else if (image->red_mask == 0x7c00 && 00192 image->green_mask == 0x03e0 && 00193 image->blue_mask == 0x001f ) { 00194 av_log(s1, AV_LOG_DEBUG, "16 bit RGB555\n"); 00195 input_pixfmt = PIX_FMT_RGB555; 00196 } else { 00197 av_log(s1, AV_LOG_ERROR, "RGB ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel); 00198 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); 00199 ret = AVERROR(EIO); 00200 goto out; 00201 } 00202 break; 00203 case 24: 00204 if ( image->red_mask == 0xff0000 && 00205 image->green_mask == 0x00ff00 && 00206 image->blue_mask == 0x0000ff ) { 00207 input_pixfmt = PIX_FMT_BGR24; 00208 } else if ( image->red_mask == 0x0000ff && 00209 image->green_mask == 0x00ff00 && 00210 image->blue_mask == 0xff0000 ) { 00211 input_pixfmt = PIX_FMT_RGB24; 00212 } else { 00213 av_log(s1, AV_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel); 00214 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); 00215 ret = AVERROR(EIO); 00216 goto out; 00217 } 00218 break; 00219 case 32: 00220 #if 0 00221 GetColorInfo (image, &c_info); 00222 if ( c_info.alpha_mask == 0xff000000 && image->green_mask == 0x0000ff00) { 00223 /* byte order is relevant here, not endianness 00224 * endianness is handled by avcodec, but atm no such thing 00225 * as having ABGR, instead of ARGB in a word. Since we 00226 * need this for Solaris/SPARC, but need to do the conversion 00227 * for every frame we do it outside of this loop, cf. below 00228 * this matches both ARGB32 and ABGR32 */ 00229 input_pixfmt = PIX_FMT_ARGB32; 00230 } else { 00231 av_log(s1, AV_LOG_ERROR,"image depth %i not supported ... aborting\n", image->bits_per_pixel); 00232 return AVERROR(EIO); 00233 } 00234 #endif 00235 input_pixfmt = PIX_FMT_RGB32; 00236 break; 00237 default: 00238 av_log(s1, AV_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel); 00239 ret = AVERROR(EINVAL); 00240 goto out; 00241 } 00242 00243 x11grab->frame_size = x11grab->width * x11grab->height * image->bits_per_pixel/8; 00244 x11grab->dpy = dpy; 00245 x11grab->time_base = (AVRational){framerate.den, framerate.num}; 00246 x11grab->time_frame = av_gettime() / av_q2d(x11grab->time_base); 00247 x11grab->x_off = x_off; 00248 x11grab->y_off = y_off; 00249 x11grab->image = image; 00250 x11grab->use_shm = use_shm; 00251 00252 st->codec->codec_type = AVMEDIA_TYPE_VIDEO; 00253 st->codec->codec_id = CODEC_ID_RAWVIDEO; 00254 st->codec->width = x11grab->width; 00255 st->codec->height = x11grab->height; 00256 st->codec->pix_fmt = input_pixfmt; 00257 st->codec->time_base = x11grab->time_base; 00258 st->codec->bit_rate = x11grab->frame_size * 1/av_q2d(x11grab->time_base) * 8; 00259 00260 out: 00261 return ret; 00262 } 00263 00271 static void 00272 paint_mouse_pointer(XImage *image, struct x11_grab *s) 00273 { 00274 int x_off = s->x_off; 00275 int y_off = s->y_off; 00276 int width = s->width; 00277 int height = s->height; 00278 Display *dpy = s->dpy; 00279 XFixesCursorImage *xcim; 00280 int x, y; 00281 int line, column; 00282 int to_line, to_column; 00283 int pixstride = image->bits_per_pixel >> 3; 00284 /* Warning: in its insanity, xlib provides unsigned image data through a 00285 * char* pointer, so we have to make it uint8_t to make things not break. 00286 * Anyone who performs further investigation of the xlib API likely risks 00287 * permanent brain damage. */ 00288 uint8_t *pix = image->data; 00289 00290 /* Code doesn't currently support 16-bit or PAL8 */ 00291 if (image->bits_per_pixel != 24 && image->bits_per_pixel != 32) 00292 return; 00293 00294 xcim = XFixesGetCursorImage(dpy); 00295 00296 x = xcim->x - xcim->xhot; 00297 y = xcim->y - xcim->yhot; 00298 00299 to_line = FFMIN((y + xcim->height), (height + y_off)); 00300 to_column = FFMIN((x + xcim->width), (width + x_off)); 00301 00302 for (line = FFMAX(y, y_off); line < to_line; line++) { 00303 for (column = FFMAX(x, x_off); column < to_column; column++) { 00304 int xcim_addr = (line - y) * xcim->width + column - x; 00305 int image_addr = ((line - y_off) * width + column - x_off) * pixstride; 00306 int r = (uint8_t)(xcim->pixels[xcim_addr] >> 0); 00307 int g = (uint8_t)(xcim->pixels[xcim_addr] >> 8); 00308 int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16); 00309 int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24); 00310 00311 if (a == 255) { 00312 pix[image_addr+0] = r; 00313 pix[image_addr+1] = g; 00314 pix[image_addr+2] = b; 00315 } else if (a) { 00316 /* pixel values from XFixesGetCursorImage come premultiplied by alpha */ 00317 pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255; 00318 pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255; 00319 pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255; 00320 } 00321 } 00322 } 00323 00324 XFree(xcim); 00325 xcim = NULL; 00326 } 00327 00328 00339 static int 00340 xget_zpixmap(Display *dpy, Drawable d, XImage *image, int x, int y) 00341 { 00342 xGetImageReply rep; 00343 xGetImageReq *req; 00344 long nbytes; 00345 00346 if (!image) { 00347 return 0; 00348 } 00349 00350 LockDisplay(dpy); 00351 GetReq(GetImage, req); 00352 00353 /* First set up the standard stuff in the request */ 00354 req->drawable = d; 00355 req->x = x; 00356 req->y = y; 00357 req->width = image->width; 00358 req->height = image->height; 00359 req->planeMask = (unsigned int)AllPlanes; 00360 req->format = ZPixmap; 00361 00362 if (!_XReply(dpy, (xReply *)&rep, 0, xFalse) || !rep.length) { 00363 UnlockDisplay(dpy); 00364 SyncHandle(); 00365 return 0; 00366 } 00367 00368 nbytes = (long)rep.length << 2; 00369 _XReadPad(dpy, image->data, nbytes); 00370 00371 UnlockDisplay(dpy); 00372 SyncHandle(); 00373 return 1; 00374 } 00375 00383 static int 00384 x11grab_read_packet(AVFormatContext *s1, AVPacket *pkt) 00385 { 00386 struct x11_grab *s = s1->priv_data; 00387 Display *dpy = s->dpy; 00388 XImage *image = s->image; 00389 int x_off = s->x_off; 00390 int y_off = s->y_off; 00391 00392 int64_t curtime, delay; 00393 struct timespec ts; 00394 00395 /* Calculate the time of the next frame */ 00396 s->time_frame += INT64_C(1000000); 00397 00398 /* wait based on the frame rate */ 00399 for(;;) { 00400 curtime = av_gettime(); 00401 delay = s->time_frame * av_q2d(s->time_base) - curtime; 00402 if (delay <= 0) { 00403 if (delay < INT64_C(-1000000) * av_q2d(s->time_base)) { 00404 s->time_frame += INT64_C(1000000); 00405 } 00406 break; 00407 } 00408 ts.tv_sec = delay / 1000000; 00409 ts.tv_nsec = (delay % 1000000) * 1000; 00410 nanosleep(&ts, NULL); 00411 } 00412 00413 av_init_packet(pkt); 00414 pkt->data = image->data; 00415 pkt->size = s->frame_size; 00416 pkt->pts = curtime; 00417 00418 if(s->use_shm) { 00419 if (!XShmGetImage(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off, AllPlanes)) { 00420 av_log (s1, AV_LOG_INFO, "XShmGetImage() failed\n"); 00421 } 00422 } else { 00423 if (!xget_zpixmap(dpy, RootWindow(dpy, DefaultScreen(dpy)), image, x_off, y_off)) { 00424 av_log (s1, AV_LOG_INFO, "XGetZPixmap() failed\n"); 00425 } 00426 } 00427 00428 if(!s->nomouse){ 00429 paint_mouse_pointer(image, s); 00430 } 00431 00432 return s->frame_size; 00433 } 00434 00441 static int 00442 x11grab_read_close(AVFormatContext *s1) 00443 { 00444 struct x11_grab *x11grab = s1->priv_data; 00445 00446 /* Detach cleanly from shared mem */ 00447 if (x11grab->use_shm) { 00448 XShmDetach(x11grab->dpy, &x11grab->shminfo); 00449 shmdt(x11grab->shminfo.shmaddr); 00450 shmctl(x11grab->shminfo.shmid, IPC_RMID, NULL); 00451 } 00452 00453 /* Destroy X11 image */ 00454 if (x11grab->image) { 00455 XDestroyImage(x11grab->image); 00456 x11grab->image = NULL; 00457 } 00458 00459 /* Free X11 display */ 00460 XCloseDisplay(x11grab->dpy); 00461 return 0; 00462 } 00463 00464 #define OFFSET(x) offsetof(struct x11_grab, x) 00465 #define DEC AV_OPT_FLAG_DECODING_PARAM 00466 static const AVOption options[] = { 00467 { "video_size", "A string describing frame size, such as 640x480 or hd720.", OFFSET(video_size), FF_OPT_TYPE_STRING, {.str = "vga"}, 0, 0, DEC }, 00468 { "framerate", "", OFFSET(framerate), FF_OPT_TYPE_STRING, {.str = "ntsc"}, 0, 0, DEC }, 00469 { NULL }, 00470 }; 00471 00472 static const AVClass x11_class = { 00473 .class_name = "X11grab indev", 00474 .item_name = av_default_item_name, 00475 .option = options, 00476 .version = LIBAVUTIL_VERSION_INT, 00477 }; 00478 00480 AVInputFormat ff_x11_grab_device_demuxer = 00481 { 00482 "x11grab", 00483 NULL_IF_CONFIG_SMALL("X11grab"), 00484 sizeof(struct x11_grab), 00485 NULL, 00486 x11grab_read_header, 00487 x11grab_read_packet, 00488 x11grab_read_close, 00489 .flags = AVFMT_NOFILE, 00490 .priv_class = &x11_class, 00491 };