• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • Examples
  • File List
  • Globals

libavdevice/x11grab.c

Go to the documentation of this file.
00001 /*
00002  * X11 video grab interface
00003  *
00004  * This file is part of FFmpeg.
00005  *
00006  * FFmpeg 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  * FFmpeg 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  * FFmpeg 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 FFmpeg; 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 "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); /* 64 bits pts in us */
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             /* needs some better error subroutine :) */
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             /* byte order is relevant here, not endianness
00225              * endianness is handled by avcodec, but atm no such thing
00226              * as having ABGR, instead of ARGB in a word. Since we
00227              * need this for Solaris/SPARC, but need to do the conversion
00228              * for every frame we do it outside of this loop, cf. below
00229              * this matches both ARGB32 and ABGR32 */
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     /* Warning: in its insanity, xlib provides unsigned image data through a
00286      * char* pointer, so we have to make it uint8_t to make things not break.
00287      * Anyone who performs further investigation of the xlib API likely risks
00288      * permanent brain damage. */
00289     uint8_t *pix = image->data;
00290 
00291     /* Code doesn't currently support 16-bit or PAL8 */
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                 /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
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     /* First set up the standard stuff in the request */
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     /* Calculate the time of the next frame */
00397     s->time_frame += INT64_C(1000000);
00398 
00399     /* wait based on the frame rate */
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     /* Detach cleanly from shared mem */
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     /* Destroy X11 image */
00455     if (x11grab->image) {
00456         XDestroyImage(x11grab->image);
00457         x11grab->image = NULL;
00458     }
00459 
00460     /* Free X11 display */
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 };

Generated on Fri Feb 22 2013 07:24:30 for FFmpeg by  doxygen 1.7.1