/*****************************************************************************
* Copyright (c) 2006-2008 g-Eclipse Consortium
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Initial development of the original code was made for the
* g-Eclipse project founded by European Union
* project number: FP6-IST-034327 http://www.geclipse.eu/
*
* Contributors:
* Thomas Koeckerbauer GUP, JKU - initial API and implementation
*****************************************************************************/
package eu.geclipse.gvid.internal;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Panel;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.swing.event.EventListenerList;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.swt.widgets.Display;
import eu.geclipse.core.reporting.ProblemException;
import eu.geclipse.core.reporting.ReportingPlugin;
import eu.geclipse.gvid.Activator;
import eu.geclipse.gvid.IDecoder;
import eu.geclipse.gvid.IGVidStatsListener;
import eu.geclipse.gvid.internal.preferences.PreferenceConstants;
import eu.geclipse.ui.dialogs.ProblemDialog;
/**
* Output client for GVid. Allows to interact with remote rendered interactive
* visualization applictions.
*/
public class GVidClient extends Panel implements Runnable {
private static final long serialVersionUID = 1L;
private Connection connection;
private IDecoder decoder;
private int oldFps = -1;
private long oldRecvSpd = -1;
private long oldSendSpd = -1;
private Events events;
private AwtEventConverter awtEventConverter;
private boolean running;
private BufferedImage lastImage;
private Graphics graphics;
private final EventListenerList listenerList = new EventListenerList();
private final FpsCounter fpsCounter = new FpsCounter();
/**
* Creates a new GVid client.
* @param in stream to read the video stream from.
* @param out stream to write the event data to.
*/
public GVidClient( final InputStream in, final OutputStream out ) {
IPreferenceStore store = Activator.getDefault().getPreferenceStore();
String codecName = store.getString( PreferenceConstants.P_CODEC_NAME );
this.connection = new Connection( in, out );
this.events = new Events( this.connection );
this.awtEventConverter = new AwtEventConverter( this.events );
this.running = false;
this.lastImage = new BufferedImage( 100, 100, BufferedImage.TYPE_3BYTE_BGR );
this.decoder = getDecoderImpl( codecName );
if( this.decoder != null ) {
this.decoder.init( this.connection, this.events );
}
registerEvents();
}
private IDecoder getDecoderImpl( final String codecName ) {
CoreException exception = null;
IDecoder decoderImpl = null;
try {
IExtensionPoint p = Platform.getExtensionRegistry()
.getExtensionPoint( IDecoder.CODEC_EXTENSION_POINT );
IExtension[] extensions = p.getExtensions();
for( IExtension extension : extensions ) {
IConfigurationElement[] elements = extension.getConfigurationElements();
for( IConfigurationElement element : elements ) {
if( IDecoder.EXT_CODEC.equals( element.getName() )
&& codecName.equals( element.getAttribute( IDecoder.EXT_NAME ) ) ) {
decoderImpl = (IDecoder) element.createExecutableExtension( IDecoder.EXT_DECODER_CLASS );
}
}
}
} catch( CoreException coreException ) {
exception = coreException;
// Activator.logException( coreException );
}
if ( decoderImpl == null || exception != null) {
ProblemDialog.openProblem( Display.getCurrent().getActiveShell(),
Messages.getString( "GVidClient.gvid" ), //$NON-NLS-1$
Messages.formatMessage( "GVidClient.cantInstanciateCodec", codecName ), //$NON-NLS-1$
exception );
}
return decoderImpl;
}
/**
* Sends a quit event to the remote host and stops the GVid client.
*/
public void stop() {
try {
this.events.sendQuitEvent();
this.running = false;
} catch( IOException exception ) {
// ignore
}
}
/**
* Checks if the GVid output client is running.
* @return true if the GVid output client is running, false otherwise.
*/
public boolean isRunning() {
return this.running;
}
/* (non-Javadoc)
* @see java.awt.Component#getPreferredSize()
*/
@Override
public Dimension getPreferredSize() {
return new Dimension( this.lastImage.getWidth(), this.lastImage.getHeight() );
}
private void registerEvents() {
addMouseListener( this.awtEventConverter );
addMouseMotionListener( this.awtEventConverter );
addMouseWheelListener( this.awtEventConverter );
addKeyListener( this.awtEventConverter );
addComponentListener( this.awtEventConverter );
// frame.addWindowListener(awtEventConverter);
}
private void unregisterEvents() {
removeMouseListener( this.awtEventConverter );
removeMouseMotionListener( this.awtEventConverter );
removeMouseWheelListener( this.awtEventConverter );
removeKeyListener( this.awtEventConverter );
removeComponentListener( this.awtEventConverter );
// frame.removeWindowListener(awtEventConverter);
}
/* (non-Javadoc)
* @see java.awt.Component#getFocusTraversalKeysEnabled()
*/
@Override
public boolean getFocusTraversalKeysEnabled() {
return false;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
boolean initialSizeSet = false;
if( this.decoder == null )
return;
this.running = true;
this.graphics = getGraphics();
requestFocusInWindow();
while( this.running ) {
try {
if( this.decoder.decodeNextFrame() ) {
this.fpsCounter.incFrameCount();
if( this.lastImage.getWidth() != this.decoder.getImage().getWidth()
|| this.lastImage.getHeight() != this.decoder.getImage().getHeight() ) {
this.graphics = getGraphics();
}
this.lastImage = this.decoder.getImage();
if( !initialSizeSet ) {
setSize( this.lastImage.getWidth(), this.lastImage.getHeight() );
initialSizeSet = true;
}
imageUpdate( this.lastImage, ImageObserver.FRAMEBITS, 0, 0, this.lastImage.getWidth(), this.lastImage.getHeight() );
} else {
Thread.sleep( 10 );
}
this.awtEventConverter.checkForException();
updateTransferStats();
this.events.flush();
} catch( InterruptedException interruptedEx ) {
this.running = false;
} catch( IOException ioException ) {
this.running = false;
} catch( Exception exception ) {
this.running = false;
final ProblemException pException = new ProblemException(
ReportingPlugin.getReportingService()
.getProblem( null, null, exception, Activator.PLUGIN_ID ) );
Display.getDefault().asyncExec( new Runnable() {
public void run() {
ProblemDialog.openProblem( null,
"Error during video decoding",
"An error occurred during video decoding (is the right codec selected?)",
pException );
}
} );
}
}
unregisterEvents();
}
private void updateTransferStats() {
if( this.fpsCounter.getFps() != this.oldFps
|| this.connection.getCurrentRecvSpeed() != this.oldRecvSpd
|| this.connection.getCurrentSendSpeed() != this.oldSendSpd ) {
this.oldFps = this.fpsCounter.getFps();
this.oldRecvSpd = this.connection.getCurrentRecvSpeed();
this.oldSendSpd = this.connection.getCurrentSendSpeed();
GVidStatsEvent event = new GVidStatsEvent( this,
this.oldFps,
this.oldRecvSpd,
this.oldSendSpd );
fireStatsEvent( event );
}
}
/**
* Adds a listener for GVid status updates.
* @param listener the listener to register.
*/
public void addStatsListener( final IGVidStatsListener listener ) {
this.listenerList.add(IGVidStatsListener.class, listener);
}
/**
* Removes a listener for GVid status updates.
* @param listener the listener to unregister.
*/
public void removeStatsListener( final IGVidStatsListener listener ) {
this.listenerList.remove(IGVidStatsListener.class, listener);
}
private void fireStatsEvent( final GVidStatsEvent event ) {
IGVidStatsListener[] listeners = this.listenerList.getListeners( IGVidStatsListener.class );
for (int i = 0; i < listeners.length; i++) {
listeners[i].statsUpdated( event );
}
}
@Override
public void update( final Graphics g ) {
paint(g);
}
/* (non-Javadoc)
* @see java.awt.Component#paint(java.awt.Graphics)
*/
@Override
public void paint( final Graphics g ) {
g.drawImage( this.lastImage, 0, 0, null );
}
}