Playing Movies in a Java 3D World, Part 2:by Andrew Davison, author of Killer Game Programming in Java
In part one of this series, I described how to play a movie clip inside of a Java 3D scene with the help of the Java Media Framework (JMF). The implementation uses the Model-View-Controller design pattern:
- The movie screen is the view element, represented by a
- The movie is the model part, managed by a
- A Java 3D
TimeBehavior, is the controller, triggering periodic frame retrievals from the movie, which are drawn onto the screen.
In this article, I'll revisit the movie component, re-implementing it using QuickTime for Java (QTJ). QTJ provides an object-based Java layer over the QuickTime API, making it possible to play, edit, and create QuickTime movies; capture audio and video; and perform 2D and 3D animations. QuickTime is available for the Mac and Windows. Details about QTJ's installation, documentation, and examples can be found at developer.apple.com/quicktime/qtjava.
As a consequence of the design pattern, the replacement of JMF by
QTJ has little effect on the application--only the movie class
JMFSnapper) departs, replaced by a QuickTime for Java version
Figure 1 shows two screenshots of the QTJ version of the Movie3D application. The picture on the right is a view of the screen from the back.
Figure 1. Two views of the QTJ Movie3D application
A quick glance back at Figure 1 of part one shows no obvious differences between the QTJ-based application and the JMF one.
However, a closer comparison of the executing programs reveals two
changes: the QTJ movie is slightly more pixelated, and plays more
slowly. The pixelation was introduced when the original movie was
translated from MPEG to QuickTime's MOV format, and could be remedied
with the help of a better conversion tool. The speed issue is more
fundamental: it relates to the underlying implementation of
The important elements of this article are:
- A discussion of the two main ways of implementing
QTSnapper. One approach renders every frame of the movie onto the screen, while the other selects a frame based on the current time. This latter approach means that frames may be skipped, making the movie jitter, but the skipping permits the movie to play faster.
- The introduction of some simple frame-per-second (FPS) measures, which I'll use to judge the relative speeds of the different approaches, and to detect skipped frames.
1. I'm Still on a Mountain, But a Different One
As in part one, the code here utilizes two large APIs, which I don't have time to describe in any detail. I'm using Java 3D again, but switching media APIs from JMF to QTJ.
You'll find plenty of information about Java 3D in my O'Reilly book, Killer Game Programming in Java (KGPJ), including all of the code for the checkerboard scene in Figure 1, where the movie screen is standing.
I won't be explaining the movie screen or the movie updating behavior, since they're unchanged from part one.
What I will be doing is focusing on the QTJ techniques I employ in
QTSnapper for extracting frames from the movie.
2. Two Overviews of the Application
The application's scene graph is shown in Figure 2.
Figure 2. Scene graph for Movie3D
This graph is almost identical to the one in the first article.
The QuickTime movie is loaded by the
QTSnapper class. The movie screen
is created by
QTMovieScreen, which manages a Java 3D quadrilateral
(a quad) resting on the checkerboard floor. Every 40 milliseconds, the
TimeBehavior object calls the
nextFrame() method in
That, in turn, calls
QTSnapper to get a frame in the
movie, which is laid over the quad managed by
There's an important difference between
JMFSnapper returns the current frame for the playing movie, while
QTSnapper returns the current frame in the movie based on an
For example, as
getFrame() is called repeatedly in
may retrieve frames 1, 3, 6, 9, 11, etc., depending on when the
method is called and the movie's playing speed. When
is called in
QTSnapper, it will return frames 1, 2, 3, 4, and so on.
The UML class diagrams for the application are given in Figure 3. Only the public methods of the classes are shown.
Figure 3. Class diagrams for Movie3D
Aside from the names of the movie screen and movie classes
QTSnapper), there's no difference between this
figure and the class diagrams for the application in part one. In fact,
only the internal implementation of the
Snapper class has altered.
To migrate between the JMF Movie3D application and this QTJ-based
version requires the
Snapper class to be replaced. It's also
necessary to change two lines in the movie screen class, where the
Snapper class is declared and instantiated:
// global variable private QTSnapper snapper; // was JMFSnapper // in the constructor, load the movie in fnm snapper = new QTSnapper(fnm);
These two changes are the only reason for renaming
All the code for this example, as well as an early version of this article, can be found at the KGPJ website.