00001
00002
00003
00004
00005
00006
00007
package org.nees.rbnb;
00008
00009
import java.io.ByteArrayInputStream;
00010
import java.io.ByteArrayOutputStream;
00011
import java.io.IOException;
00012
import javax.media.protocol.PullBufferDataSource;
00013
import javax.media.protocol.PullBufferStream;
00014
00015
import java.util.Date;
00016
import java.util.TimeZone;
00017
import java.text.SimpleDateFormat;
00018
00019
import java.awt.Dimension;
00020
import java.awt.image.BufferedImage;
00021
import java.awt.Image;
00022
00023
import javax.media.MediaLocator;
00024
import javax.media.Time;
00025
import javax.media.*;
00026
import javax.media.control.*;
00027
import javax.media.protocol.*;
00028
import javax.media.protocol.DataSource;
00029
import javax.media.datasink.*;
00030
import javax.media.format.VideoFormat;
00031
import javax.media.format.AudioFormat;
00032
import javax.imageio.ImageIO;
00033
import com.sun.image.codec.jpeg.*;
00034
00035
import com.sun.media.multiplexer.*;
00036
00037
import com.sun.image.codec.jpeg.ImageFormatException;
00038
import com.sun.image.codec.jpeg.JPEGCodec;
00039
import com.sun.image.codec.jpeg.JPEGImageDecoder;
00040
00041
import com.rbnb.sapi.*;
00042
00043
import com.rbnb.utility.ArgHandler;
00044
00045
00052 public class DataVideoSink {
00053
00054
double[] dataArray =
new double[0];
00055
double[] timeArray =
new double[0];
00056
00057 Sink dataSink;
00058 Sink videoSink;
00059
00060
boolean connected =
false;
00061
00062
int videoWidth = 480;
00063
int videoHeight = 320;
00064
00065 byte [] prevImage = (byte[]) null;
00066
00067
00068
public static boolean DEBUG =
false;
00069
00070
private static final String SERVER_NAME =
"neestpm.mcs.anl.gov";
00071
private static final String SERVER_PORT =
"3333";
00072
private static final String SINK_NAME =
"DataVideoSink";
00073
00074
private static final SimpleDateFormat DATE_FORMAT =
new SimpleDateFormat(
"MMM d, yyyy h:mm:ss.SSS aa");
00075
private static final TimeZone TZ = TimeZone.getTimeZone(
"GMT");
00076
00077
private static final SimpleDateFormat INPUT_FORMAT =
new SimpleDateFormat(
"yyyy-MM-dd:hh:mm:ss.SSS");
00078
00079
static
00080 {
00081 DATE_FORMAT.setTimeZone(TZ);
00082 INPUT_FORMAT.setTimeZone(TZ);
00083 }
00084
00085
private static double USER_GIVEN_FRAME_RATE = 1.0;
00086
private static int ITEMS_TO_SKIP = 0;
00087
00088
private static String t_sinkName = SINK_NAME;
00089
private static String t_dataSourcePath = null;
00090
private static String t_videoSourcePath = null;
00091
private static double t_startTime = -1;
00092
private static double t_duration = -1;
00093
private static double t_userGivenFrameRate = USER_GIVEN_FRAME_RATE;
00094
private static int t_itemsToSkip = ITEMS_TO_SKIP;
00095
00096
private static String t_outputURL;
00097
00098
private static String t_serverName = SERVER_NAME;
00099
private static String t_serverPort = SERVER_PORT;
00100
private static String t_server = t_serverName +
":" + t_serverPort;
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
public static void main(String[] args) {
00116
00117
try {
00118
if (setArgs(args))
00119 {
00120
DataVideoSink s =
new DataVideoSink();
00121
if (s.
connect(t_server,t_sinkName))
00122 {
00123 System.out.println(
"DataVideoSink: Connection made to server = "
00124 + t_server +
" as " + t_sinkName +
" for " + t_dataSourcePath
00125 +
" and as " + t_sinkName +
"_video" +
" for " + t_videoSourcePath +
".");
00126 System.out.println(
"Set to request a fetch of time and duration = "
00127 + t_startTime +
"," + t_duration);
00128
00129 s.
fetchData(t_dataSourcePath, t_startTime, t_duration);
00130
00131
int items = s.
getNumberOfItems();
00132 System.out.println(
"Number of Items is " + items);
00133
00134 s.
makeMovie((
float)t_userGivenFrameRate,
00135 t_outputURL, t_videoSourcePath, t_itemsToSkip);
00136
00137
double [] times = s.
getTimeArray();
00138
double [] data = s.
getDataArray();
00139
00140 DEBUG =
true;
00141
00142 System.out.println(
"Number of Items is " + items);
00143
for (
int i = 0; i < items; i++)
00144 {
00145
long unixTime = (
long)(times[i] * 1000.0);
00146 String time = DATE_FORMAT.format(
new Date(unixTime));
00147
00148 System.out.println(
" " + i
00149 +
" ("+ times[i] +
" - " + time +
"): "
00150 + data[i]);
00151 }
00152 }
00153 }
00154 }
catch (Exception e) {
00155 e.printStackTrace();
00156 printUsage();
00157 }
00158 System.exit(0);
00159 }
00160
00161
private static void printUsage() {
00162 System.out.println(
"DataVideoSink: usage is... (for testing only)");
00163 System.out.println(
"DataVideoSink ");
00164 System.out.println(
"[-s Server Hostname *" + SERVER_NAME +
"] ");
00165 System.out.println(
"[-p Server Port Number *" + SERVER_PORT +
"] ");
00166 System.out.println(
"[-k Sink Name *" + SINK_NAME +
" ]");
00167 System.out.println(
"[-f Frame Rate in final movie *" + USER_GIVEN_FRAME_RATE +
"]");
00168 System.out.println(
"[-i Items to skip in data for each movie frame *" +
00169 ITEMS_TO_SKIP +
"]");
00170 System.out.println(
"-d Data Source Path - required");
00171 System.out.println(
"-v Video Source path - required");
00172 System.out.println(
"-a Start Time - required");
00173 System.out.println(
"-z End Time - required");
00174 System.out.println(
"-m Movie output file URL - required");
00175
00176 System.out.println(
"Note: times can either be yyyy-mm-dd:hh:mm:ss.nnn or");
00177 System.out.println(
"an arbitraty floating point number");
00178 }
00179
00180
public static boolean setArgs(String[] args)
00181
throws Exception
00182 {
00183
double t_endTime = -1;
00184
00185
try {
00186 ArgHandler ah=
new ArgHandler(args);
00187
if (ah.checkFlag(
'h')) {
00188 printUsage();
00189
return false;
00190 }
00191
if (ah.checkFlag(
's')) {
00192 String a=ah.getOption(
's');
00193
if (a!=null) t_serverName=a;
00194 }
00195
if (ah.checkFlag(
'p')) {
00196 String a=ah.getOption(
'p');
00197
if (a!=null) t_serverPort=a;
00198 }
00199
if (ah.checkFlag(
'k')) {
00200 String a=ah.getOption(
'k');
00201
if (a!=null) t_sinkName=a;
00202 }
00203
if (ah.checkFlag(
'd')) {
00204 String a=ah.getOption(
'd');
00205
if (a!=null) t_dataSourcePath=a;
00206 }
00207
if (ah.checkFlag(
'v')) {
00208 String a=ah.getOption(
'v');
00209
if (a!=null) t_videoSourcePath=a;
00210 }
00211
if (ah.checkFlag(
'm')) {
00212 String a=ah.getOption(
'm');
00213
if (a!=null) t_outputURL=a;
00214 }
00215
if (ah.checkFlag(
'a')) {
00216 String a=ah.getOption(
'a');
00217
if (a!=null)
00218 {
00219
try
00220 {
00221
double value = getTimeOrDouble(a);
00222 t_startTime = value;
00223 }
00224
catch (Exception ex)
00225 {
00226 System.out.println(
"Failed to parse start time (" + a +
"): " + ex);
00227 }
00228 }
00229 }
00230
if (ah.checkFlag(
'z')) {
00231 String a=ah.getOption(
'z');
00232
if (a!=null)
00233 {
00234
try
00235 {
00236
double value = getTimeOrDouble(a);
00237 t_endTime = value;
00238 }
00239
catch (Exception ex)
00240 {
00241 System.out.println(
"Failed to parse end time (" + a +
"): " + ex);
00242 }
00243 }
00244 }
00245
if (ah.checkFlag(
'f')) {
00246 String a=ah.getOption(
'f');
00247
if (a!=null)
00248 {
00249
try
00250 {
00251
double value = Double.parseDouble(a);
00252 t_userGivenFrameRate = value;
00253 }
00254
catch (Exception ex)
00255 {
00256 System.out.println(
"Failed to parse Frame Rate (" + a +
"): " + ex);
00257 }
00258 }
00259 }
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
if (ah.checkFlag(
'i')) {
00276 String a=ah.getOption(
'i');
00277
if (a!=null)
00278 {
00279
try
00280 {
00281
int value = Integer.parseInt(a);
00282 t_itemsToSkip = value;
00283 }
00284
catch (Exception ex)
00285 {
00286 System.out.println(
"Failed to parse Frame Rate (" + a +
"): " + ex);
00287 }
00288 }
00289 }
00290 }
catch (Exception e) {
00291
throw new Exception(
"Arg setting failed: " + e);
00292 }
00293
00294
if ((t_startTime > 0) && (t_endTime > 0))
00295 {
00296 t_duration = t_endTime - t_startTime;
00297
if (t_duration < 0)
00298 {
00299 System.out.println(
"End Time must be after Start Time.");
00300 printUsage();
00301
return false;
00302 }
00303 }
00304
00305 t_server = t_serverName +
":" + t_serverPort;
00306
00307
if (t_startTime < 0)
00308 {
00309 System.out.println(
"Start Time is required.");
00310 printUsage();
00311
return false;
00312 }
00313
00314
if (t_duration < 0 )
00315 {
00316 System.out.println(
"End Time is required.");
00317 printUsage();
00318
return false;
00319 }
00320
00321
if (t_dataSourcePath == null)
00322 {
00323 System.out.println(
"Data Source Path is required.");
00324 printUsage();
00325
return false;
00326 }
00327
00328
if (t_videoSourcePath == null)
00329 {
00330 System.out.println(
"Video Source Path is required.");
00331 printUsage();
00332
return false;
00333 }
00334
00335
if (t_outputURL == null)
00336 {
00337 System.out.println(
"Output File URL is required.");
00338 printUsage();
00339
return false;
00340 }
00341
00342
return true;
00343 }
00344
00345
public static double getTimeOrDouble(String arg)
throws Exception
00346 {
00347
double value = 0.0;
00348
boolean gotit =
false;
00349 String reason = null;
00350
00351
try{
00352 Date d = INPUT_FORMAT.parse(arg);
00353
long t = d.getTime();
00354 value = ((
double)t)/1000.0;
00355 gotit =
true;
00356 }
catch (Exception e1)
00357 {
00358 reason = e1.toString();
00359 gotit =
false;
00360 }
00361
00362
if (!gotit)
00363
try {
00364 value = Double.parseDouble(arg);
00365 gotit =
true;
00366 }
catch (Exception e2)
00367 {
00368 reason = reason +
"; " + e2.toString();
00369 gotit =
false;
00370 }
00371
00372
if (!gotit)
00373
throw(
new Exception(
"Failed to parse time " + arg
00374 +
"; exception:" + reason));
00375
00376
return value;
00377
00378 }
00379
00380
00381
00387 public boolean connect(String server, String sinkName)
00388 {
00389 ChannelMap sMap;
00390
try {
00391
00392 dataSink =
new Sink();
00393 dataSink.OpenRBNBConnection(server,sinkName);
00394
00395 videoSink =
new Sink();
00396 videoSink.OpenRBNBConnection(server,sinkName +
"_video");
00397
00398 connected =
true;
00399 }
catch (SAPIException se) { se.printStackTrace(); }
00400
return connected;
00401 }
00402
00406 public void disconnect()
00407 {
00408 dataSink.CloseRBNBConnection();
00409 videoSink.CloseRBNBConnection();
00410 connected =
false;
00411 }
00417 public void fetchData(String dataSourcePath,
double startTime,
double duration)
00418 {
00419 fetchData(dataSourcePath, startTime, duration,
"absolute");
00420 }
00421
00430 public void fetchData(String dataSourcePath,
00431
double startTime,
double duration, String requestType)
00432 {
00433 dataArray =
new double[0];
00434 timeArray =
new double[0];
00435
00436
if (!connected)
return;
00437
00438
if (DEBUG) System.out.println(
"Attempting fetch (at "
00439 + dataSourcePath +
") with: "
00440 +
"start time = " + startTime +
", "
00441 +
"duration = " + duration +
", "
00442 +
"request type = " + requestType +
". "
00443 );
00444
try {
00445 ChannelMap dataMap =
new ChannelMap();
00446
int dataIndex = dataMap.Add(dataSourcePath);
00447 dataSink.Request(dataMap, startTime, duration, requestType);
00448 ChannelMap res = dataSink.Fetch(-1,dataMap);
00449
if (res != null)
00450 {
00451
if (res.NumberOfChannels() < 1)
00452 {
00453
if (DEBUG) System.out.println(
"no channels returned in fetch");
00454
return;
00455 }
00456 dataArray = res.GetDataAsFloat64(dataIndex);
00457 timeArray = res.GetTimes(dataIndex);
00458 }
00459 }
catch (SAPIException se) {
00460 se.printStackTrace();
00461
return;
00462 }
catch (IllegalStateException se) {
00463
00464 se.printStackTrace();
00465
return;
00466 }
catch (IllegalArgumentException se) {
00467
00468 se.printStackTrace();
00469
return;
00470 }
00471
00472 }
00490 public void makeMovie(
float frameRate,
00491 String oURL, String videoSourcePath,
00492
int dataItemsToSkip)
throws Exception
00493 {
00494
00495 setWidthHeight(videoSourcePath);
00496
00497 PullBufferDataSource ds = (PullBufferDataSource)
00498
new RBNBImageData(
00499 getWidth(), getHeight(), frameRate,
00500 videoSourcePath,dataItemsToSkip);
00501
00502
00503
JpegImagesToMovie imageToMovie =
new JpegImagesToMovie();
00504 MediaLocator oml;
00505
00506
if ((oml = imageToMovie.
createMediaLocator(oURL)) == null) {
00507
throw new Exception(
"Cannot build media locator from: " + oURL);
00508 }
00509
00510 imageToMovie.
makeMovieFromPullBufferDataSource(
00511 getWidth(), getHeight(), frameRate, ds, oml);
00512
00513 }
00514
00519 public int getNumberOfItems() {
00520
return timeArray.length;
00521 }
00522
00527 public double[]
getTimeArray() {
00528
return timeArray;
00529 }
00530
00535 public double[]
getDataArray() {
00536
return dataArray;
00537 }
00538
00539
00543
private int getWidth() {
00544
return videoWidth;
00545 }
00546
00550
private int getHeight() {
00551
return videoHeight;
00552 }
00553
00554
private void setWidth(
int val)
00555 {
00556 videoWidth = val;
00557 }
00558
00559
private void setHeight(
int val)
00560 {
00561 videoHeight = val;
00562 }
00563
00564
private void setWidthHeight(String videoSourcePath)
00565 {
00566
try
00567 {
00568
if (timeArray.length > 0)
00569 {
00570
00571
00572
00573
00574
00575
00576 byte[] image =
getRBNBImage(videoSourcePath, timeArray[0]);
00577 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(
new ByteArrayInputStream(image));
00578 BufferedImage bi;
00579 bi = decoder.decodeAsBufferedImage();
00580 videoWidth = bi.getWidth();
00581 videoHeight = bi.getHeight();
00582 }
00583
else
00584 {
00585 videoWidth = 480;
00586 videoHeight = 320;
00587 }
00588 }
00589
catch (Exception e)
00590 {
00591 System.out.println(
"Possible problem? (setWidthHeight) = " + e);
00592 videoWidth = 480;
00593 videoHeight = 320;
00594 }
00595 }
00596
00607 public byte[]
getRBNBImage(String videoSourcePath,
double videoTime)
00608
throws SAPIException
00609 {
00610
if (DEBUG)
00611 {
00612
long unixTime = (
long)(videoTime * 1000.0);
00613 String time = DATE_FORMAT.format(
new Date(unixTime));
00614 System.out.println(
"Video Source Path = " + videoSourcePath);
00615 System.out.println(
"Request time stamp = " + time);
00616 }
00617 ChannelMap videoMap =
new ChannelMap();
00618
int videoIndex = videoMap.Add(videoSourcePath);
00619 videoSink.Request(videoMap,videoTime-1.0,3.0,
"Absolute");
00620 videoMap = videoSink.Fetch(5000, videoMap);
00621
if (videoMap.GetIfFetchTimedOut())
00622
throw new SAPIException(
"Request on channel (" + videoSourcePath +
") timed out. No Data.");
00623
if ((videoMap.NumberOfChannels() < 1)
00624 || (!videoMap.GetName(0).equals(videoSourcePath)))
00625
throw new SAPIException(
"Requested channel (" + videoSourcePath +
") no data");
00626
if (DEBUG)
00627 {
00628
double[] realTimes = videoMap.GetTimes(0);
00629
double realtime = realTimes[0];
00630
double diff = realtime - videoTime;
00631
long realUnixTime = (
long)(realtime * 1000.0);
00632 String realTimeString = DATE_FORMAT.format(
new Date(realUnixTime));
00633 System.out.println(
"The numbers " + videoTime +
"," + realtime +
"," + diff);
00634 System.out.println(
"Actual time stamp = " + realTimeString);
00635 }
00636
return videoMap.GetDataAsByteArray(0)[0];
00637 }
00638
00644
private class RBNBImageData
extends PullBufferDataSource
00645 {
00646
00647 RBNBImageDataStream streams[];
00648
00649 RBNBImageData(
int width,
int height,
float frameRate,
00650 String videoSourcePath ,
int dataItemsToSkip)
00651 {
00652 streams =
new RBNBImageDataStream[1];
00653 streams[0] =
new RBNBImageDataStream(width, height, frameRate,
00654 videoSourcePath, dataItemsToSkip);
00655 }
00656
00657
public void setLocator(MediaLocator source) {
00658 }
00659
00660
public MediaLocator getLocator() {
00661
return null;
00662 }
00663
00668
public String getContentType() {
00669
return ContentDescriptor.RAW;
00670 }
00671
00672
public void connect() {
00673 }
00674
00675
public void disconnect() {
00676 }
00677
00678
public void start() {
00679 }
00680
00681
public void stop() {
00682 }
00683
00687
public PullBufferStream[] getStreams() {
00688
return streams;
00689 }
00690
00696
public Time getDuration() {
00697
return DURATION_UNKNOWN;
00698 }
00699
00700
public Object[] getControls() {
00701
return new Object[0];
00702 }
00703
00704
public Object getControl(String type) {
00705
return null;
00706 }
00707 }
00708
00709
class RBNBImageDataStream
implements PullBufferStream
00710 {
00711
00712 String videoSourcePath;
00713
int dataItemsToSkip;
00714 VideoFormat format;
00715
00716
int width;
00717
int height;
00718
00719
int nextImage = 0;
00720
boolean ended =
false;
00721
00722
public RBNBImageDataStream(
int width,
int height,
float frameRate,
00723 String videoSourcePath ,
int dataItemsToSkip)
00724 {
00725
00726 format =
new VideoFormat(VideoFormat.JPEG,
00727
new Dimension(getWidth(), getHeight()),
00728 Format.NOT_SPECIFIED,
00729 Format.byteArray,
00730 frameRate);
00731
00732
this.videoSourcePath = videoSourcePath;
00733
this.dataItemsToSkip = dataItemsToSkip;
00734
this.width = width;
00735
this.height = height;
00736 }
00737
00741
public boolean willReadBlock() {
00742
return false;
00743 }
00744
00749
public void read(Buffer buf)
throws IOException {
00750
00751
if (DEBUG) System.out.println(
"processing image " + nextImage);
00752
00753
if (nextImage >= timeArray.length) {
00754
00755 System.err.println(
"Array: Done processing all images.");
00756 buf.setEOM(
true);
00757 buf.setOffset(0);
00758 buf.setLength(0);
00759 ended =
true;
00760
return;
00761 }
00762
00763
00764
00765
00766
00767
00768 byte[] data;
00769
try {
00770
00771 data = getRBNBImage(videoSourcePath, timeArray[nextImage]);
00772 prevImage = data;
00773 }
catch (SAPIException e) {
00774
if (prevImage == null)
00775 {
00776 System.out.println(
"Image fatch failed, skipping: " + e);
00777 data =
new byte[0];
00778 buf.setData(data);
00779 buf.setOffset(0);
00780 buf.setLength(0);
00781 nextImage += dataItemsToSkip + 1;
00782
return;
00783 }
00784 data = prevImage;
00785 }
00786 nextImage += dataItemsToSkip + 1;
00787
00788 buf.setOffset(0);
00789 buf.setLength(data.length);
00790 buf.setFormat(format);
00791 buf.setFlags(buf.getFlags() | buf.FLAG_KEY_FRAME);
00792 buf.setData(data);
00793
00794
00795
00796
00797 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(
new ByteArrayInputStream(data));
00798
00799
00800 BufferedImage bi;
00801 bi = decoder.decodeAsBufferedImage();
00802
if ( height != bi.getHeight() || width != bi.getWidth() ) {
00803 System.out.println(
"Resizing... to "+width+
" x "+height);
00804
00805 Image newimg = bi.getScaledInstance(width, height, 0);
00806
00807 System.out.println(
"Converting back to buffered image...");
00808
00809 BufferedImage dest = null;
00810 dest =
new BufferedImage(width, height, dest.TYPE_INT_RGB);
00811 dest.getGraphics().drawImage((Image) newimg, 0, 0, null);
00812
00813 System.out.println(
"resized height="+dest.getHeight()+
" w="+dest.getWidth());
00814
00815 setHeight(dest.getHeight());
00816 setWidth(dest.getWidth());
00817
00818
00819 ByteArrayOutputStream out =
new ByteArrayOutputStream();
00820 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
00821 System.out.println(
"Encoding resized image");
00822 encoder.encode(dest);
00823 System.out.println(
"Setting image as buffer");
00824
00825 byte[] newdata = out.toByteArray();
00826 buf.setData(newdata);
00827 System.out.println(
"Length="+newdata.length);
00828 buf.setLength(newdata.length);
00829 }
00830
00831 }
00832
00836
public Format getFormat() {
00837
return format;
00838 }
00839
00840
public ContentDescriptor getContentDescriptor() {
00841
return new ContentDescriptor(ContentDescriptor.RAW);
00842 }
00843
00844
public long getContentLength() {
00845
return 0;
00846 }
00847
00848
public boolean endOfStream() {
00849
return ended;
00850 }
00851
00852
public Object[] getControls() {
00853
return new Object[0];
00854 }
00855
00856
public Object getControl(String type) {
00857
return null;
00858 }
00859 }
00860 }
00861