Main Page | Class Hierarchy | Class List | File List | Class Members | Related Pages

JPEGThumbnailer.java

00001 // [TODO: gota test this !!!]
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; //for argument parsing
00022 import COM.Creare.Utility.RBNBProcess; //alternative to System.exit, so
00023                                        //don't bring down servlet engine
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; // secondes
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; // required
00050 
00051     private double outputSampleRate = DEFAULT_SAMPLE_RATE; // seconds
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         //parse args
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         // open connection to RBNB server as a sink
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         // select input channel to monitor
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         // monitor input channel for data
00136         try {
00137             //sink.Subscribe(reqmap);
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         // connect to RBNB server as source
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         // select channel to output data to
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) { //loop forever, receiving JPEG frames        
00174 
00175             // see if any data is available from source
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             // no data received, try again
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             // calculate instantaneous sample rate
00196             double sampleRate;          
00197             if (imageIndex > 0) {
00198                 sampleRate = 1/(startTime-oldStartTime);
00199             } else {
00200                 sampleRate = 30;
00201             }
00202             
00203             // time between sample is too close (or the same)
00204             if (Double.isInfinite(sampleRate)) {
00205                 sampleRate = averageInputSampleRate;
00206             }
00207         
00208             // calculate average sample rate
00209             averageInputSampleRate = averageInputSampleRate*0.995 + sampleRate*0.005;
00210             
00211             /* calculate the number of frames to skip,
00212              * roundedFramesToSkip-1 is the actual number of frames skipped
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             // only create thumbnail if we have skipped enough frames       
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                 // decode JPEG image to raw image
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                 // scale both width and height by 0.5
00238                 AffineTransformOp op = new AffineTransformOp(AffineTransform.getScaleInstance(0.5, 0.5), null);
00239                 bi = op.filter(bi, null);
00240                 
00241                 // encode scaled image as JPEG image                
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                 // get JPEG image data as byte array
00254                 imageData = out.toByteArray();
00255     
00256                 // put data in channel map, preserving original time stamp
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                 // send data to RBNB server
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 }

Generated on Tue Mar 23 11:54:24 2004 for Data turbine for NEESGrid by doxygen 1.3.6