📄 movieencoder.java
字号:
package sim.util.media;import javax.media.*;import javax.media.util.*;import javax.media.protocol.*;import javax.media.datasink.*;import javax.media.control.*;import java.io.*;import java.awt.image.*;import java.awt.*;/* * MovieEncoder * Sean Luke * With help from Sun (see license at end) * * This file contains two subsidiary classes: MovieEncoderDataStream and MovieEncoderDataSource. * These are the custom classes which provide the underlying JMF system with images in the * form of buffers to convert to Quicktime. With the license at the end of the file is a * URL which points to a (slightly broken) example file Sun provided to give some idea on how * to do this. Basically, JMF is tough stuff to write! * *//** <p>Usage of this class depends on the existence of the Java Media Framework (JMF) * which can be acquired from javasoft.com. The class was tested on JMF 2.1.1. * * <p>This class encodes BufferedImages into quicktime movies. It has three main * functions: * * <p>First, you call getEncodingFormats(), which returns an array of available formats. * You specify a frame rate and a prototypical image. * * <p>Then, you call the constructor -- specify the frame rate, file, an encoding format * chosen from the array in getEncodingFormats, and again provide a prototypical image. * You'll want to ensure that future images encoded into the MovieEncoder will have * the same size and format as the prototype image. If not, they'll be converted * and cropped accordingly, which will take more time. You should expect the constructor * to take a few seconds to start up -- JMF is not speedy. * * <p>Then you drop images into the MovieEncoder to add to the Quicktime movie * as frames, using the add(BufferedImage) method. If there was an error and the movie * could not be written, this method (and all subsequent add(...) calls) will return false. * * <p>When you're all finished, you call the stop() method to clean up and complete * writing the file. * */public class MovieEncoder implements DataSinkListener, ControllerListener, java.io.Serializable { boolean started; // are we running? boolean stopped; // are we finished? int width; // width of first frame -- guides width of all other frames int height; // height of first rame -- guides height of all other frames int type; // type of first image -- guides type of all other images float frameRate; // desired frame rate Processor processor; // the JMF processor MovieEncoderDataSource source; // Our buffer data source DataSink sink; // Our buffer data sink. Defined by the file File file; // URL pointing to the file. Dumb that JMF can't write to a stream. Format encodeFormat; // format to encode in static { // We're going to hack com.sun.media.Log so that it doesn't // create the jmf.log poops. These poops are made by Log.static{} // when the Log class is loaded. Here's the trick. // The method first checks for the current security by calling // com.sun.media.JMFSecurityManager.getJMFSecurity(), which // just returns a single object created at static initialize time. // We do that here by calling the method ourselves. com.sun.media.JMFSecurityManager.getJMFSecurity(); // That'll do the job. Now the next thing the Log.static{} // does is create the filename by calling System.getProperty("user.dir"), // and tacking on a file separator and the infamous "jmf.log" string // after that. It then makes the file and more or less exits. // We'll mess things up by deleting the System's properties // temporarily. java.util.Properties p = System.getProperties(); System.setProperties(new java.util.Properties()); // now we call something which causes Log.static{} to load. try { com.sun.media.Log.getIndent(); } catch (Exception e) { } // restore the properties and tell the Log never to try to write to the file. System.setProperties(p); com.sun.media.Log.isEnabled = false; } /** Returns null and prints an error out to stderr if an error occurred while trying to get the formats */ public static Format[] getEncodingFormats(float fps, BufferedImage typicalImage) { return new MovieEncoder().getEncodingFormatsHelper(fps,typicalImage); } private Format[] getEncodingFormatsHelper(float fps, BufferedImage typicalImage) { try { // get possible formats for encoding Format format = (Format)(ImageToBuffer.createBuffer(typicalImage,fps).getFormat()); MovieEncoderDataSource source = new MovieEncoderDataSource(format, fps); Processor processor = Manager.createProcessor(source); processor.addControllerListener(this); processor.configure(); // while necessary for encoding video, // this may not be necessary for simply getting formats // however, better safe than sorry.. so here goes: if (!waitForState(processor, Processor.Configured)) throw new RuntimeException("Failed to configure processor"); processor.setContentDescriptor(new ContentDescriptor(FileTypeDescriptor.QUICKTIME)); TrackControl tcs[] = processor.getTrackControls(); // and finally... Format f[] = tcs[0].getSupportedFormats(); if (f == null || f.length <= 0) throw new RuntimeException("The mux does not support the input format: " + tcs[0].getFormat()); processor.removeControllerListener(this); return f; } catch (Exception e) { e.printStackTrace(); processor.removeControllerListener(this); return null; } } // private empty constructor MovieEncoder() {}; /** Creates an object which will write out a move of the specified format, and written to the provided file. */ public MovieEncoder(float frameRate, File file, BufferedImage typicalImage, Format encodeFormat) { this.frameRate = frameRate; this.file = file; this.encodeFormat = encodeFormat; try { setup(typicalImage); started = true; } catch (Exception e) { e.printStackTrace(); stopped = true; } } final Object waitSync = new Object(); boolean stateTransitionOK = true; /** * Block until the processor has transitioned to the given state. * Return false if the transition failed. */ boolean waitForState(Processor p, int state) { synchronized (waitSync) { try { // System.out.println(p.getState()); while (p.getState() < state && stateTransitionOK) waitSync.wait(); } catch (Exception e) {} } return stateTransitionOK; } /** * Controller Listener. */ public void controllerUpdate(ControllerEvent evt) { // System.out.println(evt); if (evt instanceof ConfigureCompleteEvent || evt instanceof RealizeCompleteEvent || evt instanceof PrefetchCompleteEvent) { synchronized (waitSync) { stateTransitionOK = true; waitSync.notifyAll(); } } else if (evt instanceof ResourceUnavailableEvent) { synchronized (waitSync) { stateTransitionOK = false; waitSync.notifyAll(); } } else if (evt instanceof EndOfMediaEvent) { evt.getSourceController().stop(); // no need to check for null stopper, as null is not an instance of any class // sometimes Java gives us an error on this one. So we need to wrap it // and hope for the best -- Sean try { evt.getSourceController().close(); } catch (Exception e) { System.err.println("Spurious Sun JMF Error?\n\n"); e.printStackTrace(); // system sometimes gives no further event updates, so the waiter hangs, // and I'm not sure why -- so here we make it fail. I wonder if this will work -- Sean synchronized (waitSync) { stateTransitionOK = false; waitSync.notifyAll(); } synchronized (waitFileSync) { fileSuccess = false; fileDone = true; waitFileSync.notifyAll(); } // end weird mods } } } final Object waitFileSync = new Object(); boolean fileDone = false; boolean fileSuccess = true; /** * Block until file writing is done. */ boolean waitForFileDone() { synchronized (waitFileSync) { try { while (!fileDone) waitFileSync.wait(); } catch (Exception e) {} } return fileSuccess; } /** * Event handler for the file writer. */ public void dataSinkUpdate(DataSinkEvent evt) { if (evt instanceof EndOfStreamEvent) { //System.err.println(evt); synchronized (waitFileSync) { fileDone = true; waitFileSync.notifyAll(); } } else if (evt instanceof DataSinkErrorEvent) { //System.err.println(evt); synchronized (waitFileSync) { fileDone = true; fileSuccess = false; waitFileSync.notifyAll(); } } } void setup(BufferedImage i) throws IOException, NoDataSinkException, NoProcessorException, CannotRealizeException, RuntimeException { width = i.getWidth(); height = i.getHeight(); type = i.getType(); // i think my hacking begins here (dan) // get formats with current framerate, which should not matter Format format = (Format)(ImageToBuffer.createBuffer(i,frameRate).getFormat());
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -