I recently wrote a small sample sample program which takes a
Quicktime movie
and uses OpenGL to render it. The code is based on examples that have
been posted in this forum, and else where. You can find the project
here:
/**
* Class to play a Quicktime movie and to allow to get each frame.
*
* Heavily based on th QTSnapper source code, from the "Playing Movies
* in a Java 3D World, Part 2" article at O'Reilly:
*
* http://www.onjava.com/pub/a/onjava/2005/06/01/kgpjava_part2.html
*
* @author Andre-John Mas
*
*/
public class QTMoviePlayer {
private boolean isSessionOpen = false;
private OpenMovieFile movieFile;
private Movie movie;
private Track videoTrack;
private Media vidMedia;
private MediaSample mediaSample; // current sample/frame
private int rowInts; // no. of ints in each row of a frame
private int numSamples; // number of samples in the movie
public QTMoviePlayer(String fileName) throws IOException {
File file = new File(fileName);
if (!file.exists()) {
throw new FileNotFoundException("could not find " + fileName);
}
try {
// open a QuickTime session
QTSession.open();
isSessionOpen = true;
// open the movie
movieFile = OpenMovieFile.asRead(new QTFile(fileName));
movie = Movie.fromFile(movieFile);
// extrack the video track from the movie
videoTrack = movie.getIndTrackType(1,
StdQTConstants.videoMediaType,
StdQTConstants.movieTrackMediaType);
if (videoTrack == null) {
System.out.println("Sorry, not a video");
System.exit(0);
}
// get the media data struct. used by the video track
vidMedia = videoTrack.getMedia();
numSamples = vidMedia.getSampleCount();
sampIdx = 1; // get the first sample in the track
mediaSample = vidMedia.getSample(0, vidMedia
.sampleNumToMediaTime(sampIdx).time, 1);
// store the width and height of the image in the media sample
ImageDescription imgDesc = (ImageDescription)
mediaSample.description;
width = imgDesc.getWidth();
height = imgDesc.getHeight();
// set up a drawing environment for coverting future frame images
qdGraphics = new QDGraphics(imgDesc.getBounds());
dSeq = new DSequence(imgDesc, qdGraphics, imgDesc.getBounds(),
null,
null, StdQTConstants.codecFlagUseImageBuffer,
StdQTConstants.codecLosslessQuality,
CodecComponent.bestFidelityCodec);
// the sample will be decompressed into qdGraphics
// divide by four to get number of 32-bit ints.
rowInts = qdGraphics.getPixMap().getRowBytes()/4;
width = rowInts;
int bufferSize = rowInts*height;
pixelData = new int[bufferSize];
pixelBuffer = IntBuffer.wrap(pixelData);
public void start() {
//Not implemented - does nothing
}
/**
* stopMovie() and getFrame() are synchronized so that it's not
possible to
* terminate the QuickTime session while a frame is being copied
from the
* movie.
*/
synchronized public void stop() {
if (isSessionOpen) {
QTSession.close();
isSessionOpen = false;
}
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
/** Not implemented */
public Image getNextFrameImage() {
throw new RuntimeException("Not Implemented");
}
/**
* Return a single sample (a frame) from the movie as a
BufferedImage object.
*
* The frame is the next one in the movie, which is counted off
using sampIdx
* (which goes 1 to numSamples, then repeats).
*
* This coding differs from getFrame() in JMFSnapper in that
JMFSnapper gets
* the current frame from a running movie, while this method
always gets the
* next frame from the movie.
*
* The current time is written on top of the image when it's
converted to a
* BufferedImage.
*/
public Buffer getNextFrame() {
if (!isSessionOpen)
return null;
if (sampIdx > numSamples) // start back with the first sample
sampIdx = 1;
try {
// get the sample starting at the specified index time
TimeInfo ti = vidMedia.sampleNumToMediaTime(sampIdx);
mediaSample = vidMedia.getSample(0, ti.time, 1);
sampIdx++;
writeToBufferedImage(mediaSample);
numFramesMade++; // count another frame generated
} catch (Exception e) {
throw new RuntimeException(e);
}
return pixelBuffer;
}
/**
* Write the contents of the sample into the BufferedImage
*
* The data passes through many stages. The basic steps are to get
a 'raw'
* image from the current media sample (frame), then write it as a
pixel array
* into the DataBuffer part of the BufferedImage.
*/
private void writeToBufferedImage(MediaSample mediaSample)
throws Exception
{
// extract the raw image from the sample
RawEncodedImage rawIm = RawEncodedImage.fromQTHandle
(mediaSample.data);
dSeq.decompressFrameS(rawIm, 0);
rawIm.disposeQTObject();
// pull a raw image from qdGraphics (this one will be
decompressed)
RawEncodedImage rawIm2 = qdGraphics.getPixMap().getPixelData();
rawIm2.copyToArray(0, pixelData, 0, pixelData.length);
pixelBuffer.rewind();