package streamExample.agent; import org.jboss.netty.bootstrap.ServerBootstrap; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.group.ChannelGroup; import org.jboss.netty.channel.group.DefaultChannelGroup; import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import streamExample.channel.StreamServerChannelPipelineFactory; import streamExample.coserver.CoordinationServer; import streamExample.handler.H264StreamEncoder; import streamExample.handler.StreamServerListener; import streamExample.utils.HostData; import java.awt.*; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.util.concurrent.*; public class StreamServerAgent implements IStreamServerAgent { protected final static Logger logger = LoggerFactory.getLogger(StreamServerAgent.class); protected final ImageSource imageSource; protected final Dimension dimension; protected final ChannelGroup channelGroup = new DefaultChannelGroup(); protected final ServerBootstrap serverBootstrap; //I just move the stream encoder out of the channel pipeline for the performance protected final H264StreamEncoder h264StreamEncoder; protected volatile boolean isStreaming; protected ScheduledExecutorService timeWorker; protected ExecutorService encodeWorker; protected double FPS = 25; protected ScheduledFuture<?> imageGrabTaskFuture; protected final int streamServerPort; public StreamServerAgent(ImageSource imageSource, Dimension dimension, int port) { super(); this.imageSource = imageSource; this.dimension = dimension; this.serverBootstrap = new ServerBootstrap(); this.serverBootstrap.setFactory(new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); this.serverBootstrap.setPipelineFactory(new StreamServerChannelPipelineFactory( new StreamServerListenerIMPL())); this.timeWorker = new ScheduledThreadPoolExecutor(1); this.encodeWorker = Executors.newSingleThreadExecutor(); this.h264StreamEncoder = new H264StreamEncoder(dimension, false); this.streamServerPort = port; } public double getFPS() { return FPS; } public void setFPS(double FPS) { this.FPS = FPS; } public void notifyStreamServer() { Socket clientSocket = null; InetSocketAddress address = null; try { clientSocket = new Socket(CoordinationServer.COORDINATION_SERVER_HOST, CoordinationServer.COORDINATION_SERVER_PORT); ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream()); String msgType = CoordinationServer.SERVER_CONNECTING; outToServer.writeObject(new HostData(null, streamServerPort, msgType)); } catch (IOException e) { e.printStackTrace(); } finally { try { clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } @Override public void start(SocketAddress streamAddress) { notifyStreamServer(); logger.info("Server started :{}", streamAddress); Channel channel = serverBootstrap.bind(streamAddress); channelGroup.add(channel); } @Override public void stop() { logger.info("server is stoping"); channelGroup.close(); timeWorker.shutdown(); encodeWorker.shutdown(); serverBootstrap.releaseExternalResources(); } private class StreamServerListenerIMPL implements StreamServerListener { @Override public void onClientConnectedIn(Channel channel) { //here we just start to stream when the first client connected in channelGroup.add(channel); if (!isStreaming) { Runnable imageGrabTask = new ImageGrabTask(); ScheduledFuture<?> imageGrabFuture = timeWorker.scheduleWithFixedDelay(imageGrabTask, 0, (long) (1000000 / FPS), TimeUnit.MICROSECONDS); imageGrabTaskFuture = imageGrabFuture; isStreaming = true; } logger.info("current connected clients :{}", channelGroup.size()); } @Override public void onClientDisconnected(Channel channel) { channelGroup.remove(channel); int size = channelGroup.size(); logger.info("current connected clients :{}", size); } @Override public void onException(Channel channel, Throwable t) { channelGroup.remove(channel); channel.close(); int size = channelGroup.size(); logger.info("current connected clients :{}", size); } protected volatile long frameCount = 0; private class ImageGrabTask implements Runnable { @Override public void run() { logger.info("image grabed ,count :{}", frameCount++); BufferedImage bufferedImage = imageSource.getImage(); encodeWorker.execute(new EncodeTask(bufferedImage)); } } private class EncodeTask implements Runnable { private final BufferedImage image; public EncodeTask(BufferedImage image) { super(); this.image = image; } @Override public void run() { try { Object msg = h264StreamEncoder.encode(image); if (msg != null) { channelGroup.write(msg); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }