I am trying to play a Quicktime movie using JOGL (Java OpenGL) and
noticed that there was an
increase in the memory usage. Below is the code I am using to play
the Quicktime movie. Putting
System.gc() in the main() method helps reduce the memory usage,
though I would be interested to
know if there are any optimizations I could make in the way I access
the Quicktime for Java API,
in order to reduce unnecessary resource creaions.
Attached is the source code that I am using.
BTW I am also curious to know what I need to do so this code also
plays MPEG movies?
/**
* 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 {
// size of BufferedImage; should be a power of 2, less than 512,
// or older graphic cards may get upset when using it as a texture
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 pixData[]; // a pixel array
private int numSamples; // number of samples in the movie
public QTMoviePlayer(String fileName, int maxWidth) 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
// set up an array for storing the pixel data
rowInts = qdGraphics.getPixMap().getRowBytes() >>> 2;
// divide by four to get number of 32-bit ints.
pixData = new int[rowInts * height];
pixelBytes = new byte[newWidth*newHeight*3];
pixelBuffer = ByteBuffer.wrap(pixelBytes);
Color bgColor = Color.YELLOW;
colorBytes = new byte[3];
colorBytes[0] = (byte) bgColor.getRed();
colorBytes[1] = (byte) bgColor.getGreen();
colorBytes[2] = (byte) bgColor.getBlue();
//Set the background color. We only need to
//do it once, since we will only be drawing
//the pixels that are changed when copying
//the frames
for ( int i=0; i<pixelBytes.length-3;i+=3) {
System.arraycopy(colorBytes,0,pixelBytes,i,3);
}
numFramesMade = 0;
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
}
public void start() {
//Not implemented
}
/**
* 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 newWidth;
}
public int getHeight() {
return newHeight;
}
/** 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();
pixelBuffer.rewind();
rawIm2.disposeQTObject();
} // end of writeToBufferedImage()
/** Find the next "power of 2" number. ensuring that it
* is not larger than maxValue.
* @param value
* @param maxValue
* @return
*/
private static int roundUpPow2(int value, int maxValue ) {
value = Math.min(value,maxValue);
int result = 1;
while (result < value) {
result *= 2;
}
return result;
}
public static void main ( String[] args ) {
try {
QTMoviePlayer moviePlayer = new QTMoviePlayer("data/
DSCN8501.MOV",1024);
while ( true ) {
Buffer buffer = moviePlayer.getNextFrame();
Thread.yield();
//System.gc();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}