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
The key to our improved strategy is a method called
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
draw()s. The new
QDGraphics's dimensions are
the same as those of the frames we intend to compress. That means its bounds
QDRect with upper-left corner 0,0 and constant dimensions
VIDEO_TRACK_HEIGHT (which I've
set to 360 by 240, but you're welcome to change in the code). For each
fromRect, we create a
Matrix to map from
fromRect's QDRect to our
The revised process looks like this:
- Get starting and ending rectangles, where a rectangle is a
QDRectrepresenting an upper-left corner point and width by height dimensions.
- Calculate a series of intermediate rectangles that take us from the
- For each of these intermediate
fromRects, use a
Matrixto scale the rectangle into the bounds of an offscreen
QDGraphics, draw it into the
QDGraphics, and then call
compressFrameto make a frame from the offscreen
QDGraphics. Add each frame as a sample.
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
VideoMedia are a very straightforward analogue to last article's
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
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.
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
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
"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);
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
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
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
intthat 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
MPEG-4. You can use MPEG-4 video in a regular QuickTime
.movcontainer, with the caveat that only QuickTime will be able to read it — to create a real
.mp4file, 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