@ -25,10 +25,16 @@
* GNU General Public License for more details .
* GNU General Public License for more details .
*
*
* You should have received a copy of the GNU General Public License along
* 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 . ,
* with FFmpeg ; if not , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
*/
*/
/**
* @ file x11grab . c
* X11 frame device demuxer by Clemens Fruhwirth < clemens @ endorphin . org >
* and Edouard Gomez < ed . gomez @ free . fr > .
*/
# include "avformat.h"
# include "avformat.h"
# include <unistd.h>
# include <unistd.h>
# include <fcntl.h>
# include <fcntl.h>
@ -46,41 +52,53 @@
# include <sys/shm.h>
# include <sys/shm.h>
# include <X11/extensions/XShm.h>
# include <X11/extensions/XShm.h>
typedef struct
/**
* X11 Device Demuxer context
*/
typedef struct x11_grab_s
{
{
Display * dpy ;
int frame_size ; /**< Size in bytes of a grabbed frame */
int frame_format ;
AVRational time_base ; /**< Time base */
int frame_size ;
int64_t time_frame ; /**< Current time */
int frame_rate ;
int frame_rate_base ;
int height ; /**< Height of the grab frame */
int64_t time_frame ;
int width ; /**< Width of the grab frame */
int x_off ; /**< Horizontal top-left corner coordinate */
int height ;
int y_off ; /**< Vertical top-left corner coordinate */
int width ;
int x_off ;
Display * dpy ; /**< X11 display from which x11grab grabs frames */
int y_off ;
XImage * image ; /**< X11 image holding the grab */
XImage * image ;
int use_shm ; /**< !0 when using XShm extension */
int use_shm ;
XShmSegmentInfo shminfo ; /**< When using XShm, keeps track of XShm infos */
XShmSegmentInfo shminfo ;
} x11_grab_t ;
int mouse_wanted ;
} X11Grab ;
/**
* Initializes the x11 grab device demuxer ( public device demuxer API ) .
*
* @ param s1 Context from avformat core
* @ param ap Parameters from avformat core
* @ return < ul >
* < li > ENOMEM no memory left < / li >
* < li > AVERROR_IO other failure case < / li >
* < li > 0 success < / li >
* < / ul >
*/
static int
static int
x11grab_read_header ( AVFormatContext * s1 , AVFormatParameters * ap )
x11grab_read_header ( AVFormatContext * s1 , AVFormatParameters * ap )
{
{
X11Grab * x11grab = s1 - > priv_data ;
x11_grab_t * x11grab = s1 - > priv_data ;
Display * dpy ;
Display * dpy ;
AVStream * st = NULL ;
AVStream * st = NULL ;
int width , height ;
int frame_rate , frame_rate_base , frame_size ;
int input_pixfmt ;
int input_pixfmt ;
XImage * image ;
XImage * image ;
int x_off = 0 ; int y_off = 0 ;
int x_off = 0 ;
int y_off = 0 ;
int use_shm ;
int use_shm ;
dpy = XOpenDisplay ( NULL ) ;
dpy = XOpenDisplay ( NULL ) ;
if ( ! dpy ) {
if ( ! dpy ) {
goto fail ;
av_free ( st ) ;
return AVERROR_IO ;
}
}
sscanf ( ap - > device , " x11:%d,%d " , & x_off , & y_off ) ;
sscanf ( ap - > device , " x11:%d,%d " , & x_off , & y_off ) ;
@ -91,11 +109,6 @@ x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
return AVERROR_IO ;
return AVERROR_IO ;
}
}
width = ap - > width ;
height = ap - > height ;
frame_rate = ap - > time_base . den ;
frame_rate_base = ap - > time_base . num ;
st = av_new_stream ( s1 , 0 ) ;
st = av_new_stream ( s1 , 0 ) ;
if ( ! st ) {
if ( ! st ) {
return - ENOMEM ;
return - ENOMEM ;
@ -103,7 +116,7 @@ x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
av_set_pts_info ( st , 64 , 1 , 1000000 ) ; /* 64 bits pts in us */
av_set_pts_info ( st , 64 , 1 , 1000000 ) ; /* 64 bits pts in us */
use_shm = XShmQueryExtension ( dpy ) ;
use_shm = XShmQueryExtension ( dpy ) ;
av_log ( s1 , AV_LOG_INFO , " shared memory extension %s \n " , use_shm ? " found " : " not found " ) ;
av_log ( s1 , AV_LOG_INFO , " shared memory extension %s found \n " , use_shm ? " " : " not " ) ;
if ( use_shm ) {
if ( use_shm ) {
int scr = XDefaultScreen ( dpy ) ;
int scr = XDefaultScreen ( dpy ) ;
@ -142,13 +155,14 @@ x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
input_pixfmt = PIX_FMT_PAL8 ;
input_pixfmt = PIX_FMT_PAL8 ;
break ;
break ;
case 16 :
case 16 :
if ( image - > red_mask = = 0xF800 & & image - > green_mask = = 0x07E0
if ( image - > red_mask = = 0xf800 & &
& & image - > blue_mask = = 0x1F ) {
image - > green_mask = = 0x07e0 & &
image - > blue_mask = = 0x001f ) {
av_log ( s1 , AV_LOG_DEBUG , " 16 bit RGB565 \n " ) ;
av_log ( s1 , AV_LOG_DEBUG , " 16 bit RGB565 \n " ) ;
input_pixfmt = PIX_FMT_RGB565 ;
input_pixfmt = PIX_FMT_RGB565 ;
} else if ( image - > red_mask = = 0x7C 00 & &
} else if ( image - > red_mask = = 0x7c 00 & &
image - > green_mask = = 0x03E 0 & &
image - > green_mask = = 0x03e 0 & &
image - > blue_mask = = 0x1F ) {
image - > blue_mask = = 0x001f ) {
av_log ( s1 , AV_LOG_DEBUG , " 16 bit RGB555 \n " ) ;
av_log ( s1 , AV_LOG_DEBUG , " 16 bit RGB555 \n " ) ;
input_pixfmt = PIX_FMT_RGB555 ;
input_pixfmt = PIX_FMT_RGB555 ;
} else {
} else {
@ -158,12 +172,13 @@ x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
}
}
break ;
break ;
case 24 :
case 24 :
if ( image - > red_mask = = 0xFF 0000 & &
if ( image - > red_mask = = 0xff 0000 & &
image - > green_mask = = 0xFF 00
image - > green_mask = = 0x00ff00 & &
& & image - > blue_mask = = 0xFF ) {
image - > blue_mask = = 0x0000ff ) {
input_pixfmt = PIX_FMT_BGR24 ;
input_pixfmt = PIX_FMT_BGR24 ;
} else if ( image - > red_mask = = 0xFF & & image - > green_mask = = 0xFF00
} else if ( image - > red_mask = = 0x0000ff & &
& & image - > blue_mask = = 0xFF0000 ) {
image - > green_mask = = 0x00ff00 & &
image - > blue_mask = = 0xff0000 ) {
input_pixfmt = PIX_FMT_RGB24 ;
input_pixfmt = PIX_FMT_RGB24 ;
} else {
} else {
av_log ( s1 , AV_LOG_ERROR , " rgb ordering at image depth %i not supported ... aborting \n " , image - > bits_per_pixel ) ;
av_log ( s1 , AV_LOG_ERROR , " rgb ordering at image depth %i not supported ... aborting \n " , image - > bits_per_pixel ) ;
@ -174,7 +189,7 @@ x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
case 32 :
case 32 :
#if 0
#if 0
GetColorInfo ( image , & c_info ) ;
GetColorInfo ( image , & c_info ) ;
if ( c_info . alpha_mask = = 0xFF 000000 & & image - > green_mask = = 0xFF00 ) {
if ( c_info . alpha_mask = = 0xff 000000 & & image - > green_mask = = 0x0000ff00 ) {
/* byte order is relevant here, not endianness
/* byte order is relevant here, not endianness
* endianness is handled by avcodec , but atm no such thing
* endianness is handled by avcodec , but atm no such thing
* as having ABGR , instead of ARGB in a word . Since we
* as having ABGR , instead of ARGB in a word . Since we
@ -194,41 +209,41 @@ x11grab_read_header(AVFormatContext *s1, AVFormatParameters *ap)
return - 1 ;
return - 1 ;
}
}
frame_size = width * height * image - > bits_per_pixel / 8 ;
x11grab - > frame_size = ap - > width * ap - > height * image - > bits_per_pixel / 8 ;
x11grab - > frame_size = frame_size ;
x11grab - > dpy = dpy ;
x11grab - > dpy = dpy ;
x11grab - > width = ap - > width ;
x11grab - > width = ap - > width ;
x11grab - > height = ap - > height ;
x11grab - > height = ap - > height ;
x11grab - > frame_rate = frame_rate ;
x11grab - > time_base = ap - > time_base ;
x11grab - > frame_rate_base = frame_rate_base ;
x11grab - > time_frame = av_gettime ( ) / av_q2d ( ap - > time_base ) ;
x11grab - > time_frame = av_gettime ( ) * frame_rate / frame_rate_base ;
x11grab - > x_off = x_off ;
x11grab - > x_off = x_off ;
x11grab - > y_off = y_off ;
x11grab - > y_off = y_off ;
x11grab - > image = image ;
x11grab - > image = image ;
x11grab - > use_shm = use_shm ;
x11grab - > use_shm = use_shm ;
x11grab - > mouse_wanted = 1 ;
st - > codec - > codec_type = CODEC_TYPE_VIDEO ;
st - > codec - > codec_type = CODEC_TYPE_VIDEO ;
st - > codec - > codec_id = CODEC_ID_RAWVIDEO ;
st - > codec - > codec_id = CODEC_ID_RAWVIDEO ;
st - > codec - > width = width ;
st - > codec - > width = ap - > width ;
st - > codec - > height = height ;
st - > codec - > height = ap - > height ;
st - > codec - > pix_fmt = input_pixfmt ;
st - > codec - > pix_fmt = input_pixfmt ;
st - > codec - > time_base . den = frame_rate ;
st - > codec - > time_base = ap - > time_base ;
st - > codec - > time_base . num = frame_rate_base ;
st - > codec - > bit_rate = x11grab - > frame_size * 1 / av_q2d ( ap - > time_base ) * 8 ;
st - > codec - > bit_rate = frame_size * 1 / av_q2d ( st - > codec - > time_base ) * 8 ;
return 0 ;
return 0 ;
fail :
av_free ( st ) ;
return AVERROR_IO ;
}
}
/**
* Get pointer coordinates from X11 .
*
* @ param x Integer where horizontal coordinate will be returned
* @ param y Integer where vertical coordinate will be returned
* @ param dpy X11 display from where pointer coordinates are retrieved
* @ param s1 Context used for logging errors if necessary
*/
static void
static void
getCurrentPointer ( AVFormatContext * s1 , X11Grab * s , int * x , int * y )
get_pointer_coordinates ( int * x , int * y , Display * dpy , AVFormatContext * s1 )
{
{
Window mrootwindow , childwindow ;
Window mrootwindow , childwindow ;
int dummy ;
int dummy ;
Display * dpy = s - > dpy ;
mrootwindow = DefaultRootWindow ( dpy ) ;
mrootwindow = DefaultRootWindow ( dpy ) ;
@ -241,6 +256,17 @@ getCurrentPointer(AVFormatContext *s1, X11Grab *s, int *x, int *y)
}
}
}
}
/**
* Mouse painting helper function that applies an ' and ' and ' or ' mask pair to
* ' * dst ' pixel . It actually draws a mouse pointer pixel to grabbed frame .
*
* @ param dst Destination pixel
* @ param and Part of the mask that must be applied using a bitwise ' and '
* operator
* @ param or Part of the mask that must be applied using a bitwise ' or '
* operator
* @ param bits_per_pixel Bits per pixel used in the grabbed image
*/
static void inline
static void inline
apply_masks ( uint8_t * dst , int and , int or , int bits_per_pixel )
apply_masks ( uint8_t * dst , int and , int or , int bits_per_pixel )
{
{
@ -252,28 +278,39 @@ apply_masks(uint8_t *dst, int and, int or, int bits_per_pixel)
* ( uint16_t * ) dst = ( * ( uint16_t * ) dst & and ) | or ;
* ( uint16_t * ) dst = ( * ( uint16_t * ) dst & and ) | or ;
break ;
break ;
case 8 :
case 8 :
* dst = ( or ) ? 1 : 0 ;
* dst = ! ! or ;
break ;
break ;
}
}
}
}
/**
* Paints a mouse pointer in an X11 image .
*
* @ param image Image where to paint the mouse pointer
* @ param s context used to retrieve original grabbing rectangle
* coordinates
* @ param x Mouse pointer coordinate
* @ param y Mouse pointer coordinate
*/
static void
static void
paintMousePointer ( AVFormatContext * s1 , X11Grab * s , int * x , int * y , XImage * image )
paint_mouse_pointer ( XImage * image , x11_grab_t * s , int x , int y )
{
{
/* 16x20x1bpp bitmap for the black channel of the mouse pointer */
static const uint16_t const mousePointerBlack [ ] =
static const uint16_t const mousePointerBlack [ ] =
{
{
0 , 49152 , 40960 , 36864 , 34816 ,
0x0000 , 0xc000 , 0xa000 , 0x9000 , 0x8800 ,
33792 , 33280 , 33024 , 32896 , 32832 ,
0x8400 , 0x8200 , 0x8100 , 0x8080 , 0x8040 ,
33728 , 37376 , 43264 , 51456 , 1152 ,
0x83c0 , 0x9200 , 0xa900 , 0xc900 , 0x0480 ,
1152 , 576 , 576 , 448 , 0
0x0480 , 0x0240 , 0x0240 , 0x01c0 , 0x000 0
} ;
} ;
/* 16x20x1bpp bitmap for the white channel of the mouse pointer */
static const uint16_t const mousePointerWhite [ ] =
static const uint16_t const mousePointerWhite [ ] =
{
{
0 , 0 , 16384 , 24576 , 28672 ,
0x0000 , 0x0000 , 0x4000 , 0x6000 , 0x7000 ,
30720 , 31744 , 32256 , 32512 , 3264 0,
0x7800 , 0x7c00 , 0x7e00 , 0x7f00 , 0x7f8 0,
31744 , 27648 , 17920 , 1536 , 768 ,
0x7c00 , 0x6c00 , 0x4600 , 0x0600 , 0x0300 ,
768 , 384 , 384 , 0 , 0
0x0300 , 0x0180 , 0x0180 , 0x0000 , 0x000 0
} ;
} ;
int x_off = s - > x_off ;
int x_off = s - > x_off ;
@ -281,74 +318,40 @@ paintMousePointer(AVFormatContext *s1, X11Grab *s, int *x, int *y, XImage *image
int width = s - > width ;
int width = s - > width ;
int height = s - > height ;
int height = s - > height ;
if ( ( * x - x_off ) > = 0 & & * x < ( width + x_off )
if ( x - x_off > = 0 & & x < width + x_off
& & ( * y - y_off ) > = 0 & & * y < ( height + y_off ) ) {
& & y - y_off > = 0 & & y < height + y_off ) {
int line ;
uint8_t * im_data = ( uint8_t * ) image - > data ;
uint8_t * im_data = ( uint8_t * ) image - > data ;
const uint16_t * black ;
int bytes_per_pixel ;
const uint16_t * whit e;
int lin e;
int masks ;
int masks ;
int onepixel ;
/* Select correct pointer pixels */
if ( s - > mouse_wanted = = 1 ) {
/* Normal pointer */
black = mousePointerBlack ;
white = mousePointerWhite ;
} else {
/* Inverted pointer */
black = mousePointerWhite ;
white = mousePointerBlack ;
}
/* Select correct masks and pixel size */
/* Select correct masks and pixel size */
switch ( image - > bits_per_pixel ) {
if ( image - > bits_per_pixel = = 8 ) {
case 32 :
masks = ( image - > red_mask | image - > green_mask | image - > blue_mask ) ;
onepixel = 4 ;
break ;
case 24 :
/* XXX: Though the code seems to support 24bit images, the
* apply_masks lacks support for 24 bit */
masks = ( image - > red_mask | image - > green_mask | image - > blue_mask ) ;
onepixel = 3 ;
break ;
case 16 :
masks = ( image - > red_mask | image - > green_mask | image - > blue_mask ) ;
onepixel = 2 ;
break ;
case 8 :
masks = 1 ;
masks = 1 ;
onepixel = 1 ;
} else {
break ;
masks = ( image - > red_mask | image - > green_mask | image - > blue_mask ) ;
default :
/* Shut up gcc */
masks = 0 ;
onepixel = 0 ;
}
}
bytes_per_pixel = image - > bits_per_pixel > > 3 ;
/* Shift to right line */
/* Shift to right line */
im_data + = ( image - > bytes_per_line * ( * y - y_off ) ) ;
im_data + = image - > bytes_per_line * ( y - y_off ) ;
/* Shift to right pixel */
/* Shift to right pixel in the line */
im_data + = ( image - > bits_per_pixel / 8 * ( * x - x_off ) ) ;
im_data + = bytes_per_pixel * ( x - x_off ) ;
/* Draw the cursor - proper loop */
/* Draw the cursor - proper loop */
for ( line = 0 ; line < min ( 20 , ( y_off + height ) - * y ) ; line + + ) {
for ( line = 0 ; line < FFMIN ( 20 , ( y_off + height ) - y ) ; line + + ) {
uint8_t * cursor = im_data ;
uint8_t * cursor = im_data ;
int width_cursor ;
int column ;
uint16_t bm_b ;
uint16_t bm_b ;
uint16_t bm_w ;
uint16_t bm_w ;
bm_b = b lack[ line ] ;
bm_b = mousePointerB lack[ line ] ;
bm_w = w hite[ line ] ;
bm_w = mousePointerW hite[ line ] ;
for ( width_cursor = 0 ;
for ( column = 0 ; column < FFMIN ( 16 , ( x_off + width ) - x ) ; column + + ) {
width_cursor < 16 & & ( width_cursor + * x ) < ( width + x_off ) ;
apply_masks ( cursor , ~ ( masks * ( bm_b & 1 ) ) , masks * ( bm_w & 1 ) ,
width_cursor + + ) {
apply_masks ( cursor ,
~ ( masks * ( bm_b & 1 ) ) , masks * ( bm_w & 1 ) ,
image - > bits_per_pixel ) ;
image - > bits_per_pixel ) ;
cursor + = one pixel;
cursor + = bytes_per_pixel ;
bm_b > > = 1 ;
bm_b > > = 1 ;
bm_w > > = 1 ;
bm_w > > = 1 ;
}
}