Add native X11 screen capture.

cusax-fix
Sebastien Vincent 16 years ago
parent 67e5dc87ca
commit 9f44c4f923

@ -0,0 +1,21 @@
##
# \file Makefile
# \brief libscreencapture makefile.
# \author Sebastien Vincent
CC = gcc
JNI_HEADERS = -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux
CFLAGS = -std=c99 -D_POSIX_C_SOURCE=200112L -D_XOPEN_SOURCE=600 -Wall -Wextra -pedantic -Wstrict-prototypes -Wredundant-decls $(JNI_HEADERS)
all: libscreencapture
libscreencapture: net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture.c
$(CC) -shared -fPIC $(CFLAGS) -o libscreencapture.so -O $<
# To compile 32 bit library under 64 bit system
libscreencapture-32: net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture.c
$(CC) -shared -m32 $(CFLAGS) -o libscreencapture.so -O $<
clean:
rm -f *.o *.so

@ -0,0 +1,218 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
/**
* \file net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture.c
* \brief X11 screen capture.
* \author Sebastien Vincent
* \date 2009
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/XShm.h>
#include "net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture.h"
/**
* \brief Grab X11 screen.
* \param x11display display string (i.e. :0.0), if NULL getenv("DISPLAY") is used
* \param x x position to start capture
* \param y y position to start capture
* \param width capture width
* \param height capture height
* \return array of integers which will contains screen capture
* (ARGB format) or NULL if failure. This parameter needs to be freed by caller
*/
static int32_t* grab_screen(const char* x11display, int32_t x, int32_t y, int32_t w, int32_t h)
{
const char* display_str; /* display string */
Display* display = NULL; /* X11 display */
Visual* visual = NULL;
int screen = 0; /* X11 screen */
Window root_window = 0; /* X11 root window of a screen */
int width = 0;
int height = 0;
int depth = 0;
int shm_support = 0;
XImage* img = NULL;
XShmSegmentInfo shm_info;
int32_t* data = NULL;
size_t off = 0;
int i = 0;
int j = 0;
size_t size = 0;
display_str = x11display ? x11display : getenv("DISPLAY");
if(!display_str)
{
/* fprintf(stderr, "No display!\n"); */
return NULL;
}
/* open current X11 display */
display = XOpenDisplay(display_str);
if(!display)
{
/* fprintf(stderr, "Cannot open X11 display!\n"); */
return NULL;
}
screen = DefaultScreen(display);
root_window = RootWindow(display, screen);
visual = DefaultVisual(display, screen);
width = DisplayWidth(display, screen);
height = DisplayHeight(display, screen);
depth = DefaultDepth(display, screen);
/* check that user-defined parameters are in image */
if((w + x) > width || (h + y) > height)
{
XCloseDisplay(display);
return NULL;
}
size = w * h;
/* test is XServer support SHM */
shm_support = XShmQueryExtension(display);
/* fprintf(stdout, "Display=%s width=%d height=%d depth=%d SHM=%s\n", display_str, width, height, depth, shm_support ? "true" : "false"); */
if(shm_support)
{
/* fprintf(stdout, "Use XShmGetImage\n"); */
/* create image for SHM use */
img = XShmCreateImage(display, visual, depth, ZPixmap, NULL, &shm_info, w, h);
if(!img)
{
/* fprintf(stderr, "Image cannot be created!\n"); */
XCloseDisplay(display);
return NULL;
}
/* setup SHM stuff */
shm_info.shmid = shmget(IPC_PRIVATE, img->bytes_per_line * img->height, IPC_CREAT | 0777);
shm_info.shmaddr = (char*)shmat(shm_info.shmid, NULL, 0);
img->data = shm_info.shmaddr;
shmctl(shm_info.shmid, IPC_RMID, NULL);
shm_info.readOnly = 0;
if((shm_info.shmaddr == (void*)-1) || !XShmAttach(display, &shm_info))
{
/* fprintf(stderr, "Cannot use shared memory!\n"); */
XCloseDisplay(display);
return NULL;
}
/* grab screen */
if(!XShmGetImage(display, root_window, img, x, y, 0xffffffff))
{
/* fprintf(stderr, "Cannot grab image!\n"); */
XShmDetach(display, &shm_info);
shmdt(shm_info.shmaddr);
XCloseDisplay(display);
return NULL;
}
}
else
{
/* fprintf(stdout, "Use XGetImage\n"); */
/* no SHM */
img = XGetImage(display, root_window, x, y, w, h, 0xffffffff, ZPixmap);
if(!img)
{
/* fprintf(stderr, "Cannot grab image!\n"); */
XCloseDisplay(display);
return NULL;
}
}
data = malloc(sizeof(int32_t) * size);
if(!data)
{
XDestroyImage(img);
if(shm_support)
{
XShmDetach(display, &shm_info);
shmdt(shm_info.shmaddr);
}
XCloseDisplay(display);
return NULL;
}
/* convert to Java ARGB */
for(j = 0 ; j < h ; j++)
{
for(i = 0 ; i < w ; i++)
{
/* do not care about hight 32-bit for 64 bit host
* (sizeof(long) = 8 on 64 bit Linux host)
*/
unsigned int pixel = (unsigned int)XGetPixel(img, i, j);
pixel |= 0xff000000; /* ARGB */
data[off++] = pixel;
}
}
/* free X11 resources and close display */
XDestroyImage(img);
if(shm_support)
{
XShmDetach(display, &shm_info);
shmdt(shm_info.shmaddr);
}
XCloseDisplay(display);
/* return array */
return data;
}
JNIEXPORT jintArray JNICALL Java_net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture_grabScreen
(JNIEnv* env, jobject obj, jint x, jint y, jint width, jint height)
{
int32_t* data = NULL; /* jint is always four-bytes signed integer */
size_t size = width * height;
jintArray ret = NULL;
obj = obj; /* not used */
data = grab_screen(NULL, x, y, width, height);
if(!data)
{
return 0;
}
ret = (*env)->NewIntArray(env, size);
/* updates array with data's content */
(*env)->SetIntArrayRegion(env, ret, 0, size, data);
free(data);
return ret;
}

@ -0,0 +1,21 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture */
#ifndef _Included_net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture
#define _Included_net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture
* Method: grabScreen
* Signature: (IIII)[I
*/
JNIEXPORT jintArray JNICALL Java_net_java_sip_communicator_impl_neomedia_imgstreaming_UnixScreenCapture_grabScreen
(JNIEnv *, jclass, jint, jint, jint, jint);
#ifdef __cplusplus
}
#endif
#endif

@ -13,18 +13,18 @@
/**
* This singleton class provide screen capture and key/mouse
* events generation by wrapping <tt>java.awt.Robot</tt>
* to interact with desktop.
* events generation by wrapping partial or all <tt>java.awt.Robot</tt>
* methods to interact with desktop.
*
* @see java.awt.Robot
* @author Sebastien Vincent
*/
public class RobotDesktopInteractImpl implements DesktopInteract
public class DesktopInteractImpl implements DesktopInteract
{
/**
* The <tt>Logger</tt>.
*/
private static final Logger logger = Logger.getLogger(RobotDesktopInteractImpl.class);
private static final Logger logger = Logger.getLogger(DesktopInteractImpl.class);
/**
* Screen capture robot.
@ -34,31 +34,17 @@ public class RobotDesktopInteractImpl implements DesktopInteract
/**
* The unique instance of this class (singleton).
*/
private static RobotDesktopInteractImpl instance = null;
private static DesktopInteractImpl instance = null;
/**
* Constructor.
*/
private RobotDesktopInteractImpl()
{
}
/**
* Get the unique instance of <tt>RobotDesktopInteractImpl</tt>.
*
* @return instance
*
* @throws AWTException if platform configuration does not allow low-level input control
* @throws SecurityException if Robot creation is not permitted
*/
public static RobotDesktopInteractImpl getInstance() throws AWTException, SecurityException
public DesktopInteractImpl() throws AWTException, SecurityException
{
if(instance == null)
{
instance = new RobotDesktopInteractImpl();
instance.robot = new Robot();
}
return instance;
robot = new Robot();
}
/**
@ -76,6 +62,10 @@ public BufferedImage captureScreen()
/**
* Capture a part of the desktop screen.
*
* @param x x position to start capture
* @param y y position to start capture
* @param width capture width
* @param height capture height
* @return <tt>BufferedImage</tt> of a part of the desktop screen
* or null if Robot problem
*/
@ -83,18 +73,25 @@ public BufferedImage captureScreen(int x, int y, int width, int height)
{
BufferedImage img = null;
Rectangle rect = null;
/* Robot has not been created so abort */
if(robot == null)
if(OSUtils.IS_LINUX)
{
return null;
return UnixScreenCapture.captureScreen(x, y, width, height);
}
else
{
/* Robot has not been created so abort */
if(robot == null)
{
return null;
}
logger.info("Begin capture: " + System.nanoTime());
rect = new Rectangle(x, y, width, height);
img = robot.createScreenCapture(rect);
logger.info("End capture: " + System.nanoTime());
return img;
}
logger.info("Begin capture: " + System.nanoTime());
rect = new Rectangle(x, y, width, height);
img = robot.createScreenCapture(rect);
logger.info("End capture: " + System.nanoTime());
return img;
}
/**

@ -0,0 +1,70 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.neomedia.imgstreaming;
import java.awt.*;
import java.awt.image.*;
/**
* This class uses native code to capture
* desktop screen.
*
* It should work on all OS with underlying X11.
*
* @author Sebastien Vincent
*/
public class UnixScreenCapture
{
static
{
System.loadLibrary("screencapture");
}
/**
* Capture desktop screen.
*
* @param x x position to start capture
* @param y y position to start capture
* @param width capture width
* @param height capture height
* @return <tt>BufferedImage</tt> of the desktop screen
*/
public static BufferedImage captureScreen(int x, int y, int width, int height)
{
DirectColorModel model = new DirectColorModel(24, 0xFF0000, 0x00FF00, 0xFF);
int masks[] = {0xFF0000, 0xFF00, 0xFF};
WritableRaster raster = null;
DataBufferInt buffer = null;
BufferedImage image = null;
int data[] = null;
data = grabScreen(x, y, width, height);
if(data == null)
{
return null;
}
buffer = new DataBufferInt(data, data.length);
raster = Raster.createPackedRaster(buffer, width, height, width, masks, null);
image = new BufferedImage(model, raster, false, null);
return image;
}
/**
* Grab desktop screen and get ARGB pixels.
*
* @param x x position to start capture
* @param y y position to start capture
* @param width capture width
* @param height capture height
* @return array of ARGB pixels
*/
private static native int[] grabScreen(int x, int y, int width, int height);
}

@ -78,6 +78,11 @@ public class ImageStream implements PushBufferStream, Runnable
*/
private RingBuffer ringBuffer = null;
/**
* Destkop interaction (screen capture, key press, ...).
*/
private DesktopInteract desktopInteract = null;
/**
* Constructor.
*/
@ -276,91 +281,75 @@ public void run()
final RGBFormat format = (RGBFormat)currentFormat;
final int width = (int)format.getSize().getWidth();
final int height = (int)format.getSize().getHeight();
BufferedImage scaledScreen = null;
BufferedImage screen = null;
Buffer buffer = new Buffer();
byte data[] = null;
/* capture first full desktop screen
try
{
screen = RobotDesktopInteractImpl.getInstance().captureScreen();
scaledScreen = ImageStreamingUtils.getScaledImage(screen,
width, height, BufferedImage.TYPE_INT_ARGB);
}
catch(Exception e)
{
}
screen = null;
*/
/*
synchronized(this)
if(desktopInteract == null)
{
while(transferHandler == null && started)
try
{
try
{
wait(1000);
}
catch (InterruptedException e)
{
}
desktopInteract = new DesktopInteractImpl();
}
catch(Exception e)
{
logger.warn("Cannot create DesktopInteract object!");
started = false;
return;
}
}
*/
while(started)
{
try
{
long t = System.nanoTime();
byte data[] = null;
BufferedImage scaledScreen = null;
BufferedImage screen = null;
long t = System.nanoTime();
/* get desktop screen and resize it */
screen = RobotDesktopInteractImpl.getInstance().captureScreen();
scaledScreen = ImageStreamingUtils.getScaledImage(screen,
/* get desktop screen and resize it */
screen = desktopInteract.captureScreen();
scaledScreen = ImageStreamingUtils.getScaledImage(screen,
width, height, BufferedImage.TYPE_INT_ARGB);
/* get raw bytes */
data = ImageStreamingUtils.getImageByte(scaledScreen);
/* add it to a RingBuffer and notify JMF that new data
* is available
*/
buffer.setData(data);
buffer.setOffset(0);
buffer.setLength(data.length);
buffer.setFormat(currentFormat);
buffer.setHeader(null);
buffer.setTimeStamp(System.nanoTime());
buffer.setSequenceNumber(seqNo);
buffer.setFlags(Buffer.FLAG_LIVE_DATA | Buffer.FLAG_SYSTEM_TIME);
seqNo++;
ringBuffer.put(buffer);
/* pass to JMF handler */
if(transferHandler != null)
{
transferHandler.transferData(this);
}
t = System.nanoTime() - t;
logger.info("Desktop capture processing time: " + t);
/* cleanup */
screen = null;
scaledScreen = null;
data = null;
/* get raw bytes */
data = ImageStreamingUtils.getImageByte(scaledScreen);
/* add it to a RingBuffer and notify JMF that new data
* is available
*/
buffer.setData(data);
buffer.setOffset(0);
buffer.setLength(data.length);
buffer.setFormat(currentFormat);
buffer.setHeader(null);
buffer.setTimeStamp(System.nanoTime());
buffer.setSequenceNumber(seqNo);
buffer.setFlags(Buffer.FLAG_LIVE_DATA | Buffer.FLAG_SYSTEM_TIME);
seqNo++;
ringBuffer.put(buffer);
/* pass to JMF handler */
if(transferHandler != null)
{
transferHandler.transferData(this);
}
t = System.nanoTime() - t;
logger.info("Desktop capture processing time: " + t);
/* cleanup */
screen = null;
scaledScreen = null;
data = null;
try
{
/* 500 ms */
Thread.sleep(500);
}
catch(AWTException ae)
{
logger.warn("Desktop capture failed!");
}
catch (InterruptedException e)
catch(InterruptedException e)
{
/* do nothing */
}
}

Loading…
Cancel
Save