Stream Java Swing Components over RTP
Currently, I am looking for an option to stream the contents of a JPanel over RTP to a receivi开发者_Python百科ng client. The JPanel would e.g. contain a JFreeChart and some labels that should be displayed on a remote client player (which in fact happens to be a presentation software with RTP support).
Are there any libraries that would allow such a stream generation from UI components and its subsequent transmission over RTP?
Thanks for any hints and advices KC
P.S.: In response to Qwerky's question: Only their visual representation (e.g. their image) should be transmitted.
Concludingly, I ended up implementing a JMF-based component stream package:
JpegHelper:
package ch.beo.streaming.swing;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
/**
* Helper class to convert UI components to JPEG images.
*/
public final class JpegHelper {
private JpegHelper() {
}
/**
* Transforms any given UI {@link java.awt.Component Component} to a JPEG
* image.
*
* @param component
* A paintable {@link java.awt.Component Component}.
* @return The binary representation of JPEG image, representing
* <code>component's</code> current visual state.
*/
public static byte[] componentToJpeg(Component component) {
Dimension size = component.getSize();
BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
Graphics2D graphics2d = image.createGraphics();
component.paint(graphics2d);
graphics2d.dispose();
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(image, "jpg", out);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
return out.toByteArray();
}
}
ComponentBufferStream:
package ch.beo.streaming.swing;
import java.awt.Component;
import java.awt.Dimension;
import java.io.IOException;
import javax.media.Buffer;
import javax.media.Format;
import javax.media.format.VideoFormat;
import javax.media.protocol.ContentDescriptor;
import javax.media.protocol.PullBufferStream;
import ch.beo.streaming.base.ControlHelper;
/**
* Implements a {@link javax.media.protocol.PullBufferStream PullBufferStream}
* based on a given {@link java.awt.Component Component}. The current visual
* representation of the {@link java.awt.Component component} is continuously
* provided as a JPEG image.
*/
public class ComponentBufferStream implements PullBufferStream {
private static final int KEY_FRAME_RATE = 100;
private final Component component;
private int frameCount = -1;
public ComponentBufferStream(Component component) {
this.component = component;
}
/*
* (non-Javadoc)
* @see javax.media.protocol.SourceStream#endOfStream()
*/
@Override
public boolean endOfStream() {
return false;
}
/*
* (non-Javadoc)
* @see javax.media.protocol.SourceStream#getContentDescriptor()
*/
@Override
public ContentDescriptor getContentDescriptor() {
return new ContentDescriptor(ContentDescriptor.RAW);
}
/*
* (non-Javadoc)
* @see javax.media.protocol.SourceStream#getContentLength()
*/
@Override
public long getContentLength() {
return LENGTH_UNKNOWN;
}
/*
* (non-Javadoc)
* @see javax.media.Controls#getControl(java.lang.String)
*/
@Override
public Object getControl(String controlType) {
return ControlHelper.getControl(this, controlType);
}
/*
* (non-Javadoc)
* @see javax.media.Controls#getControls()
*/
@Override
public Object[] getControls() {
return ControlHelper.getControls(this);
}
/*
* (non-Javadoc)
* @see javax.media.protocol.PullBufferStream#getFormat()
*/
@Override
public Format getFormat() {
Dimension size = component.getSize();
return new VideoFormat(VideoFormat.JPEG, size, Format.NOT_SPECIFIED, Format.byteArray, 0.1f);
}
/*
* (non-Javadoc)
* @see javax.media.protocol.PullBufferStream#read(javax.media.Buffer)
*/
@Override
public void read(Buffer buffer) throws IOException {
byte[] image = JpegHelper.componentToJpeg(component);
byte[] data = (byte[]) buffer.getData();
if (data != null && data.length - buffer.getOffset() >= image.length) {
System.arraycopy(image, 0, data, buffer.getOffset(), image.length);
} else {
buffer.setData(image);
buffer.setOffset(0);
}
buffer.setLength(image.length);
buffer.setFormat(getFormat());
if (isKeyFrame()) {
buffer.setFlags(buffer.getFlags() | Buffer.FLAG_KEY_FRAME);
}
}
private boolean isKeyFrame() {
if (++frameCount % KEY_FRAME_RATE == 0) {
frameCount = 0;
return true;
}
return false;
}
/*
* (non-Javadoc)
* @see javax.media.protocol.PullBufferStream#willReadBlock()
*/
@Override
public boolean willReadBlock() {
Dimension dimension = component.getSize();
return dimension.height > 0 && dimension.width > 0;
}
}
ComponentDataSource:
package ch.beo.streaming.swing;
import java.awt.Component;
import java.io.IOException;
import javax.media.Time;
import javax.media.protocol.PullBufferDataSource;
import javax.media.protocol.PullBufferStream;
import ch.beo.streaming.base.ControlHelper;
/**
* Implements a {@link javax.media.protocol.PullBufferDataSource
* PullBufferDataSource} based on a given {@link java.awt.Component Component}.
* The current visual representation of the {@link java.awt.Component component}
* is continuously provided as a JPEG image.
*/
public class ComponentDataSource extends PullBufferDataSource {
private final PullBufferStream[] streams;
public ComponentDataSource(Component component) {
streams = new PullBufferStream[] { new ComponentBufferStream(component) };
}
/*
* (non-Javadoc)
* @see javax.media.protocol.DataSource#connect()
*/
@Override
public void connect() throws IOException {
}
/*
* (non-Javadoc)
* @see javax.media.protocol.DataSource#disconnect()
*/
@Override
public void disconnect() {
}
/*
* (non-Javadoc)
* @see javax.media.protocol.DataSource#getContentType()
*/
@Override
public String getContentType() {
return streams[0].getContentDescriptor().getContentType();
}
/*
* (non-Javadoc)
* @see javax.media.protocol.DataSource#getControl(java.lang.String)
*/
@Override
public Object getControl(String controlType) {
return ControlHelper.getControl(this, controlType);
}
/*
* (non-Javadoc)
* @see javax.media.protocol.DataSource#getControls()
*/
@Override
public Object[] getControls() {
return ControlHelper.getControls(this);
}
/*
* (non-Javadoc)
* @see javax.media.protocol.DataSource#getDuration()
*/
@Override
public Time getDuration() {
return Time.TIME_UNKNOWN;
}
/*
* (non-Javadoc)
* @see javax.media.protocol.DataSource#start()
*/
@Override
public void start() throws IOException {
}
/*
* (non-Javadoc)
* @see javax.media.protocol.DataSource#stop()
*/
@Override
public void stop() throws IOException {
}
/*
* (non-Javadoc)
* @see javax.media.protocol.PullBufferDataSource#getStreams()
*/
@Override
public PullBufferStream[] getStreams() {
return streams;
}
}
Along with some other helper classes, this did the trick.
精彩评论