Mailing Lists: Apple Mailing Lists

Image of Mac OS face in stamp
 
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Possible optimisations in the way I used the Quicktime API?



Hi,

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?

Andre


package ajmas74.oglmpeg;

import java.awt.image.*;
import java.awt.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;

import quicktime.QTSession;
import quicktime.io.OpenMovieFile;
import quicktime.io.QTFile;
import quicktime.qd.QDGraphics;
import quicktime.std.StdQTConstants;
import quicktime.std.image.CodecComponent;
import quicktime.std.image.DSequence;
import quicktime.std.image.ImageDescription;
import quicktime.std.movies.Movie;
import quicktime.std.movies.TimeInfo;
import quicktime.std.movies.Track;
import quicktime.std.movies.media.Media;
import quicktime.std.movies.media.MediaSample;
import quicktime.util.RawEncodedImage;

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

  private int sampIdx; // current sample index

  private QDGraphics qdGraphics; // off-screen drawing surface

  private DSequence dSeq; // decompressed image sequence

  private int width; // frame width

  private int height; // frame height

  private int newWidth;
  private int newHeight;

  private byte[]     colorBytes;
  private byte[]     pixelBytes;
  private ByteBuffer pixelBuffer;

  private long numFramesMade;

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];

      newWidth = roundUpPow2(width,1024);
      newHeight = roundUpPow2(height,1024);

      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.copyToArray(0, pixData, 0, pixData.length);

    int diff = newWidth - rowInts;
    int left = diff/2;
    int right = left;
    if ( diff % 1 > 0 ) {
      right++;
    }

    //We draw the movie centered in the buffer
    int packedPixel = 0;
    int lines = (newHeight - height)/2;
    pixelBuffer.position(lines*(newWidth*3));

    for (int row = height- 1; row >= 0; row--) {
      pixelBuffer.position(pixelBuffer.position()+(left*3));
      for (int col = 0; col < rowInts; col++) {
        packedPixel = pixData[row * rowInts + col];
        pixelBuffer.put((byte) ((packedPixel >> 16) & 0xFF));
        pixelBuffer.put((byte) ((packedPixel >> 8) & 0xFF));
        pixelBuffer.put((byte) ((packedPixel >> 0) & 0xFF));
      }
      pixelBuffer.position(pixelBuffer.position()+(right*3));
    }

    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();
}
}
}


_______________________________________________
Do not post admin requests to the list. They will be ignored.
QuickTime-java mailing list      (email@hidden)
Help/Unsubscribe/Update your Subscription:
http://lists.apple.com/mailman/options/quicktime-java/email@hidden

This email sent to email@hidden


Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Contact Apple | Terms of Use | Privacy Policy

Copyright © 2007 Apple Inc. All rights reserved.