[x264-devel] [PATCH 1/2] Add X11 input module (like ffmpeg's x11grab)

Corentin Chary corentin.chary at gmail.com
Fri Aug 5 16:45:13 CEST 2011


---
 Makefile      |    4 +
 configure     |   36 +++++-
 input/input.h |    1 +
 input/x11.c   |  436 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 x264.c        |   13 ++
 5 files changed, 487 insertions(+), 3 deletions(-)
 create mode 100644 input/x11.c

diff --git a/Makefile b/Makefile
index 5831091..e66acf6 100644
--- a/Makefile
+++ b/Makefile
@@ -51,6 +51,10 @@ ifneq ($(findstring HAVE_FFMS 1, $(CONFIG)),)
 SRCCLI += input/ffms.c
 endif
 
+ifneq ($(findstring HAVE_X11 1, $(CONFIG)),)
+SRCCLI += input/x11.c
+endif
+
 ifneq ($(findstring HAVE_GPAC 1, $(CONFIG)),)
 SRCCLI += output/mp4.c
 endif
diff --git a/configure b/configure
index dc1179e..aa95d24 100755
--- a/configure
+++ b/configure
@@ -49,6 +49,7 @@ External library support:
   --disable-lavf           disable libavformat support
   --disable-ffms           disable ffmpegsource support
   --disable-gpac           disable gpac support
+  --disable-x11            disables X11 support
 
 EOF
 exit 1
@@ -128,7 +129,7 @@ cc_check() {
         log_check "for $3 in $1";
     fi
     rm -f conftest.c
-    [ -n "$1" ] && echo "#include <$1>" > conftest.c
+    [ -n "$1" ] && for i in $1; do echo "#include <$i>"; done > conftest.c
     echo "int main () { $3 return 0; }" >> conftest.c
     if [ $compiler = ICL ]; then
         cc_cmd="$CC conftest.c $CFLAGS $2 -link $(icl_ldflags $2 $LDFLAGSCLI $LDFLAGS)"
@@ -229,6 +230,7 @@ gpac="auto"
 gpl="yes"
 thread="auto"
 swscale="auto"
+x11="auto"
 asm="auto"
 interlaced="yes"
 debug="no"
@@ -249,7 +251,7 @@ cross_prefix=""
 EXE=""
 
 # list of all preprocessor HAVE values we can define
-CONFIG_HAVE="MALLOC_H ALTIVEC ALTIVEC_H MMX ARMV6 ARMV6T2 NEON BEOSTHREAD POSIXTHREAD WIN32THREAD THREAD LOG2F VISUALIZE SWSCALE LAVF FFMS GPAC GF_MALLOC AVS GPL VECTOREXT INTERLACED"
+CONFIG_HAVE="MALLOC_H ALTIVEC ALTIVEC_H MMX ARMV6 ARMV6T2 NEON BEOSTHREAD POSIXTHREAD WIN32THREAD THREAD LOG2F VISUALIZE SWSCALE X11 LAVF FFMS GPAC GF_MALLOC AVS GPL VECTOREXT INTERLACED"
 
 # parse options
 
@@ -322,6 +324,9 @@ for opt do
         --disable-swscale)
             swscale="no"
             ;;
+        --disable-x11)
+            x11="no"
+            ;;
         --enable-debug)
             debug="yes"
             ;;
@@ -747,6 +752,30 @@ if [ "$swscale" = "auto" ] ; then
     fi
 fi
 
+if [ "$x11" = "auto" ] ; then
+    x11="no"
+    if ${cross_prefix}pkg-config --exists x11 xext xfixes 2>/dev/null; then
+        X11_LIBS="$X11_LIBS $(${cross_prefix}pkg-config --libs x11 xext xfixes)"
+        X11_CFLAGS="$X11_CFLAGS $(${cross_prefix}pkg-config --cflags x11 xext xfixes)"
+    fi
+    [ -z "$X11_LIBS" ] && X11_LIBS="-lx11 -lXext -lXfixes"
+
+    if cc_check "X11/Xlib.h" "$X11_CFLAGS $X11_LIBS" "XOpenDisplay(0);" ; then
+	if cc_check "X11/Xlib.h X11/extensions/XShm.h" "$X11_CFLAGS $X11_LIBS" "XShmCreateImage(0, 0, 0, 0, NULL, 0, 0, 0);" ;then
+	    if cc_check "X11/Xlib.h X11/extensions/Xfixes.h" "$X11_CFLAGS $X11_LIBS" "XFixesGetCursorImage(0);" ;then
+		x11="yes"
+		define HAVE_X11
+		CFLAGS="$CFLAGS $X11_CFLAGS"
+		LDFLAGSCLI="$LDLAGS $X11_LIBS"
+            else
+		echo "Warning: XFixesGetCursor is missing from libXfixes, update for x11 support"
+            fi
+	else
+	    echo "Warning: XShmCreateImage is missing from libXext, update for x11 support"
+	fi
+    fi
+fi
+
 if [ "$lavf" = "auto" ] ; then
     lavf="no"
     if ${cross_prefix}pkg-config --exists libavformat libavcodec libswscale 2>/dev/null; then
@@ -863,7 +892,7 @@ if [ "$strip" = "yes" ]; then
 fi
 
 if [ "$debug" = "yes" ]; then
-    CFLAGS="-O1 -g $CFLAGS"
+    CFLAGS="-O0 -g $CFLAGS"
 elif [ $ARCH = ARM ]; then
     # arm-gcc-4.2 produces incorrect output with -ffast-math
     # and it doesn't save any speed anyway on 4.4, so disable it
@@ -1088,6 +1117,7 @@ interlaced: $interlaced
 avs:        $avs
 lavf:       $lavf
 ffms:       $ffms
+x11:        $x11
 gpac:       $gpac
 gpl:        $gpl
 thread:     $thread
diff --git a/input/input.h b/input/input.h
index 4a4bb0b..ebe9cd8 100644
--- a/input/input.h
+++ b/input/input.h
@@ -99,6 +99,7 @@ extern cli_input_t thread_input;
 extern const cli_input_t lavf_input;
 extern const cli_input_t ffms_input;
 extern cli_input_t timecode_input;
+extern const cli_input_t x11_input;
 
 extern cli_input_t cli_input;
 
diff --git a/input/x11.c b/input/x11.c
new file mode 100644
index 0000000..46be2e1
--- /dev/null
+++ b/input/x11.c
@@ -0,0 +1,436 @@
+/*****************************************************************************
+ * x11.c: x11 input
+ *****************************************************************************
+ * Copyright (C) 2003-2011 x264 project
+ *
+ * Authors: Corentin Chary <corentin.chary at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111, USA.
+ *
+ * This program is also available under a commercial proprietary license.
+ * For more information, contact us at licensing at x264.com.
+ *****************************************************************************/
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+#include <X11/Xproto.h>
+#include <X11/Xutil.h>
+#include <sys/shm.h>
+#include <X11/extensions/XShm.h>
+#include <X11/extensions/Xfixes.h>
+
+#include "input.h"
+#define FAIL_IF_ERROR( cond, ... ) FAIL_IF_ERR( cond, "x11", __VA_ARGS__ )
+
+typedef struct
+{
+    int next_frame;
+    uint64_t plane_size[4];
+    uint64_t frame_size;
+    int bit_depth;
+    int csp;
+
+    double frame_rate;
+    int64_t time_frame;
+    int64_t next_frame_time;
+
+    int height;
+    int width;
+    int x_off;
+    int y_off;
+
+    Display *dpy;
+    XImage *image;
+    int use_shm;
+    XShmSegmentInfo shminfo;
+    int nomouse;
+
+} x11_hnd_t;
+
+static int close_file( hnd_t handle );
+
+static int open_file( char *psz_filename, hnd_t *p_handle, video_info_t *info, cli_input_opt_t *opt )
+{
+    x11_hnd_t *h = calloc( 1, sizeof(x11_hnd_t) );
+    Display *dpy;
+    XImage *image;
+    int x_off = 0;
+    int y_off = 0;
+    int use_shm;
+    char *dpyname, *offset;
+
+    if( !h )
+        return -1;
+
+    dpyname = strdup( psz_filename );
+    offset = strchr( dpyname, '+' );
+    if ( offset )
+    {
+        sscanf( offset, "%d,%d", &x_off, &y_off );
+        h->nomouse = !!strstr( offset, "nomouse" );
+        *offset = 0;
+    }
+
+    if( opt->resolution )
+	sscanf( opt->resolution, "%dx%d", &h->width, &h->height );
+
+    x264_cli_log( "x11", X264_LOG_INFO, "device: %s -> display: %s x: %d y: %d width: %d height: %d\n",
+		  psz_filename, dpyname, x_off, y_off, h->width, h->height );
+
+    dpy = XOpenDisplay(dpyname);
+    free(dpyname);
+
+    if(!dpy)
+    {
+	x264_cli_log( "x11", X264_LOG_ERROR, "Could not open X display.\n" );
+        goto error;
+    }
+
+    if ( !h->width || !h->height )
+    {
+	XWindowAttributes attrs;
+
+	XGetWindowAttributes( dpy, DefaultRootWindow ( dpy ), &attrs );
+
+	if ( !h->width )
+	    h->width = attrs.width;
+	if ( !h->height )
+	    h->height = attrs.height;
+    }
+
+    use_shm = XShmQueryExtension( dpy );
+    x264_cli_log( "x11", X264_LOG_INFO, "shared memory extension%s found\n", use_shm ? "" : " not");
+
+    if ( use_shm )
+    {
+        int scr = XDefaultScreen( dpy );
+        image = XShmCreateImage( dpy,
+				 DefaultVisual( dpy, scr ),
+				 DefaultDepth( dpy, scr ),
+				 ZPixmap,
+				 NULL,
+				 &h->shminfo,
+				 h->width, h->height );
+        h->shminfo.shmid = shmget( IPC_PRIVATE,
+				   image->bytes_per_line * image->height,
+				   IPC_CREAT|0777 );
+        if ( h->shminfo.shmid == -1 )
+	{
+            x264_cli_log( "x11", X264_LOG_ERROR, "Fatal: Can't get shared memory!\n");
+	    goto error;
+        }
+
+        h->shminfo.shmaddr = image->data = shmat(h->shminfo.shmid, 0, 0);
+        h->shminfo.readOnly = False;
+
+        if ( !XShmAttach( dpy, &h->shminfo ) )
+	{
+            x264_cli_log( "x11", X264_LOG_ERROR, "Fatal: Failed to attach shared memory!\n");
+            /* needs some better error subroutine :) */
+            goto error;
+        }
+    }
+    else
+    {
+        image = XGetImage( dpy, DefaultRootWindow( dpy ),
+                           x_off,y_off,
+			   h->width, h->height,
+			   AllPlanes, ZPixmap );
+    }
+
+    switch (image->bits_per_pixel)
+    {
+    case 32:
+	h->csp = X264_CSP_BGRA;
+	break;
+    case 24:
+        if (image->red_mask   == 0xff0000 &&
+	    image->green_mask == 0x00ff00 &&
+	    image->blue_mask  == 0x0000ff )
+	{
+            h->csp = X264_CSP_BGR;
+        }
+	else if ( image->red_mask   == 0x0000ff &&
+                    image->green_mask == 0x00ff00 &&
+                    image->blue_mask  == 0xff0000 )
+	{
+            h->csp = X264_CSP_RGB;
+        }
+	else
+	{
+            x264_cli_log( "x11", X264_LOG_ERROR,"rgb ordering at image depth %i not supported ... aborting\n", image->bits_per_pixel);
+            x264_cli_log( "x11", X264_LOG_ERROR, "color masks: r 0x%.6lx g 0x%.6lx b 0x%.6lx\n", image->red_mask, image->green_mask, image->blue_mask);
+            goto error;
+        }
+        break;
+    default:
+        x264_cli_log( "x11", X264_LOG_ERROR, "image depth %i not supported ... aborting\n", image->bits_per_pixel);
+	x264_cli_log( "x11", X264_LOG_ERROR, "color masks: r 0x%.6lx g 0x%.6lx b 0x%.6lx\n", image->red_mask, image->green_mask, image->blue_mask);
+	goto error;
+    }
+
+    h->dpy = dpy;
+    h->frame_rate = (double) info->fps_num / info->fps_den;
+    h->time_frame = INT64_C(1000000) / h->frame_rate;
+    h->next_frame_time = x264_mdate();
+    h->x_off = x_off;
+    h->y_off = y_off;
+    h->image = image;
+    h->use_shm = use_shm;
+
+    info->thread_safe = 0;
+    info->num_frames  = 0;
+    info->vfr         = 0;
+    info->width = h->width;
+    info->height = h->height;
+    info->csp = h->csp;
+
+    const x264_cli_csp_t *csp = x264_cli_get_csp( info->csp );
+    for( int i = 0; i < csp->planes; i++ )
+    {
+        h->plane_size[i] = x264_cli_pic_plane_size( info->csp, info->width, info->height, i );
+        h->frame_size += h->plane_size[i];
+        /* x264_cli_pic_plane_size returns the size in bytes, we need the value in pixels from here on */
+        h->plane_size[i] /= x264_cli_csp_depth_factor( info->csp );
+    }
+
+    *p_handle = h;
+    return 0;
+
+error:
+    close_file (h);
+    return -1;
+
+}
+
+static int paint_mouse_pointer( XImage *image, x11_hnd_t *h )
+{
+    int x_off = h->x_off;
+    int y_off = h->y_off;
+    int width = h->width;
+    int height = h->height;
+    Display *dpy = h->dpy;
+    XFixesCursorImage *xcim;
+    int x, y;
+    int line, column;
+    int to_line, to_column;
+    int pixstride = image->bits_per_pixel >> 3;
+    /* Warning: in its insanity, xlib provides unsigned image data through a
+     * char* pointer, so we have to make it uint8_t to make things not break.
+     * Anyone who performs further investigation of the xlib API likely risks
+     * permanent brain damage. */
+    uint8_t *pix = (uint8_t *)image->data;
+
+    /* Code doesn't currently support 16-bit or PAL8 */
+    if ( image->bits_per_pixel != 24 && image->bits_per_pixel != 32 )
+        return -1;
+
+    xcim = XFixesGetCursorImage( dpy );
+
+    x = xcim->x - xcim->xhot;
+    y = xcim->y - xcim->yhot;
+
+    to_line = X264_MIN( y + xcim->height, height + y_off );
+    to_column = X264_MIN( x + xcim->width, width + x_off );
+
+    for ( line = X264_MAX( y, y_off ); line < to_line; line++ )
+    {
+        for ( column = X264_MAX( x, x_off ); column < to_column; column++ )
+	{
+            int  xcim_addr = (line - y) * xcim->width + column - x;
+            int image_addr = ((line - y_off) * width + column - x_off) * pixstride;
+            int r = (uint8_t)(xcim->pixels[xcim_addr] >>  0);
+            int g = (uint8_t)(xcim->pixels[xcim_addr] >>  8);
+            int b = (uint8_t)(xcim->pixels[xcim_addr] >> 16);
+            int a = (uint8_t)(xcim->pixels[xcim_addr] >> 24);
+
+            if ( a == 255 )
+	    {
+                pix[image_addr+0] = r;
+                pix[image_addr+1] = g;
+                pix[image_addr+2] = b;
+            }
+	    else if ( a )
+	    {
+                /* pixel values from XFixesGetCursorImage come premultiplied by alpha */
+                pix[image_addr+0] = r + (pix[image_addr+0]*(255-a) + 255/2) / 255;
+                pix[image_addr+1] = g + (pix[image_addr+1]*(255-a) + 255/2) / 255;
+                pix[image_addr+2] = b + (pix[image_addr+2]*(255-a) + 255/2) / 255;
+            }
+        }
+    }
+
+    XFree( xcim );
+    xcim = NULL;
+    return 0;
+}
+
+
+static int xget_zpixmap( Display *dpy, Drawable d, XImage *image, int x, int y )
+{
+    xGetImageReply rep;
+    xGetImageReq *req;
+    long nbytes;
+
+    if ( !image )
+        return -1;
+
+    LockDisplay( dpy );
+    GetReq( GetImage, req );
+
+    /* First set up the standard stuff in the request */
+    req->drawable = d;
+    req->x = x;
+    req->y = y;
+    req->width = image->width;
+    req->height = image->height;
+    req->planeMask = (unsigned int)AllPlanes;
+    req->format = ZPixmap;
+
+    if ( !_XReply( dpy, (xReply *)&rep, 0, xFalse ) || !rep.length )
+    {
+        UnlockDisplay(dpy);
+        SyncHandle();
+        return -1;
+    }
+
+    nbytes = (long)rep.length << 2;
+    _XReadPad( dpy, image->data, nbytes );
+
+    UnlockDisplay( dpy );
+    SyncHandle();
+    return 0;
+}
+
+static int x11_grab_image( x11_hnd_t *h )
+{
+    Display *dpy = h->dpy;
+    XImage *image = h->image;
+    int x_off = h->x_off;
+    int y_off = h->y_off;
+
+    /* wait based on the frame rate */
+    for(;;) {
+	int64_t curtime, delay;
+	struct timespec ts;
+
+        curtime = x264_mdate();
+        delay = h->next_frame_time - curtime;
+
+        if (delay <= 0) {
+	    h->next_frame_time = curtime; /* Do we really want to do that ? */
+            break;
+	}
+
+        ts.tv_sec = delay / 1000000;
+        ts.tv_nsec = (delay % 1000000) * 1000;
+
+        nanosleep(&ts, NULL);
+    }
+
+    /* Calculate the time of the next frame */
+    h->next_frame_time += h->time_frame;
+
+    if( h->use_shm )
+    {
+        if ( !XShmGetImage( dpy, DefaultRootWindow( dpy ), image, x_off, y_off, AllPlanes ) )
+	{
+            x264_cli_log( "x11", X264_LOG_INFO, "XShmGetImage() failed\n" );
+	    return -1;
+        }
+    }
+    else
+    {
+        if ( xget_zpixmap( dpy, DefaultRootWindow( dpy ), image, x_off, y_off ) )
+	{
+            x264_cli_log( "x11", X264_LOG_INFO, "XGetZPixmap() failed\n" );
+	    return -1;
+        }
+    }
+
+    if( !h->nomouse )
+        return paint_mouse_pointer( image, h );
+
+    return 0;
+}
+
+static int read_frame_internal( cli_pic_t *pic, x11_hnd_t *h )
+{
+    int pixel_depth = x264_cli_csp_depth_factor( pic->img.csp );
+    uint64_t offset = 0;
+
+    if ( x11_grab_image( h ) )
+	return -1;
+
+    for( int i = 0; i < pic->img.planes; i++ )
+    {
+	memcpy (pic->img.plane[i], h->image->data + offset, h->plane_size[i]);
+	offset += h->plane_size[i] * pixel_depth;
+    }
+    return 0;
+}
+
+static int read_frame( cli_pic_t *pic, hnd_t handle, int i_frame )
+{
+    x11_hnd_t *h = handle;
+
+    if( i_frame > h->next_frame )
+    {
+            while( i_frame > h->next_frame )
+            {
+                if( read_frame_internal( pic, h ) )
+                    return -1;
+                h->next_frame++;
+            }
+    }
+
+    if( read_frame_internal( pic, h ) )
+        return -1;
+
+    h->next_frame = i_frame+1;
+    return 0;
+}
+
+static int close_file( hnd_t handle )
+{
+    x11_hnd_t *h = handle;
+
+    if( !h )
+        return 0;
+
+    /* Detach cleanly from shared mem */
+    if ( h->use_shm )
+    {
+        XShmDetach( h->dpy, &h->shminfo );
+        shmdt( h->shminfo.shmaddr );
+        shmctl( h->shminfo.shmid, IPC_RMID, NULL );
+    }
+
+    /* Destroy X11 image */
+    if ( h->image )
+    {
+        XDestroyImage( h->image );
+        h->image = NULL;
+    }
+
+    /* Free X11 display */
+    XCloseDisplay( h->dpy );
+
+    free( h );
+    return 0;
+}
+
+const cli_input_t x11_input = { open_file, x264_cli_pic_alloc, read_frame, NULL, x264_cli_pic_clean, close_file };
diff --git a/x264.c b/x264.c
index 29fe270..355954d 100644
--- a/x264.c
+++ b/x264.c
@@ -104,6 +104,9 @@ static const char * const demuxer_names[] =
 #if HAVE_FFMS
     "ffms",
 #endif
+#if HAVE_X11
+    "x11",
+#endif
     0
 };
 
@@ -1048,6 +1051,16 @@ static int select_input( const char *demuxer, char *used_demuxer, char *filename
         cli_input = y4m_input;
     else if( !strcasecmp( module, "raw" ) || !strcasecmp( ext, "yuv" ) )
         cli_input = raw_input;
+    else if( !strcasecmp( module, "x11" ) )
+    {
+#if HAVE_X11
+        cli_input = x11_input;
+        module = "x11";
+#else
+        x264_cli_log( "x264", X264_LOG_ERROR, "not compiled with X11 input support\n" );
+        return -1;
+#endif
+    }
     else
     {
 #if HAVE_FFMS
-- 
1.7.3.4



More information about the x264-devel mailing list