package us.sosia.video.stream.agent; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.net.SocketAddress; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; 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 us.sosia.video.stream.channel.StreamServerChannelPipelineFactory; import us.sosia.video.stream.handler.H264StreamEncoder; import us.sosia.video.stream.handler.StreamServerListener; import com.github.sarxos.webcam.Webcam; public class StreamServerAgent implements IStreamServerAgent{ protected final static Logger logger = LoggerFactory.getLogger(StreamServer.class); protected final Webcam webcam; 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 int FPS = 25; protected ScheduledFuture<?> imageGrabTaskFuture; public StreamServerAgent(Webcam webcam, Dimension dimension) { super(); this.webcam = webcam; this.dimension = dimension; //this.h264StreamEncoder = new H264StreamEncoder(dimension,false); this.serverBootstrap = new ServerBootstrap(); this.serverBootstrap.setFactory(new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); this.serverBootstrap.setPipelineFactory(new StreamServerChannelPipelineFactory( new StreamServerListenerIMPL(), dimension)); this.timeWorker = new ScheduledThreadPoolExecutor(1); this.encodeWorker = Executors.newSingleThreadExecutor(); this.h264StreamEncoder = new H264StreamEncoder(dimension, false); } public int getFPS() { return FPS; } public void setFPS(int fPS) { FPS = fPS; } @Override public void start(SocketAddress streamAddress) { 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) { //do some thing Runnable imageGrabTask = new ImageGrabTask(); ScheduledFuture<?> imageGrabFuture = timeWorker.scheduleWithFixedDelay(imageGrabTask, 0, 1000/FPS, TimeUnit.MILLISECONDS); 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); if (size == 1) { //cancel the task imageGrabTaskFuture.cancel(false); webcam.close(); isStreaming = false; } } @Override public void onExcaption(Channel channel, Throwable t) { channelGroup.remove(channel); channel.close(); int size = channelGroup.size(); logger.info("current connected clients :{}",size); if (size == 1) { //cancel the task imageGrabTaskFuture.cancel(false); webcam.close(); isStreaming = false; } } protected volatile long frameCount = 0; private class ImageGrabTask implements Runnable{ @Override public void run() { logger.info("image grabed ,count :{}",frameCount++); BufferedImage bufferedImage = webcam.getImage(); /** * using this when the h264 encoder is added to the pipeline * */ //channelGroup.write(bufferedImage); /** * using this when the h264 encoder is inside this class * */ 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(); } } } } }