package VideoProcessing;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.List;
import javax.media.Buffer;
import javax.media.Effect;
import javax.media.Format;
import javax.media.PlugIn;
import javax.media.format.RGBFormat;
import javax.media.format.VideoFormat;
public abstract class RgbVideoEffect implements Effect {
public static final String PROP_VIDEO_SIZE = "videoSize";
protected BufferedImage displayImage;
private List<VideoFrameListener> frameListenerList;
// We pretend that our effects will always input and output RGB
protected Format supportedIns[] = new Format[] { new RGBFormat() };
protected Format supportedOuts[] = new Format[] { new RGBFormat() };
private Format input = null, output = null;
/** For profiling: total processing time ever. */
private double totalTime;
/** For profiling: total number of calls to processRGB(). */
private long nCalls;
/** Whether or not the effect is active. */
private boolean active;
/** Size of the incoming video frames. */
private Dimension videoSize = new Dimension();
private PropertyChangeSupport propSupport = new PropertyChangeSupport(this);
public RgbVideoEffect() {
active = true;
displayImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
frameListenerList = new ArrayList<VideoFrameListener>();
}
public Dimension getVideoSize() {
return videoSize;
}
/*
* public int process(Buffer in, Buffer out) {
*
* if (in.getFormat() instanceof VideoFormat && in.getData() != null) {
* byte[] bin; byte[] bout; if (in.getData() instanceof byte[]) { bin =
* (byte[]) in.getData(); } else if (in.getData() instanceof int[]) { int[]
* iin = (int[]) in.getData(); bin = new byte[iin.length * 3]; int bi, ii;
* for (bi = 0, ii = 0; bi < bin.length; bi += 3, ii++) { int v = iin[ii];
* bin[bi + 2] = (byte) (v & 0xff); bin[bi + 1] = (byte) ((v >> 8) & 0xff);
* bin[bi] = (byte) ((v >> 16) & 0xff); } } else { return
* PlugIn.BUFFER_PROCESSED_FAILED; } if (!(out.getData() instanceof byte[])
* || ((byte[])out.getData()).length < bin.length) { bout = new
* byte[bin.length]; out.setData(bout); } else { bout = (byte[])
* out.getData(); }
*
* VideoFormat vformat = (VideoFormat) in.getFormat(); if
* (vformat.getSize().width != videoSize.width || vformat.getSize().height
* != videoSize.height) { videoSize = vformat.getSize();
* propSupport.firePropertyChange(PROP_VIDEO_SIZE, null, videoSize); }
*
* //// Assure output buffer is large enough if(bout == null || bout.length
* < bin.length) { }
*
* byte[] buffToDraw = bout; boolean processed = false; if (active) { long
* startTime = System.nanoTime();
* //System.out.println("in "+this.getClass().getName()); processed =
* processRGB(bin, bout, vformat); long stopTime = System.nanoTime();
* totalTime += (stopTime - startTime) / 1.0e9; ++nCalls; }
*
* // Why swap input and output data? if(!processed) { // Swap the data
* between the input & output. Object data = in.getData();
* in.setData(out.getData()); // not necessary out.setData(data); buffToDraw
* = bin; }
*
* //// Update frame image available to UI if (frameListenerList.size() > 0)
* {
*
* //// Assure the image is the proper size if (displayImage.getWidth() !=
* vformat.getSize().width || displayImage.getHeight() !=
* vformat.getSize().height) { displayImage = new
* BufferedImage(vformat.getSize().width, vformat.getSize().height,
* BufferedImage.TYPE_INT_RGB); } updateImage(buffToDraw, vformat);
* notifyVideoFrameListeners(); } }
*
* // Copy the input attributes to the output
* //out.setFormat(in.getFormat()); //out.setLength(in.getLength());
* //out.setOffset(in.getOffset());
*
* return BUFFER_PROCESSED_OK; }
*/
public int process(Buffer in, Buffer out) {
if (!(in.getFormat() instanceof VideoFormat && in.getData() != null)) {
return PlugIn.BUFFER_PROCESSED_FAILED;
}
byte[] bin, bout;
VideoFormat vformat = (VideoFormat) in.getFormat();
// get input byte array
if (in.getData() instanceof byte[]) {
bin = (byte[]) in.getData();
} else if (in.getData() instanceof int[]) {
int[] rgb = (int[]) in.getData();
bin = new byte[rgb.length * 3];
for (int i = 0; i < rgb.length; i++) {
int index = i * 3;
int pixel = rgb[i];
bin[index + 2] = (byte) (pixel & 0xff);
bin[index + 1] = (byte) (pixel >> 8 & 0xff);
bin[index] = (byte) (pixel >> 16 & 0xff);
}
} else {
return PlugIn.BUFFER_PROCESSED_FAILED;
}
// get output byte array
if (!(out.getData() instanceof byte[])
|| ((byte[]) out.getData()).length < bin.length) {
bout = new byte[bin.length];
out.setData(bout);
} else {
bout = (byte[]) out.getData();
}
maintainSize(vformat);
boolean processed = false;
if (active) {
processed = processRGB(bin, bout, vformat);
}
byte[] buffToDraw = bout;
if (!processed) {
Object data = in.getData();
out.setData(data);
buffToDraw = bin;
}
updateDisplay(vformat, buffToDraw);
return BUFFER_PROCESSED_OK;
}
private void updateDisplay(VideoFormat vformat, byte[] buffToDraw) {
if (frameListenerList.size() > 0) {
boolean isSameSize = displayImage.getWidth() == vformat.getSize().width
&& displayImage.getHeight() == vformat.getSize().height;
if (!isSameSize) {
displayImage = new BufferedImage(vformat.getSize().width,
vformat.getSize().height, BufferedImage.TYPE_INT_RGB);
}
updateImage(buffToDraw, vformat);
notifyVideoFrameListeners();
}
}
private void maintainSize(VideoFormat vformat) {
if (!vformat.getSize().equals(videoSize)) {
videoSize = vformat.getSize();
propSupport.firePropertyChange(PROP_VIDEO_SIZE, null, videoSize);
}
}
protected abstract boolean processRGB(byte[] bin, byte[] bout,
VideoFormat format);
protected void updateImage(byte[] bout, VideoFormat vformat) {
synchronized (displayImage) {
WritableRaster rast = displayImage.getRaster();
int[] pixel = new int[] { 0, 0, 0, 255 };
int p = 0;
for (int y = vformat.getSize().height - 1; y >= 0; y--) {
for (int x = 0; x < vformat.getSize().width; x++) {
pixel[0] = bout[p++];
pixel[1] = bout[p++];
pixel[2] = bout[p++];
rast.setPixel(x, y, pixel);
}
}
}
}
/*
* public void setDisplayImage(BufferedImage image) { displayImage = image;
* }
*/
public BufferedImage getDisplayImage() {
return displayImage;
}
public void addVideoFrameListener(VideoFrameListener listener) {
synchronized (frameListenerList) {
frameListenerList.add(listener);
}
}
public void removeVideoFrameListener(VideoFrameListener listener) {
synchronized (frameListenerList) {
frameListenerList.remove(listener);
/*
* if(frameListenerList.size() < 0) { displayImage = new
* BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); }
*/
}
}
/**
* Gets the average time per frame that this effect takes to process, in
* seconds.
*
* @return Average time in seconds.
*/
public double getAvgProcessingTime() {
return totalTime == 0.0 ? 0.0 : (double) nCalls / totalTime;
}
public Object[] getControls() {
return new Object[0];
}
public Object getControl(String type) {
return null;
}
public abstract String getName();
// No op.
public void open() {
}
// No op.
public void close() {
}
// No op.
public void reset() {
}
public Format[] getSupportedInputFormats() {
// return new Format[] {
// new RGBFormat(new Dimension(640, 480), 640 * 480 * 4, Byte.class,
// 29.9f, 8, 1, 2, 3)
// };
return supportedIns;
}
public Format[] getSupportedOutputFormats(Format in) {
if (in == null)
return supportedOuts;
else {
// If an input format is given, we use that input format
// as the output since we are not modifying the bit stream
// at all.
Format outs[] = new Format[1];
outs[0] = in;
return outs;
}
}
protected int byte2Int(byte b) {
if (b<0) {
return 256+b;
} else {
return b;
}
}
public Format setInputFormat(Format format) {
input = format;
return input;
}
public Format setOutputFormat(Format format) {
output = format;
return output;
}
/**
* Returns true if the effect is active; false if deactivated.
*
* @return true if the effect is active; false if deactivated.
*/
public boolean isActive() {
return active;
}
/**
* Actives or deactivates this effect. If deactivated, the effect will
* simply pass the video frames through to the next effect in the processing
* chain.
*
* @param active
* True if the effect is active.
*/
public void setActive(boolean active) {
this.active = active;
}
/** Notifies the frame listeners of a new frame. */
private void notifyVideoFrameListeners() {
synchronized (frameListenerList) {
for (VideoFrameListener listener : frameListenerList) {
listener.newVideoFrame(displayImage);
}
}
}
}