View Single Post
Old Nov 28th, 2007, 11:12 AM   #11
Harakim
Hobbyist Programmer
 
Join Date: May 2006
Location: West Jordan, Utah, United States
Posts: 176
Rep Power: 3 Harakim is on a distinguished road
Re: convert image sequence to quicktime movie

EDIT: I have read that people have trouble saving in a directory other than the one the program runs in, though I have been able to.

Here is my session:
Quote:
Z:\More of my junk\jpegs to qt>dir
Volume in drive Z is ??
Volume Serial Number is 8317-027E

Directory of Z:\More of my junk\jpegs to qt

11/28/2007 10:01 AM <DIR> .
11/28/2007 09:54 AM <DIR> ..
11/28/2007 09:38 AM 11,878 JpegImagesToMovie.java
11/28/2007 09:38 AM 46,147 Red_line5.jpeg.jpg
11/28/2007 09:56 AM 46,749 movie.mov
05/01/2003 01:54 PM 1,904,171 jmf.jar
4 File(s) 2,008,945 bytes
2 Dir(s) 11,190,403,072 bytes free

Z:\More of my junk\jpegs to qt>javac -cp .;jmf.jar JpegImagesToMovie.java
Note: JpegImagesToMovie.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.

Z:\More of my junk\jpegs to qt>java -cp .;jmf.jar JpegImagesToMovie -h 480 -w 640 -f 1 -o movie.mov Red_line5.jpeg.jpg

Z:\More of my junk\jpegs to qt>dir
Volume in drive Z is ??
Volume Serial Number is 8317-027E

Directory of Z:\More of my junk\jpegs to qt

11/28/2007 10:01 AM <DIR> .
11/28/2007 09:54 AM <DIR> ..
11/28/2007 10:01 AM 2,607 JpegImagesToMovie$ImageSourceStream.class
11/28/2007 09:38 AM 11,878 JpegImagesToMovie.java
11/28/2007 10:01 AM 6,047 JpegImagesToMovie.class
11/28/2007 09:38 AM 46,147 Red_line5.jpeg.jpg
11/28/2007 10:01 AM 1,488 JpegImagesToMovie$ImageDataSource.class
11/28/2007 10:01 AM 46,749 movie.mov
05/01/2003 01:54 PM 1,904,171 jmf.jar
7 File(s) 2,019,087 bytes
2 Dir(s) 11,190,403,072 bytes free
My friend RB Whitaker modified the original example. This is the code I used:
(I used JMF2.1.1e)
/*
 * @(#)JpegImagesToMovie.java	1.3 01/03/13
 *
 * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
import java.awt.Dimension;

import javax.media.*;
import javax.media.control.*;
import javax.media.protocol.*;
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 JpegImagesToMovie implements ControllerListener, DataSinkListener
{
	public boolean doIt(int width, int height, int frameRate, Vector inFiles, MediaLocator outML)
	{
		ImageDataSource ids = new ImageDataSource(width, height, frameRate, inFiles);

		Processor p;

		try
		{
			p = Manager.createProcessor(ids);
		} catch (Exception e)
		{
			return false;
		}

		p.addControllerListener(this);

		// Put the Processor into configured state so we can set
		// some processing options on the processor.
		p.configure();
		if (!waitForState(p, Processor.Configured))
		{
			return false;
		}

		// 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)
		{
			return false;
		}

		tcs[0].setFormat(f[0]);

		// We are done with programming the processor.  Let's just
		// realize it.
		p.realize();
		if (!waitForState(p, Processor.Realized))
		{
			return false;
		}

		// Now, we'll need to create a DataSink.
		DataSink dsink;
		if ((dsink = createDataSink(p, outML)) == null)
		{
			return false;
		}

		dsink.addDataSinkListener(this);
		fileDone = false;

		// OK, we can now start the actual transcoding.
		try
		{
			p.start();
			dsink.start();
		}
		catch (IOException e)
		{
			return false;
		}

		// Wait for EndOfStream event.
		waitForFileDone();

		// Cleanup.
		try
		{
			dsink.close();
		}
		catch (Exception e)
		{
		}

		p.removeControllerListener(this);

		return true;
	}


	/**
	 * Create the DataSink.
	 */
	DataSink createDataSink(Processor p, MediaLocator outML)
	{
		DataSource ds;

		if ((ds = p.getDataOutput()) == null)
		{
			return null;
		}

		DataSink dsink;

		try
		{
			dsink = Manager.createDataSink(ds, outML);
			dsink.open();
		}
		catch (Exception e)
		{
			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)
		{
			if(evt.getSourceController() != null)
			{
				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();
			}
		}
	}


	public static void main(String args[])
	{
		if (args.length == 0)
			prUsage();

		// Parse the arguments.
		int i = 0;
		int width = -1, height = -1, frameRate = 1;
		Vector inputFiles = new Vector();
		String outputURL = null;

		while (i < args.length) {

			if (args[i].equals("-w"))
			{
				i++;
				if (i >= args.length)
					prUsage();
				width = new Integer(args[i]).intValue();
			}
			else if (args[i].equals("-h"))
			{
				i++;
				if (i >= args.length)
					prUsage();
				height = new Integer(args[i]).intValue();
			}
			else if (args[i].equals("-f"))
			{
				i++;
				if (i >= args.length)
					prUsage();
				frameRate = new Integer(args[i]).intValue();
			}
			else if (args[i].equals("-o"))
			{
				i++;
				if (i >= args.length)
					prUsage();
				outputURL = args[i];
			}
			else
			{
				inputFiles.addElement(args[i]);
			}

			i++;
		}

		if (outputURL == null || inputFiles.size() == 0)
			prUsage();


		// Check the frame rate.
		if (frameRate < 1)
			frameRate = 1;

		// Generate the output media locators.
		MediaLocator oml;

		if ((oml = createMediaLocator(outputURL)) == null)
		{
			return;
		}

		JpegImagesToMovie imageToMovie = new JpegImagesToMovie();
		imageToMovie.doIt(width, height, frameRate, inputFiles, oml);
	}

	static void prUsage()
	{
		System.err.println("Usage: java JpegImagesToMovie -w <width> -h <height> -f <frame rate> -o <output URL> <input JPEG file 1> <input JPEG file 2> ...");
		return;
	}

	private static MediaLocator createMediaLocator(String url)
	{
		try
		{
			return new MediaLocator(new URL("file:" + url));
		}
		catch (MalformedURLException e)
		{
			e.printStackTrace();
			return null;
		}
	}

	/**
	 * 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.
	 */
	class ImageDataSource extends PullBufferDataSource
	{

		ImageSourceStream streams[];

		ImageDataSource(int width, int height, int frameRate, Vector images)
		{
			streams = new ImageSourceStream[1];
			streams[0] = new ImageSourceStream(width, height, frameRate, images);
		}

		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 images;
		int width, height;
		VideoFormat format;

		int nextImage = 0;	// index of the next image to be read.
		boolean ended = false;

		public ImageSourceStream(int width, int height, int frameRate, Vector images)
		{
			this.width = width;
			this.height = height;
			this.images = images;

			format = new VideoFormat(VideoFormat.JPEG,
					new Dimension(width, height),
					Format.NOT_SPECIFIED,
					Format.byteArray,
					(float)frameRate);
		}

		/**
		 * We should never need to block assuming data are read from files.
		 */
		public boolean willReadBlock()
		{
			return false;
		}

		/**
		 * This is called from the Processor to read a frame worth
		 * of video data.
		 */
		public void read(Buffer buf) throws IOException
		{

			// Check if we've finished all the frames.
			if (nextImage >= images.size()) {
				// We are done.  Set EndOfMedia.
				buf.setEOM(true);
				buf.setOffset(0);
				buf.setLength(0);
				ended = true;
				return;
			}

			String imageFile = (String)images.elementAt(nextImage);
			nextImage++;

			// Open a random access file for the next image.
			RandomAccessFile raFile;
			raFile = new RandomAccessFile(imageFile, "r");

			byte data[] = null;

			// Check the input buffer type & size.

			if (buf.getData() instanceof byte[])
				data = (byte[])buf.getData();

			// Check to see the given buffer is big enough for the frame.
			if (data == null || data.length < raFile.length()) {
				data = new byte[(int)raFile.length()];
				buf.setData(data);
			}

			// Read the entire JPEG image from the file.
			raFile.readFully(data, 0, (int)raFile.length());

			buf.setOffset(0);
			buf.setLength((int)raFile.length());
			buf.setFormat(format);
			buf.setFlags(buf.getFlags() | Buffer.FLAG_KEY_FRAME);

			// Close the random access file.
			raFile.close();
		}

		/**
		 * 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;
		}
	}
}

Last edited by Harakim; Nov 28th, 2007 at 11:24 AM.
Harakim is offline   Reply With Quote