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


AddThis Social Bookmark Button

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 JMFMovieScreen class.
  • The movie is the model part, managed by a JMFSnapper class.
  • A Java 3D Behavior class, 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 called QTSnapper.

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
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.

Related Reading

Killer Game Programming in Java
By Andrew Davison

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 QTSnapper.

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
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 QTMovieScreen. That, in turn, calls getFrame() in QTSnapper to get a frame in the movie, which is laid over the quad managed by QTMovieScreen.

There's an important difference between JMFSnapper and QTSnapper. JMFSnapper returns the current frame for the playing movie, while QTSnapper returns the current frame in the movie based on an incrementing index.

For example, as getFrame() is called repeatedly in JMFSnapper, it may retrieve frames 1, 3, 6, 9, 11, etc., depending on when the method is called and the movie's playing speed. When getFrame() 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
Figure 3. Class diagrams for Movie3D

Aside from the names of the movie screen and movie classes (QTMovieScreen and 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 JMFMovieScreen to QTMovieScreen.

All the code for this example, as well as an early version of this article, can be found at the KGPJ website.

Pages: 1, 2, 3

Next Pagearrow