ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Making Media from Scratch, Part 2
Pages: 1, 2, 3

The Matrix Reloaded

Part 1 demonstrated how QuickTime's Matrix class could be used to define a spatial transformation. Mainly, we used it to move text located at (0,0) to a point at the bottom of a movie, but look at the javadocs and you'll see some intriguing methods, like rotate() and scale().



The key to our improved strategy is a method called rect() that combines a coordinate mapping with a scaling operation. This allows us to use any source rectangle and scale it to the size of the frames we're compressing for the movie.

To make this work, the sample code creates an offscreen QDGraphics and tells the GraphicsImporter to use this for its draw()s. The new QDGraphics's dimensions are the same as those of the frames we intend to compress. That means its bounds are a QDRect with upper-left corner 0,0 and constant dimensions VIDEO_TRACK_WIDTH by VIDEO_TRACK_HEIGHT (which I've set to 360 by 240, but you're welcome to change in the code). For each intermediate fromRect, we create a Matrix to map from the fromRect's QDRect to our QDRect's bounds.

The revised process looks like this:

  1. Get starting and ending rectangles, where a rectangle is a QDRect representing an upper-left corner point and width by height dimensions.

    Step On
    Step One

  2. Calculate a series of intermediate rectangles that take us from the startRect to the endRect.

    Step Two
    Step Two

  3. For each of these intermediate fromRects, use a Matrix to scale the rectangle into the bounds of an offscreen QDGraphics, draw it into the QDGraphics, and then call compressFrame to make a frame from the offscreen QDGraphics. Add each frame as a sample.

    Step Three
    Step Three

Making It Real

Given that strategy, let's step through the code that makes it all work. We'll skip over creating the movie itself, which we covered last time. Similarly, creating and adding the VideoTrack and VideoMedia are a very straightforward analogue to last article's TextTrack and TextMedia setup.

If this is your first time compiling and running QuickTime for Java code, see my earlier article, "A Gentle Re-Introduction to QuickTime for Java," for information on how to work out CLASSPATH and Java versioning issues.

To get things started in this example, we need to know the source image file, as well as the startRect and endRect rectangles that define the movie we are to make. The sample code expects a makecsequence.properties file to be in the current directory, with entries that look something like this:

file=/Users/cadamson/Pictures/keagy/DSC01763.jpg

start.x=545
start.y=370
start.width=1500
start.height=1125

end.x=400
end.y=390
end.width=800
end.height=600

If this file is absent, the user will be queried for an image file at runtime, and the rectangles will be chosen randomly.

Given a QTFile for the image file, creating the GraphicsImporter is quite straightforward:

GraphicsImporter importer = new GraphicsImporter (imgFile);

Next, we create the offscreen QDGraphics and tell the GraphicsImporter to use it for its drawing:

QDGraphics gw =
    new QDGraphics (new QDRect (0, 0,
                                VIDEO_TRACK_WIDTH,
                                VIDEO_TRACK_HEIGHT));
importer.setGWorld (gw, null);

Notice that I inadvertently called the variable gw, as in "GWorld". The use of that term in the API and Apple's docs is really pervasive!

One thing we have to prepare early is a block of memory big enough to hold the largest possible frame that the chosen video compressor could create. To do this, we call a getMaxCompressionSize() method, allocate a block of memory of that size (as referenced by a QTHandle), and "lock" the handle so it can't move while we're working with it. Finally, we can create a RawEncodedImage object with this buffer:

int rawImageSize =
    QTImage.getMaxCompressionSize (gw, 
                                   gRect, 
                                   gw.getPixMap().getPixelSize(),
                                   StdQTConstants.codecNormalQuality, 
                                   CODEC_TYPE,
                                   CodecComponent.anyCodec);
QTHandle imageHandle = new QTHandle (rawImageSize, true);
imageHandle.lock();
RawEncodedImage compressedImage =
    RawEncodedImage.fromQTHandle(imageHandle);

Related Reading

Mac OS X for Java Geeks
By Will Iverson

The CODEC_TYPE is a constant defined early in the sample code. It is an int that indicates which QuickTime-supported compression scheme we've chosen to use, "codec" being the term for a scheme by which video is encoded and decoded. Many of these are provided as constants in the StdQTConstants class. Among the popular choices are:

  • kCinepakCodecType. Cinepak is a widely supported codec dating back to the early 90s. However, its image quality and compression ratios aren't very compelling anymore.

  • kSorensonCodecType. Sorenson Video pretty much replaced Cinepak for a lot of QuickTime users with its higher quality and great compression.

  • kH263CodecType. H.263 is a codec originally designed for videoconferencing but widely used in other environments. It is also supported by Windows Media Player, the Java Media Framework, and is a simple form of MPEG-4 video.

  • kAnimationCodecType. A compressor meant for use with synthetic images. Apple's demo code uses this a lot, but that's because their sample apps create their own image data. Our photo doesn't compress well with the Animation codec, so only use it here if you want to be shocked by how big the resulting file is (hint: make sure you have at least 15MB free!).

There are more supported codecs than QTJ lets on, but you have to look in the native API's ImageCompression.h to find them. Two great options are:

  • Sorenson 3. A newer version of the Sorenson codec, Sorenson 3 is available in QuickTime 5 and up. The identifier for this codec is SVQ3, so to create the int that QuickTime wants, we take the bottom eight bits of each character (we pretend that we've gone back in time and Unicode doesn't exist yet). Since S is 0x53, V is 0x56, Q is 0x51, and 3 is 0x33, the int value is 0x53565133.

  • MPEG-4. You can use MPEG-4 video in a regular QuickTime .mov container, with the caveat that only QuickTime will be able to read it — to create a real .mp4 file, you'd need to use a MovieExporter, as shown in the article on the QuickTime File Format. For our current purposes, the codec type of MPEG-4 video is mp4v, which translates to the int value 0x6d703476.

Pages: 1, 2, 3

Next Pagearrow