[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