00001
00002 package org.nees.buffalo.video;
00003
00004 import java.awt.geom.AffineTransform;
00005 import java.awt.image.AffineTransformOp;
00006 import java.awt.image.BufferedImage;
00007 import java.io.ByteArrayInputStream;
00008 import java.io.ByteArrayOutputStream;
00009 import java.io.IOException;
00010
00011 import com.rbnb.sapi.ChannelMap;
00012 import com.rbnb.sapi.SAPIException;
00013 import com.rbnb.sapi.Sink;
00014 import com.rbnb.sapi.Source;
00015
00016 import com.sun.image.codec.jpeg.JPEGCodec;
00017 import com.sun.image.codec.jpeg.JPEGEncodeParam;
00018 import com.sun.image.codec.jpeg.JPEGImageDecoder;
00019 import com.sun.image.codec.jpeg.JPEGImageEncoder;
00020
00021 import COM.Creare.Utility.ArgHandler;
00022 import COM.Creare.Utility.RBNBProcess;
00023
00024
00032 public class JPEGThumbnailer {
00033
00034 private final static String DEFAULT_RBNB_SERVER = "localhost";
00035 private final static String DEFAULT_RBNB_PORT = "3333";
00036 private final static String DEFAULT_RBNB_OUTPUT_NAME = "TSource";
00037 private final static String DEFAULT_RBNB_OUTPUT_CHANNEL = "thumbnail.jpg";
00038 private final static String DEFAULT_RBNB_SINK_NAME = "JPEGThumbanilerSink";
00039 private final static double DEFAULT_SAMPLE_RATE = 1.0;
00040
00041 private String rbnbServerName = DEFAULT_RBNB_SERVER;
00042 private String rbnbServerPort = DEFAULT_RBNB_PORT;
00043 private String rbnbHostName = rbnbServerName + ":" + rbnbServerPort;
00044 private String rbnbSourceName = DEFAULT_RBNB_OUTPUT_NAME;
00045 private String rbnbSourceChannel = DEFAULT_RBNB_OUTPUT_CHANNEL;
00046
00047 private String rbnbSinkName = DEFAULT_RBNB_SINK_NAME;
00048
00049 private String rbnbInputPath = null;
00050
00051 private double outputSampleRate = DEFAULT_SAMPLE_RATE;
00052
00053 private static void printUsage()
00054 {
00055 System.out.println("Usage for JPEGThumbnailer...");
00056 System.out.println(" -v RBNB Sink (input video) path (required) ");
00057 System.out.println(" [-r RBNB Server Name *"+ DEFAULT_RBNB_SERVER + "]");
00058 System.out.println(" [-p RBNB Server Port *" + DEFAULT_RBNB_PORT + "]");
00059 System.out.println(" [-s RBNB Source (output) Name *" + DEFAULT_RBNB_OUTPUT_NAME + "]");
00060 System.out.println(" [-c RBNB Source (output) Channel *" + DEFAULT_RBNB_OUTPUT_CHANNEL + "]");
00061 System.out.println(" [-f desired output frame rate (in seconds)*" + DEFAULT_SAMPLE_RATE + "]");
00062 }
00063
00064 public JPEGThumbnailer(String[] args)
00065 {
00066
00067 try {
00068 ArgHandler ah=new ArgHandler(args);
00069 if (ah.checkFlag('h')) {
00070 printUsage();
00071 RBNBProcess.exit(0);
00072 }
00073 if (ah.checkFlag('r')) {
00074 String a=ah.getOption('r');
00075 if (a!=null) rbnbServerName=a;
00076 }
00077 if (ah.checkFlag('p')) {
00078 String a=ah.getOption('p');
00079 if (a!=null) rbnbServerPort=a;
00080 }
00081 if (ah.checkFlag('s')) {
00082 String a=ah.getOption('s');
00083 if (a!=null) rbnbSourceName=a;
00084 }
00085 if (ah.checkFlag('c')) {
00086 String a=ah.getOption('c');
00087 if (a!=null) rbnbSourceChannel=a;
00088 }
00089 if (ah.checkFlag('v')) {
00090 String a=ah.getOption('v');
00091 if (a!=null) rbnbInputPath=a;
00092 }
00093 if (ah.checkFlag('f')) {
00094 String a=ah.getOption('f');
00095 if (a!=null) outputSampleRate = Double.parseDouble(a);
00096 }
00097 } catch (Exception e) {
00098 System.err.println("JPEGThumbnailer argument exception "+e.getMessage());
00099 e.printStackTrace();
00100 RBNBProcess.exit(0);
00101 }
00102
00103 if (rbnbInputPath == null)
00104 {
00105 System.err.println("The source/channel path for the video source is required. "
00106 + "Use JPEGThumbnailer -h for help");
00107 RBNBProcess.exit(0);
00108 }
00109
00110 rbnbHostName = rbnbServerName + ":" + rbnbServerPort;
00111 }
00112
00113 private void execute() {
00114
00115 Sink sink = new Sink();
00116 try {
00117 sink.OpenRBNBConnection(rbnbHostName, rbnbSinkName);
00118 } catch (SAPIException e) {
00119 System.err.println("Failed to connect to RBNB server for sink (video input).");
00120 e.printStackTrace();
00121 return;
00122 }
00123
00124
00125 ChannelMap reqmap = new ChannelMap();
00126 int inputChannelIndex = -1;
00127 try {
00128 inputChannelIndex = reqmap.Add(rbnbInputPath);
00129 } catch (SAPIException e) {
00130 System.err.println("Failed to add input channel to channel map.");
00131 e.printStackTrace();
00132 return;
00133 }
00134
00135
00136 try {
00137
00138 sink.Monitor(reqmap, 0);
00139 } catch (SAPIException e) {
00140 System.err.println("Failed to monitor input channel.");
00141 e.printStackTrace();
00142 return;
00143 }
00144
00145
00146 Source source = new Source(10, "none", 0);
00147 try {
00148 source.OpenRBNBConnection(rbnbHostName, rbnbSourceName);
00149 } catch (SAPIException e) {
00150 System.err.println("Failed to connect to RBNB server for source.");
00151 e.printStackTrace();
00152 return;
00153 }
00154
00155
00156 ChannelMap cmap = new ChannelMap();
00157 int outputChannelIndex = -1;
00158 try {
00159 outputChannelIndex = cmap.Add(rbnbSourceChannel);
00160 } catch (SAPIException e) {
00161 System.err.println("Failed to add output channel to channel map.");
00162 e.printStackTrace();
00163 return;
00164 }
00165 cmap.PutMime(outputChannelIndex, "image/jpeg");
00166
00167 byte[] imageData;
00168 double startTime = 0;
00169 double oldStartTime = 0;
00170 double durationTime = 0;
00171 int imageIndex = 0;
00172 double averageInputSampleRate = 30;
00173 while (true) {
00174
00175
00176 ChannelMap getmap = null;
00177 try {
00178 getmap = sink.Fetch(1000);
00179 } catch (SAPIException e) {
00180 System.err.println("Failed to fetch input data, retrying.");
00181 continue;
00182 }
00183
00184
00185 if (getmap.GetIfFetchTimedOut()) {
00186 System.err.println("Data request timed out, retrying.");
00187 continue;
00188 }
00189
00190 oldStartTime = startTime;
00191 startTime = getmap.GetTimeStart(inputChannelIndex);
00192 durationTime = getmap.GetTimeDuration(inputChannelIndex);
00193 imageData = getmap.GetData(inputChannelIndex);
00194
00195
00196 double sampleRate;
00197 if (imageIndex > 0) {
00198 sampleRate = 1/(startTime-oldStartTime);
00199 } else {
00200 sampleRate = 30;
00201 }
00202
00203
00204 if (Double.isInfinite(sampleRate)) {
00205 sampleRate = averageInputSampleRate;
00206 }
00207
00208
00209 averageInputSampleRate = averageInputSampleRate*0.995 + sampleRate*0.005;
00210
00211
00212
00213
00214 long roundedFramesToSkip = Math.round(averageInputSampleRate/outputSampleRate);
00215 if (roundedFramesToSkip < 1) {
00216 roundedFramesToSkip = 1;
00217 } else if (roundedFramesToSkip > averageInputSampleRate){
00218 roundedFramesToSkip = 1;
00219 }
00220
00221
00222 if (imageIndex % roundedFramesToSkip == 0) {
00223
00224 System.out.print("Receiving " + ((double)Math.round(averageInputSampleRate*10))/10 + " fps and converting to " + outputSampleRate + " fps \r");
00225
00226 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(imageData));
00227
00228
00229 BufferedImage bi;
00230 try {
00231 bi = decoder.decodeAsBufferedImage();
00232 } catch (IOException e){
00233 System.err.println("Failed to decode input JPEG image, skipping.");
00234 continue;
00235 }
00236
00237
00238 AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(0.5, 0.5), null);
00239 bi = op.filter(bi, null);
00240
00241
00242 ByteArrayOutputStream out = new ByteArrayOutputStream();
00243 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
00244 JPEGEncodeParam param = encoder.getDefaultJPEGEncodeParam(bi);
00245 param.setQuality(0.75f, false);
00246 try {
00247 encoder.encode(bi, param);
00248 } catch (IOException e) {
00249 System.err.println("Failed to encode output JPEG image, skipping.");
00250 continue;
00251 }
00252
00253
00254 imageData = out.toByteArray();
00255
00256
00257 try {
00258 cmap.PutTime(startTime, durationTime);
00259 cmap.PutDataAsByteArray(outputChannelIndex, imageData);
00260 } catch (SAPIException e) {
00261 System.err.println("Failed to put output data to channel map, skipping.");
00262 continue;
00263 }
00264
00265
00266 try {
00267 source.Flush(cmap, true);
00268 } catch (SAPIException e) {
00269 System.err.println("Failed to flush output data to server, skipping.");
00270 continue;
00271 }
00272
00273 }
00274
00275 imageIndex++;
00276 }
00277 }
00278
00279 public static void main(String[] args) {
00280 JPEGThumbnailer t = new JPEGThumbnailer(args);
00281 t.execute();
00282 }
00283 }