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

JpegImagesToMovie.java

00001 /* 00002 * Created on May 10, 2004 00003 */ 00004 package org.nees.rbnb; 00005 00006 /* 00007 * Derived from... 00008 * 00009 * @(#)JpegImagesToMovie.java 1.3 01/03/13 00010 * 00011 * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved. 00012 * 00013 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, 00014 * modify and redistribute this software in source and binary code form, 00015 * provided that i) this copyright notice and license appear on all copies of 00016 * the software; and ii) Licensee does not utilize the software in a manner 00017 * which is disparaging to Sun. 00018 * 00019 * This software is provided "AS IS," without a warranty of any kind. ALL 00020 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY 00021 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR 00022 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE 00023 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING 00024 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS 00025 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, 00026 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER 00027 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF 00028 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 00029 * POSSIBILITY OF SUCH DAMAGES. 00030 * 00031 * This software is not designed or intended for use in on-line control of 00032 * aircraft, air traffic, aircraft navigation or aircraft communications; or in 00033 * the design, construction, operation or maintenance of any nuclear 00034 * facility. Licensee represents and warrants that it will not use or 00035 * redistribute the Software for such purposes. 00036 */ 00037 00038 import java.io.*; 00039 import java.util.*; 00040 import java.awt.Dimension; 00041 import java.awt.Image; 00042 import java.awt.image.BufferedImage; 00043 00044 import javax.media.*; 00045 import javax.media.control.*; 00046 import javax.media.protocol.*; 00047 import javax.media.protocol.DataSource; 00048 import javax.media.datasink.*; 00049 import javax.media.format.VideoFormat; 00050 import javax.media.format.AudioFormat; 00051 import javax.imageio.ImageIO; 00052 import com.sun.image.codec.jpeg.*; 00053 00054 import com.sun.media.multiplexer.*; 00055 00060 public class JpegImagesToMovie implements ControllerListener, DataSinkListener { 00061 00062 public boolean doIt(float frameRate, byte[][] images, MediaLocator outML) 00063 { 00064 int width = 480; 00065 int height = 320; 00066 00067 byte[] data = images[0]; 00068 // get the width and height from the first image in the buffer 00069 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(data)); 00070 00071 // decode JPEG image to raw image 00072 BufferedImage bi; 00073 try { 00074 bi = decoder.decodeAsBufferedImage(); 00075 height = bi.getHeight(); 00076 width = bi.getWidth(); 00077 } catch (IOException e){ 00078 System.err.println("Failed to decode input JPEG image, for height and width."); 00079 } 00080 00081 PullBufferDataSource pbds = 00082 (PullBufferDataSource) new ImageDataSourceArray(width, height, frameRate, images); 00083 return makeMovieFromPullBufferDataSource(width, height, frameRate, pbds, outML); 00084 } 00085 00086 public boolean doIt(int width, int height, float frameRate, Vector inFiles, MediaLocator outML) 00087 { 00088 PullBufferDataSource ids = 00089 (PullBufferDataSource) new ImageDataSourceFile(width, height, frameRate, inFiles); 00090 00091 return makeMovieFromPullBufferDataSource(width, height, frameRate, ids, outML); 00092 } 00093 00094 public boolean makeMovieFromPullBufferDataSource( 00095 int width, int height, float frameRate, 00096 PullBufferDataSource ids, MediaLocator outML) 00097 { 00098 Processor p; 00099 00100 try { 00101 System.err.println("- create processor for the image datasource ..."); 00102 p = Manager.createProcessor(ids); 00103 } catch (Exception e) { 00104 System.err.println("Yikes! Cannot create a processor from the data source."); 00105 return false; 00106 } 00107 00108 p.addControllerListener(this); 00109 00110 // Put the Processor into configured state so we can set 00111 // some processing options on the processor. 00112 p.configure(); 00113 if (!waitForState(p, p.Configured)) { 00114 System.err.println("Failed to configure the processor."); 00115 return false; 00116 } 00117 00118 // Set the output content descriptor to QuickTime. 00119 p.setContentDescriptor(new ContentDescriptor(FileTypeDescriptor.QUICKTIME)); 00120 00121 // Query for the processor for supported formats. 00122 // Then set it on the processor. 00123 TrackControl tcs[] = p.getTrackControls(); 00124 Format f[] = tcs[0].getSupportedFormats(); 00125 if (f == null || f.length <= 0) { 00126 System.err.println("The mux does not support the input format: " + tcs[0].getFormat()); 00127 return false; 00128 } 00129 00130 System.out.println("Found count="+f.length); 00131 00132 for ( int i=0; i<f.length;i++ ) System.out.println("Found "+f[i]); 00133 00134 tcs[0].setFormat(f[0]); 00135 00136 System.err.println("Setting the track format to: " + f[0]); 00137 00138 // We are done with programming the processor. Let's just 00139 // realize it. 00140 p.realize(); 00141 if (!waitForState(p, p.Realized)) { 00142 System.err.println("Failed to realize the processor."); 00143 return false; 00144 } 00145 00146 ContentDescriptor[] descriptors = p.getSupportedContentDescriptors(); 00147 for (int n = 0; n < descriptors.length; n++) { 00148 System.out.println("Desc: " + descriptors[n].toString()); 00149 } 00150 00151 DataSource output = p.getDataOutput(); 00152 00153 System.out.println("DataSource type: "); 00154 Class cls = output.getClass(); 00155 while (cls != null) { 00156 System.out.println(cls.toString()); 00157 cls = cls.getSuperclass(); 00158 } 00159 00160 // Now, we'll need to create a DataSink. 00161 DataSink dsink; 00162 if ((dsink = createDataSink(p, outML)) == null) { 00163 System.err.println("Failed to create a DataSink for the given output MediaLocator: " + outML); 00164 return false; 00165 } 00166 00167 dsink.addDataSinkListener(this); 00168 00169 fileDone = false; 00170 00171 System.err.println("start processing..."); 00172 00173 // OK, we can now start the actual transcoding. 00174 try { 00175 p.start(); 00176 dsink.start(); 00177 } catch (Exception e) { 00178 System.err.println("IO error during processing"); 00179 return false; 00180 } 00181 00182 // Wait for EndOfStream event. 00183 waitForFileDone(); 00184 00185 // Cleanup. 00186 try { 00187 dsink.close(); 00188 } catch (Exception e) {} 00189 p.removeControllerListener(this); 00190 00191 System.err.println("...done processing."); 00192 00193 return true; 00194 } 00195 00199 DataSink createDataSink(Processor p, MediaLocator outML) { 00200 00201 DataSource ds; 00202 00203 if ((ds = p.getDataOutput()) == null) { 00204 System.err.println("Something is really wrong: the processor does not have an output DataSource"); 00205 return null; 00206 } 00207 00208 DataSink dsink; 00209 00210 try { 00211 System.err.println("- create DataSink for: " + outML); 00212 dsink = Manager.createDataSink(ds, outML); 00213 dsink.open(); 00214 } catch (Exception e) { 00215 System.err.println("Cannot create the DataSink: " + e); 00216 return null; 00217 } 00218 00219 return dsink; 00220 } 00221 00222 00223 Object waitSync = new Object(); 00224 boolean stateTransitionOK = true; 00225 00230 boolean waitForState(Processor p, int state) { 00231 synchronized (waitSync) { 00232 try { 00233 while (p.getState() < state && stateTransitionOK) 00234 waitSync.wait(); 00235 } catch (Exception e) {} 00236 } 00237 return stateTransitionOK; 00238 } 00239 00243 public void controllerUpdate(ControllerEvent evt) { 00244 00245 if (evt instanceof ConfigureCompleteEvent || 00246 evt instanceof RealizeCompleteEvent || 00247 evt instanceof PrefetchCompleteEvent) { 00248 synchronized (waitSync) { 00249 stateTransitionOK = true; 00250 waitSync.notifyAll(); 00251 } 00252 } else if (evt instanceof ResourceUnavailableEvent) { 00253 synchronized (waitSync) { 00254 stateTransitionOK = false; 00255 waitSync.notifyAll(); 00256 } 00257 } else if (evt instanceof EndOfMediaEvent) { 00258 evt.getSourceController().stop(); 00259 evt.getSourceController().close(); 00260 } 00261 } 00262 00263 00264 Object waitFileSync = new Object(); 00265 boolean fileDone = false; 00266 boolean fileSuccess = true; 00267 00271 boolean waitForFileDone() { 00272 synchronized (waitFileSync) { 00273 try { 00274 while (!fileDone) 00275 waitFileSync.wait(); 00276 } catch (Exception e) {} 00277 } 00278 return fileSuccess; 00279 } 00280 00281 00285 public void dataSinkUpdate(DataSinkEvent evt) { 00286 00287 if (evt instanceof EndOfStreamEvent) { 00288 synchronized (waitFileSync) { 00289 fileDone = true; 00290 waitFileSync.notifyAll(); 00291 } 00292 } else if (evt instanceof DataSinkErrorEvent) { 00293 synchronized (waitFileSync) { 00294 fileDone = true; 00295 fileSuccess = false; 00296 waitFileSync.notifyAll(); 00297 } 00298 } 00299 } 00300 00301 00302 public static void main(String args[]) { 00303 00304 if (args.length == 0) 00305 prUsage(); 00306 00307 // Parse the arguments. 00308 int i = 0; 00309 int width = -1, height = -1; 00310 float frameRate = (float)1.0; 00311 Vector inputFiles = new Vector(); 00312 String outputURL = null; 00313 00314 while (i < args.length) { 00315 00316 if (args[i].equals("-w")) { 00317 i++; 00318 if (i >= args.length) 00319 prUsage(); 00320 width = new Integer(args[i]).intValue(); 00321 } else if (args[i].equals("-h")) { 00322 i++; 00323 if (i >= args.length) 00324 prUsage(); 00325 height = new Integer(args[i]).intValue(); 00326 } else if (args[i].equals("-f")) { 00327 i++; 00328 if (i >= args.length) 00329 prUsage(); 00330 00331 frameRate = Float.parseFloat(args[i]); 00332 } else if (args[i].equals("-o")) { 00333 i++; 00334 if (i >= args.length) 00335 prUsage(); 00336 outputURL = args[i]; 00337 } else { 00338 inputFiles.addElement(args[i]); 00339 } 00340 i++; 00341 } 00342 00343 if (outputURL == null || inputFiles.size() == 0) 00344 prUsage(); 00345 00346 // Check for output file extension. 00347 if (!outputURL.endsWith(".mov") && !outputURL.endsWith(".MOV")) { 00348 System.err.println("The output file extension should end with a .mov extension"); 00349 prUsage(); 00350 } 00351 00352 if (width < 0 || height < 0) { 00353 System.err.println("Please specify the correct image size."); 00354 prUsage(); 00355 } 00356 00357 // Check the frame rate. 00358 if (frameRate < (float)1.0) 00359 frameRate = (float)1.0; 00360 00361 // Generate the output media locators. 00362 MediaLocator oml; 00363 00364 if ((oml = createMediaLocator(outputURL)) == null) { 00365 System.err.println("Cannot build media locator from: " + outputURL); 00366 System.exit(0); 00367 } 00368 00369 JpegImagesToMovie imageToMovie = new JpegImagesToMovie(); 00370 imageToMovie.doIt(width, height, frameRate, inputFiles, oml); 00371 00372 System.exit(0); 00373 } 00374 00375 static void prUsage() { 00376 System.err.println("Usage: java JpegImagesToMovie -w <width> -h <height> -f <frame rate> -o <output URL> <input JPEG file 1> <input JPEG file 2> ..."); 00377 System.exit(-1); 00378 } 00379 00383 static MediaLocator createMediaLocator(String url) { 00384 00385 MediaLocator ml; 00386 00387 if (url.indexOf(":") > 0 && (ml = new MediaLocator(url)) != null) 00388 return ml; 00389 00390 if (url.startsWith(File.separator)) { 00391 if ((ml = new MediaLocator("file:" + url)) != null) 00392 return ml; 00393 } else { 00394 String file = "file:" + System.getProperty("user.dir") + File.separator + url; 00395 if ((ml = new MediaLocator(file)) != null) 00396 return ml; 00397 } 00398 00399 return null; 00400 } 00401 00402 00404 // 00405 // Inner classes. 00407 00413 class ImageDataSourceArray extends PullBufferDataSource 00414 { 00415 00416 ImageDataSourceArrayStream streams[]; 00417 00418 ImageDataSourceArray(int width, int height, float frameRate, byte[][] images) { 00419 streams = new ImageDataSourceArrayStream[1]; 00420 streams[0] = new ImageDataSourceArrayStream(width, height, frameRate, images); 00421 } 00422 00423 public void setLocator(MediaLocator source) { 00424 } 00425 00426 public MediaLocator getLocator() { 00427 return null; 00428 } 00429 00434 public String getContentType() { 00435 return ContentDescriptor.RAW; 00436 } 00437 00438 public void connect() { 00439 } 00440 00441 public void disconnect() { 00442 } 00443 00444 public void start() { 00445 } 00446 00447 public void stop() { 00448 } 00449 00453 public PullBufferStream[] getStreams() { 00454 return streams; 00455 } 00456 00462 public Time getDuration() { 00463 return DURATION_UNKNOWN; 00464 } 00465 00466 public Object[] getControls() { 00467 return new Object[0]; 00468 } 00469 00470 public Object getControl(String type) { 00471 return null; 00472 } 00473 } 00474 00475 class ImageDataSourceArrayStream implements PullBufferStream 00476 { 00477 00478 byte[][] images; 00479 int width, height; 00480 VideoFormat format; 00481 00482 int nextImage = 0; // index of the next image to be read. 00483 boolean ended = false; 00484 00485 public ImageDataSourceArrayStream(int width, int height, float frameRate, byte[][] images) { 00486 this.width = width; 00487 this.height = height; 00488 this.images = images; 00489 00490 format = new VideoFormat(VideoFormat.JPEG, 00491 new Dimension(width, height), 00492 Format.NOT_SPECIFIED, 00493 Format.byteArray, 00494 frameRate); 00495 } 00496 00500 public boolean willReadBlock() { 00501 return false; 00502 } 00503 00508 public void read(Buffer buf) throws IOException { 00509 00510 // Check if we've finished all the frames. 00511 if (nextImage >= images.length) { 00512 // We are done. Set EndOfMedia. 00513 System.err.println("Array: Done processing all images."); 00514 buf.setEOM(true); 00515 buf.setOffset(0); 00516 buf.setLength(0); 00517 ended = true; 00518 return; 00519 } 00520 00521 byte[] data = images[nextImage]; 00522 nextImage++; 00523 00524 buf.setOffset(0); 00525 buf.setLength(data.length); 00526 buf.setFormat(format); 00527 buf.setFlags(buf.getFlags() | buf.FLAG_KEY_FRAME); 00528 buf.setData(data); 00529 00530 // Check the height and width of the read image to make sure that it is what 00531 // is expected - if not resize and update buffer 00532 00533 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(data)); 00534 00535 // decode JPEG image to raw image 00536 BufferedImage bi; 00537 try { 00538 bi = decoder.decodeAsBufferedImage(); 00539 System.out.println("h="+bi.getHeight()+" w="+bi.getWidth()); 00540 if ( height != bi.getHeight() || width != bi.getWidth() ) { 00541 System.out.println("Resizing... to "+width+" x "+height); 00542 00543 Image newimg = bi.getScaledInstance(width, height, 0); 00544 00545 System.out.println("Converting back to buffered image..."); 00546 00547 BufferedImage dest = null; 00548 dest = new BufferedImage(width, height, dest.TYPE_INT_RGB); 00549 dest.getGraphics().drawImage((Image) newimg, 0, 0, null); 00550 00551 System.out.println("resized height="+dest.getHeight()+" w="+dest.getWidth()); 00552 00553 // encode scaled image as JPEG image 00554 ByteArrayOutputStream out = new ByteArrayOutputStream(); 00555 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); 00556 try { 00557 System.out.println("Encoding resized image"); 00558 encoder.encode(dest); 00559 System.out.println("Setting image as buffer"); 00560 // get JPEG image data as byte array 00561 byte[] newdata = out.toByteArray(); 00562 buf.setData(newdata); 00563 System.out.println("Length="+newdata.length); 00564 buf.setLength(newdata.length); 00565 } catch (IOException e) { 00566 System.err.println("Failed to encode output JPEG image, skipping."); 00567 } 00568 } // End if must resize 00569 00570 } catch (IOException e){ 00571 System.err.println("Failed to decode input JPEG image, skipping."); 00572 // Leave buffer data in place 00573 } 00574 00575 } 00576 00580 public Format getFormat() { 00581 return format; 00582 } 00583 00584 public ContentDescriptor getContentDescriptor() { 00585 return new ContentDescriptor(ContentDescriptor.RAW); 00586 } 00587 00588 public long getContentLength() { 00589 return 0; 00590 } 00591 00592 public boolean endOfStream() { 00593 return ended; 00594 } 00595 00596 public Object[] getControls() { 00597 return new Object[0]; 00598 } 00599 00600 public Object getControl(String type) { 00601 return null; 00602 } 00603 } 00604 00605 00611 class ImageDataSourceFile extends PullBufferDataSource { 00612 00613 ImageSourceStream streams[]; 00614 00615 ImageDataSourceFile(int width, int height, float frameRate, Vector images) { 00616 streams = new ImageSourceStream[1]; 00617 streams[0] = new ImageSourceStream(width, height, frameRate, images); 00618 } 00619 00620 public void setLocator(MediaLocator source) { 00621 } 00622 00623 public MediaLocator getLocator() { 00624 return null; 00625 } 00626 00631 public String getContentType() { 00632 return ContentDescriptor.RAW; 00633 } 00634 00635 public void connect() { 00636 } 00637 00638 public void disconnect() { 00639 } 00640 00641 public void start() { 00642 } 00643 00644 public void stop() { 00645 } 00646 00650 public PullBufferStream[] getStreams() { 00651 return streams; 00652 } 00653 00659 public Time getDuration() { 00660 return DURATION_UNKNOWN; 00661 } 00662 00663 public Object[] getControls() { 00664 return new Object[0]; 00665 } 00666 00667 public Object getControl(String type) { 00668 return null; 00669 } 00670 } 00671 00675 class ImageSourceStream implements PullBufferStream { 00676 00677 Vector images; 00678 int width, height; 00679 VideoFormat format; 00680 00681 int nextImage = 0; // index of the next image to be read. 00682 boolean ended = false; 00683 00684 public ImageSourceStream(int width, int height, float frameRate, Vector images) { 00685 this.width = width; 00686 this.height = height; 00687 this.images = images; 00688 00689 format = new VideoFormat(VideoFormat.JPEG, 00690 new Dimension(width, height), 00691 Format.NOT_SPECIFIED, 00692 Format.byteArray, 00693 frameRate); 00694 } 00695 00699 public boolean willReadBlock() { 00700 return false; 00701 } 00702 00707 public void read(Buffer buf) throws IOException { 00708 00709 // Check if we've finished all the frames. 00710 if (nextImage >= images.size()) { 00711 // We are done. Set EndOfMedia. 00712 System.err.println("Done reading all images."); 00713 buf.setEOM(true); 00714 buf.setOffset(0); 00715 buf.setLength(0); 00716 ended = true; 00717 return; 00718 } 00719 00720 String imageFile = (String)images.elementAt(nextImage); 00721 nextImage++; 00722 00723 System.err.println(" - reading image file: " + imageFile); 00724 00725 // Open a random access file for the next image. 00726 RandomAccessFile raFile; 00727 raFile = new RandomAccessFile(imageFile, "r"); 00728 00729 byte data[] = null; 00730 00731 // Check the input buffer type & size. 00732 00733 // if (buf.getData() instanceof byte[]) 00734 // data = (byte[])buf.getData(); 00735 00736 // Check to see the given buffer is big enough for the frame. 00737 if (data == null || data.length < raFile.length()) { 00738 data = new byte[(int)raFile.length()]; 00739 } 00740 00741 // Read the entire JPEG image from the file. 00742 raFile.readFully(data, 0, (int)raFile.length()); 00743 00744 System.err.println(" read " + raFile.length() + " bytes."); 00745 00746 buf.setOffset(0); 00747 buf.setLength((int)raFile.length()); 00748 buf.setFormat(format); 00749 buf.setFlags(buf.getFlags() | buf.FLAG_KEY_FRAME); 00750 buf.setData(data); 00751 00752 // Check the height and width of the read image to make sure that it is what 00753 // is expected - if not resize and update buffer 00754 00755 JPEGImageDecoder decoder = JPEGCodec.createJPEGDecoder(new ByteArrayInputStream(data)); 00756 00757 // decode JPEG image to raw image 00758 BufferedImage bi; 00759 try { 00760 bi = decoder.decodeAsBufferedImage(); 00761 System.out.println("h="+bi.getHeight()+" w="+bi.getWidth()); 00762 if ( height != bi.getHeight() || width != bi.getWidth() ) { 00763 System.out.println("Resizing... to "+width+" x "+height); 00764 00765 Image newimg = bi.getScaledInstance(width, height, 0); 00766 00767 System.out.println("Converting back to buffered image..."); 00768 00769 BufferedImage dest = null; 00770 dest = new BufferedImage(width, height, dest.TYPE_INT_RGB); 00771 dest.getGraphics().drawImage((Image) newimg, 0, 0, null); 00772 00773 System.out.println("resized height="+dest.getHeight()+" w="+dest.getWidth()); 00774 00775 // encode scaled image as JPEG image 00776 ByteArrayOutputStream out = new ByteArrayOutputStream(); 00777 JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out); 00778 try { 00779 System.out.println("Encoding resized image"); 00780 encoder.encode(dest); 00781 System.out.println("Setting image as buffer"); 00782 // get JPEG image data as byte array 00783 byte[] newdata = out.toByteArray(); 00784 buf.setData(newdata); 00785 System.out.println("Length="+newdata.length); 00786 buf.setLength(newdata.length); 00787 } catch (IOException e) { 00788 System.err.println("Failed to encode output JPEG image, skipping."); 00789 } 00790 } // End if must resize 00791 00792 } catch (IOException e){ 00793 System.err.println("Failed to decode input JPEG image, skipping."); 00794 // Leave buffer data in place 00795 } 00796 00797 // Close the random access file. 00798 raFile.close(); 00799 } 00800 00804 public Format getFormat() { 00805 return format; 00806 } 00807 00808 public ContentDescriptor getContentDescriptor() { 00809 return new ContentDescriptor(ContentDescriptor.RAW); 00810 } 00811 00812 public long getContentLength() { 00813 return 0; 00814 } 00815 00816 public boolean endOfStream() { 00817 return ended; 00818 } 00819 00820 public Object[] getControls() { 00821 return new Object[0]; 00822 } 00823 00824 public Object getControl(String type) { 00825 return null; 00826 } 00827 } // 00828 00829 }

Generated on Tue Aug 24 11:12:25 2004 for Data turbine for NEESGrid by doxygen 1.3.7