Package artofillusion.image

Source Code of artofillusion.image.MovieEncoder$ImageSourceStream

package artofillusion.image;

import java.io.*;
import java.util.*;
import java.awt.Dimension;
import java.awt.Image;

import javax.media.*;
import javax.media.control.*;
import javax.media.protocol.*;
import javax.media.protocol.DataSource;
import javax.media.datasink.*;
import javax.media.format.VideoFormat;


/**
* This program takes a list of JPEG image files and convert them into
* a QuickTime movie.
*/
public class MovieEncoder implements ControllerListener, DataSinkListener {
 
  public Processor p;
  public DataSink dsink;
  public ImageDataSource ids;
 
 
  public void pushBytes(byte[] b) {
    ids.streams[0].putImageToCache(b);
 
  }
 
 
  public void signalDone() {
    ids.streams[0].signalDone();
    waitForFileDone();
    cleanUp();
 
  }
 
 
  public MovieEncoder(int width, int height, int frameRate, File file) {
   
    try {
      MediaLocator outML = new MediaLocator(file.toURL());   
     
      ids = new ImageDataSource(width, height, frameRate);
      System.err.println("- create processor for the image datasource ..." + file.getName());
      p = Manager.createProcessor(ids);
     
      p.addControllerListener(this);
     
      // Put the Processor into configured state so we can set
      // some processing options on the processor.
      p.configure();
      if (!waitForState(p, p.Configured)) {
     
      }
     
      // Set the output content descriptor to QuickTime.
      p.setContentDescriptor(new ContentDescriptor(FileTypeDescriptor.QUICKTIME));
     
      // Query for the processor for supported formats.
      // Then set it on the processor.
      TrackControl tcs[] = p.getTrackControls();
      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());
      }
     
      tcs[0].setFormat(f[0]);
     
      System.err.println("Setting the track format to: " + f[0]);
     
      // We are done with programming the processor.  Let's just
      // realize it.
      p.realize();
      if (!waitForState(p, p.Realized)) {
       
        throw new RuntimeException(("Failed to realize the processor."));
      }
     
      // Now, we'll need to create a DataSink.
      if ((dsink = createDataSink(p, outML)) == null) {
       
        throw new RuntimeException(("Failed to create a DataSink for the given output MediaLocator: " + outML));
      }
     
      dsink.addDataSinkListener(this);
   
    } catch (Exception e) {
      e.printStackTrace()
    }
 
  } // end constructor.
 
  public boolean doIt() {
   
    // OK, we can now start the actual transcoding.
    try {
      p.start();
      dsink.start();
    } catch (IOException e) {
      System.err.println("IO error during processing");
      return false;
    }
   
    // Wait for EndOfStream event.
    // waitForFileDone();
   
    return true
  }
 
 
  public void cleanUp() {
   
    System.err.println("Closing DataSink...");
   
    try {
      dsink.close();
    } catch (Exception e) {}
    p.removeControllerListener(this);
 
  }
 
 
    /**
     * Create the DataSink.
     */
 
  DataSink createDataSink(Processor p, MediaLocator outML) {
   
    DataSource ds;
   
    if ((ds = p.getDataOutput()) == null) {
      System.err.println("Something is really wrong: the processor does not have an output DataSource");
      return null;
    }
   
    DataSink dsink;
   
    try {
      System.err.println("- create DataSink for: " + outML.getURL() + " " + outML.getProtocol());
      dsink = Manager.createDataSink(ds, outML);
      dsink.open();
    } catch (Exception e) {
      System.err.println("Cannot create the DataSink: ");
      e.printStackTrace();
      return null;
    }
   
    return dsink;
  }
 
 
  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 {
        while (p.getState() < state && stateTransitionOK)
          waitSync.wait();
      } catch (Exception e) {}
    }
    return stateTransitionOK;
  }
 
 
    /**
     * Controller Listener.
     */
  public void controllerUpdate(ControllerEvent 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();
      evt.getSourceController().close();
    }
  }
 
 
  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) {
      synchronized (waitFileSync) {
        fileDone = true;
        waitFileSync.notifyAll();
      }
    } else if (evt instanceof DataSinkErrorEvent) {
      synchronized (waitFileSync) {
        fileDone = true;
        fileSuccess = false;
        waitFileSync.notifyAll();
      }
    }
  }
 
 
  ///////////////////////////////////////////////
  //
  // Inner classes.
  ///////////////////////////////////////////////
 
 
    /**
     * A DataSource to read from a list of JPEG image files and
     * turn that into a stream of JMF buffers.
     * The DataSource is not seekable or positionable.
     */
  public class ImageDataSource extends PullBufferDataSource {
   
    protected ImageSourceStream streams[];
   
    ImageDataSource(int width, int height, int frameRate) {
      streams = new ImageSourceStream[1];
      streams[0] = new ImageSourceStream(width, height, frameRate);
    }
   
    public void setLocator(MediaLocator source) {
    }
   
   
    public MediaLocator getLocator() {
      return null;
    }
   
  /**
   * Content type is of RAW since we are sending buffers of video
   * frames without a container format.
   */
    public String getContentType() {
      return ContentDescriptor.RAW;
    }
   
    public void connect() {
    }
   
    public void disconnect() {
    }
   
    public void start() {
    }
   
    public void stop() {
   
    }
   
  /**
   * Return the ImageSourceStreams.
   */
    public PullBufferStream[] getStreams() {
      return streams;
    }
   
  /**
   * We could have derived the duration from the number of
   * frames and frame rate.  But for the purpose of this program,
   * it's not necessary.
   */
    public Time getDuration() {
      return DURATION_UNKNOWN;
    }
   
    public Object[] getControls() {
      return new Object[0];
    }
   
    public Object getControl(String type) {
      return null;
    }
  }
 
 
    /**
     * The source stream to go along with ImageDataSource.
     */
  class ImageSourceStream implements PullBufferStream {
   
    Vector cache = new Vector();
    int width, height;
    VideoFormat format;
    Object waitLock = new Object();
   
    int nextImage = 0// index of the next image to be read.
    boolean ended = false;
    boolean blocking = false;
   
    public ImageSourceStream(int width, int height, int frameRate) {
     
      this.width = width;
      this.height = height;
     
      format = new VideoFormat(VideoFormat.JPEG,
        new Dimension(width, height),
        Format.NOT_SPECIFIED,
        Format.byteArray,
        (float)frameRate);
    }
   
  /**
   * We block when the cache is empty.
   */
   
    public boolean willReadBlock() {
      return blocking;
    }
   
   
    public void signalDone() {
     
      ended = true;
      synchronized (waitLock) {
        waitLock.notifyAll();
      }
   
    }
   
    public void putImageToCache(byte[] image) {
     
      boolean wasEmpty = cache.isEmpty();
     
      cache.add(image); // Vector is synchronized.
     
      if (wasEmpty) {
        synchronized (waitLock) {
          waitLock.notify();
       
      }
    }
   
   
    private byte[] getImageFromCache() {
     
      if (cache.isEmpty() && !ended) {
        synchronized(waitLock) {
         
          blocking = true;
         
          try {
            waitLock.wait();
           
            if (ended || cache.isEmpty()) {
              return null;
            }
         
          } catch (InterruptedException rupt) {
            rupt.printStackTrace(); // not a common occurrence.
          }
         
        }
      blocking = false;
     
      } else if (ended) { return null; }
     
      return (byte[])cache.remove(0); // Vector is synchronized.
    }
   
  /**
   * This is called from the Processor to read a frame worth
   * of video data.
   */
    public void read(Buffer buf) throws IOException {
     
      byte[] image = getImageFromCache();
     
      if (image == null) {
       
        System.err.println("Done reading all images.");
        buf.setEOM(true);
        buf.setOffset(0);
        buf.setLength(0);
        // ended = true; // must be set by client.
        return;
      }
     
      // Check the input buffer type & size.
     
      /* if (buf.getData() instanceof byte[])
        data = (byte[])buf.getData(); // for future optimisation. */
     
      buf.setData(image);
     
      /* Check to see the given buffer is big enough for the frame.
      if (data == null || data.length < image.length) {
    data = new byte[(int)image.length];
    buf.setData(data); // from Sun; redundant?
      } */
     
     
      buf.setOffset(0);
      buf.setLength(image.length);
      buf.setFormat(format);
      buf.setFlags(buf.getFlags() | buf.FLAG_KEY_FRAME);
   
    }
   
  /**
   * Return the format of each video frame.  That will be JPEG.
   */
    public Format getFormat() {
      return format;
    }
   
    public ContentDescriptor getContentDescriptor() {
      return new ContentDescriptor(ContentDescriptor.RAW);
    }
   
    public long getContentLength() {
      return 0;
    }
   
    public boolean endOfStream() {
      return ended;
    }
   
    public Object[] getControls() {
      return new Object[0];
    }
   
    public Object getControl(String type) {
      return null;
    }
  }
}
TOP

Related Classes of artofillusion.image.MovieEncoder$ImageSourceStream

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.