To capture the contents of the screen and feed it to a JMF player or Processor for purposes such as saving screen shots to disk or transmitting live using RTP.
Java 2 v.1.3 introduced a new class, Robot, that provides screen
capture facilities. Since this is similar to capturing video from a VCR or
camera, it is best encapsulated in a JMF DataSource object.
This example shows how to use Robot to create a screen capture DataSource.
The DataSource is a PushDataSource that pushes captured screen shots at
regular intervals to the connected Player or Processor.
It creates a new protocol "screen" with the following URL syntax:
screen://x,y,width,height/fps
where x, y specify a screen location and width,height specify the size.
fps specifies the number of frames to push per second. This can be any fraction
greater than zero, although realistic values would be between 0.1 and 30.0.
For example, to capture screen activity in the center of a 1024x768 screen, use
screen://352,264,320,240/5
Compile the DataSource and LiveStream classes:
javac -d . DataSource.java LiveStream.java
Run using JMStudio:
java JMStudio screen://0,0,160,120/10
OR:
java VideoTransmit screen://0,0,160,120/10 224.122.122.122 2222
java AVReceive2 224.122.122.122/2222
Source Code:
DataSource.javaLiveStream.java
* @(#)DataSource.java 1.2 01/03/02
*
* Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
package com.sun.media.protocol.screen;
import javax.media.Time;
import javax.media.MediaLocator;
import javax.media.protocol.*;
import java.io.IOException;
/**
* This DataSource captures live frames from the screen.
* You can specify the location, size and frame rate in the
* URL string as follows:
* screen://x,y,width,height/framespersecond
* Eg:
* screen://20,40,160,120/12.5
* Note:
* Requires JDK 1.3+ to compile and run
*/
public class DataSource extends PushBufferDataSource {
protected Object [] controls = new Object[0];
protected boolean started = false;
protected String contentType = "raw";
protected boolean connected = false;
protected Time duration = DURATION_UNBOUNDED;
protected LiveStream [] streams = null;
protected LiveStream stream = null;
public DataSource() {
}
public String getContentType() {
if (!connected){
System.err.println("Error: DataSource not connected");
return null;
}
return contentType;
}
public void connect() throws IOException {
if (connected)
return;
connected = true;
}
public void disconnect() {
try {
if (started)
stop();
} catch (IOException e) {}
connected = false;
}
public void start() throws IOException {
// we need to throw error if connect() has not been called
if (!connected)
throw new java.lang.Error("DataSource must be connected before it can be started");
if (started)
return;
started = true;
stream.start(true);
}
public void stop() throws IOException {
if ((!connected) || (!started))
return;
started = false;
stream.start(false);
}
public Object [] getControls() {
return controls;
}
public Object getControl(String controlType) {
return null;
}
public Time getDuration() {
return duration;
}
public PushBufferStream [] getStreams() {
if (streams == null) {
streams = new LiveStream[1];
stream = streams[0] = new LiveStream(getLocator());
}
return streams;
}
}
* @(#)LiveStream.java 1.2 01/03/02
*
* Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
package com.sun.media.protocol.screen;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.media.*;
import javax.media.format.*;
import javax.media.protocol.*;
import java.io.IOException;
import java.util.StringTokenizer;
public class LiveStream implements PushBufferStream, Runnable {
protected ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW);
protected int maxDataLength;
protected int [] data;
protected Dimension size;
protected RGBFormat rgbFormat;
protected boolean started;
protected Thread thread;
protected float frameRate = 1f;
protected BufferTransferHandler transferHandler;
protected Control [] controls = new Control[0];
protected int x, y, width, height;
protected Robot robot = null;
public LiveStream(MediaLocator locator) {
try {
parseLocator(locator);
} catch (Exception e) {
System.err.println(e);
}
//size = Toolkit.getDefaultToolkit().getScreenSize();
size = new Dimension(width, height);
try {
robot = new Robot();
} catch (AWTException awe) {
throw new RuntimeException("");
}
maxDataLength = size.width * size.height * 3;
rgbFormat = new RGBFormat(size, maxDataLength,
Format.intArray,
frameRate,
32,
0xFF0000, 0xFF00, 0xFF,
1, size.width,
VideoFormat.FALSE,
Format.NOT_SPECIFIED);
// generate the data
data = new int[maxDataLength];
thread = new Thread(this, "Screen Grabber");
}
protected void parseLocator(MediaLocator locator) {
String rem = locator.getRemainder();
// Strip off starting slashes
while (rem.startsWith("/") && rem.length() > 1)
rem = rem.substring(1);
StringTokenizer st = new StringTokenizer(rem, "/");
if (st.hasMoreTokens()) {
// Parse the position
String position = st.nextToken();
StringTokenizer nums = new StringTokenizer(position, ",");
String stX = nums.nextToken();
String stY = nums.nextToken();
String stW = nums.nextToken();
String stH = nums.nextToken();
x = Integer.parseInt(stX);
y = Integer.parseInt(stY);
width = Integer.parseInt(stW);
height = Integer.parseInt(stH);
}
if (st.hasMoreTokens()) {
// Parse the frame rate
String stFPS = st.nextToken();
frameRate = (Double.valueOf(stFPS)).floatValue();
}
}
/***************************************************************************
* SourceStream
***************************************************************************/
public ContentDescriptor getContentDescriptor() {
return cd;
}
public long getContentLength() {
return LENGTH_UNKNOWN;
}
public boolean endOfStream() {
return false;
}
/***************************************************************************
* PushBufferStream
***************************************************************************/
int seqNo = 0;
public Format getFormat() {
return rgbFormat;
}
public void read(Buffer buffer) throws IOException {
synchronized (this) {
Object outdata = buffer.getData();
if (outdata == null || !(outdata.getClass() == Format.intArray) ||
((int[])outdata).length < maxDataLength) {
outdata = new int[maxDataLength];
buffer.setData(outdata);
}
buffer.setFormat( rgbFormat );
buffer.setTimeStamp( (long) (seqNo * (1000 / frameRate) * 1000000) );
BufferedImage bi = robot.createScreenCapture(
new Rectangle(x, y, width, height));
bi.getRGB(0, 0, width, height,
(int[])outdata, 0, width);
buffer.setSequenceNumber( seqNo );
buffer.setLength(maxDataLength);
buffer.setFlags(Buffer.FLAG_KEY_FRAME);
buffer.setHeader( null );
seqNo++;
}
}
public void setTransferHandler(BufferTransferHandler transferHandler) {
synchronized (this) {
this.transferHandler = transferHandler;
notifyAll();
}
}
void start(boolean started) {
synchronized ( this ) {
this.started = started;
if (started && !thread.isAlive()) {
thread = new Thread(this);
thread.start();
}
notifyAll();
}
}
/***************************************************************************
* Runnable
***************************************************************************/
public void run() {
while (started) {
synchronized (this) {
while (transferHandler == null && started) {
try {
wait(1000);
} catch (InterruptedException ie) {
}
} // while
}
if (started && transferHandler != null) {
transferHandler.transferData(this);
try {
Thread.currentThread().sleep( 10 );
} catch (InterruptedException ise) {
}
}
} // while (started)
} // run
// Controls
public Object [] getControls() {
return controls;
}
public Object getControl(String controlType) {
try {
Class cls = Class.forName(controlType);
Object cs[] = getControls();
for (int i = 0; i < cs.length; i++) {
if (cls.isInstance(cs[i]))
return cs[i];
}
return null;
} catch (Exception e) { // no such controlType or such control
return null;
}
}
}