Module 1- Initializing Quicktime
and Displaying an Image
Contents
Overview
In
this first module, we will learn the basics of QuickTime for Java, such
as initializing QuickTime for Java, creating a window with a QTCanvas,
and drawing an image centered within the window. (See image right.)
Additionally, we will familiarize ourselves with some of the principal
concepts behind QTJ and explore the architecture. At the end of this module,
you will have an knowledge of how to create and prepare an application
that uses QuickTime for Java and having a basic understanding of how to
use the QTJ API.
Introducing
QuickTime for Java
QuickTime for Java is both an API and an application framework. As an
API, it provides Java developers access to the rich wealth of multimedia
capabilities in Quicktime previously available only to C/C++ and Pascal
programmers. It enables access to QuickTime's native runtime libraries
which provide support for different forms of media (images, audio, and
movies), timing services, media capture, complex compositing, visual effects,
and custom controllers. QuickTime for Java has many other benefits as
well. Since the API relies on native libraries to perform its complex
and time-consuming tasks, it is extremely fast. It is also cross-platform-
it will run on all platforms that support QuickTime (Macintosh, Windows
NT, Windows 95, and Windows 98).
The application framework layer makes it easier for Java developers to
integrate QuickTime capabilities into Java applets and applications. The
framework includes:
- the integration of QuickTime with the Java Runtime. This includes
sharing display space between Java and QuickTime for Java and the passing
of events between QuickTime and Java.
- classes that simplify the process of authoring QuickTime content and
providing basic multimedia support
We will be taking advantage of both of these layers in the first module
of this tutorial.
Initializing
QuickTime and Drawing an image
QuickTime for Java is a large API and it is very easy to get lost. Perhaps
the best way to learn QuickTime for Java is to jump right in and start
writing a simple example. This module is presented in a progressive format
as if the code was being written before your eyes. This is designed to
help suggest a methodology for programming to the QuickTime for Java API.
Newly added code will appear in yellow (yellow)
while code from previous steps will appear in gray (gray).
Code that is removed or deleted will appear in red (red).
We will explain things as we go along, so open up your favorite source
editor, and let's start coding!
Creating the Zoo1 Class
Our first step will be creating a window and displaying it on the screen.
The easiest way to do this is to create a new class derived from java.awt.Frame
that looks something like this:
import java.awt.*;
public class Zoo1 extends Frame
{
public static void main( String[] args )
{
}
}
|
So far we haven't done anything tricky. We have a static main method because
we want to build an application instead of an applet. Now would be a good
time to save your file as "Zoo1.java". The file should be the
same name as the main class or your compiler may complain.
Back to top
Adding the Constructor
Next, we will add a constructor that takes a string parameter for the
title of the window:
import java.awt.*;
public class Zoo1 extends Frame
{
|
public Zoo1( String s )
{
super(s);
}
|
public static void main( String[] args )
{
|
Zoo1 appWindow = new Zoo1( "QTZoo 1" );
|
}
}
|
Now we have added our constructor for the Zoo class. The constructor takes
a string argument and passes it off to our superclass constructor which
is Frame. The Frame class will automatically handle initializing the window
title for us based on our string argument. We have also created an instance
of our object in main( ), and passed a window title to the constructor.
Now we are ready to perform our initialization.
Back to top
Window Initialization
Now we need to configure the parameters of the window such as the size
and resizability. Once again, this is all standard Java AWT, so we are
not going to explain most of this in great detail:
import java.awt.*;
public class Zoo1 extends Frame
{
|
static public int WIDTH = 640;
static public int HEIGHT = 480;
|
public Zoo1( String s )
{
super(s);
|
setResizable( false );
setBounds( 0, 0, WIDTH, HEIGHT );
|
}
public static void main( String[] args )
{
Zoo1 appWindow = new Zoo1( "QTZoo 1" );
|
appWindow.show();
appWindow.toFront();
|
}
}
|
We have created two static integers to store information about the height
and width of our window. We will use these values in the constructor in
which we make the window non-resizable and set the bounds of the window.
Finally, in the main method, we call show( ) to make the window visible
and call toFront( ) to ensure that it is the front-most window.
It is useful to note that thus far, no special coding to deal with QuickTime
has been done. This serves to emphasize the fact that QuickTime for Java
is designed to work in a complementary manner with the standard AWT. It
does not enforce a specific programming style.
In the next step we will initialize QT.
Back to top
Initializing QuickTime
Since QuickTime is a native library, its memory is managed outside of
the normal Java heap. In most cases, you create QuickTime for Java objects
in the same manner that you would create a standard Java object. However,
before you work with QuickTime for Java, you must allow QuickTime to allocate
memory for the native objects associated with your application and initialize
its internal data structures.
This is accomplished through one API call that is part of the quicktime.QTSession
package:
import java.awt.*; |
Import quicktime.QTSession;
import quicktime.QTException;
|
public class Zoo1 extends Frame
{
static public int WIDTH = 640;
static public int HEIGHT = 480;
public Zoo1( String s )
{
super(s);
setResizable( false );
setBounds( 0, 0, WIDTH, HEIGHT );
}
public static void main( String[] args )
{
|
try
{
QTSession.open();
|
Zoo1 appWindow = new Zoo1( "QTZoo 1" );
appWindow.show();
appWindow.toFront();
|
}
catch ( Exception e )
{
QTSession.close();
e.printStackTrace();
}
|
}
}
|
We have included two packages at the beginning of our application. The
first, the QTSession
package allows us to open and close QuickTime sessions between Java and
the native QuickTime library. The second is for QTException which is thrown
by many QTJ calls if error conditions arise.
After adding the two import statements, we add a try / catch block. This
is necessary because our call to open the QTSession which initializes
QuickTime for Java could throw an Exception (for example if QuickTime
is not installed). If we don't catch this, we will get a compiler error
because the static method QTSession.open(
) is declared as throwing an Exception.
We attempt to open the session, and if any error occurs, we close the
session in our catch block by calling the static method QTSession.close(
).
The key point of interest in this step is that we must call QTSession.open(
) to initialize QuickTime. At the end of our application, we should call
QTSession.close( ) to perform cleanup and properly shut down our QuickTime
session. You will notice that we are only closing down QTJ if an error
condition occurs. We should also close our session if the user quits the
application. We will handle this case in the following step.
Back to top
Handling Application Termination
When the user quits our application by closing our window, or choosing
Quit from the Apple Menu, we need to shut down QTJ:
import
java.awt.*; |
Import java.awt.event.*;
|
Import quicktime.QTSession;
import quicktime.QTException;
public class Zoo1 extends Frame
{
static public int WIDTH = 640;
static public int HEIGHT = 480;
public Zoo1( String s )
{
super(s);
setResizable( false );
setBounds( 0, 0, WIDTH, HEIGHT );
|
addWindowListener( new WindowAdapter()
{
public void windowClosing( WindowEvent we )
{
QTSession.close();
dispose();
}
public void windowClosed( WindowEvent we )
{
System.exit( 0 );
}
});
|
}
public static void main( String[] args )
{
try
{
QTSession.open();
Zoo1 appWindow = new Zoo1( "QTZoo 1" );
appWindow.show();
appWindow.toFront();
}
catch ( Exception e )
{
QTSession.close();
e.printStackTrace();
}
}
}
|
We have added an import statement for java.awt.event so that we can create
our new WindowAdapter. Our window adapter code is a bit tricky. We are
taking advantage of a Java's support for a feature called anonymous inner
classes. This allows us to create a new subclass of WindowAdapter and
override two of its methods from inside the call addWindowListener( )
which is part of Frame. This allows us to receive notification when the
close box is pressed via the windowClosing( ) method and when the window
has been destroyed via the windowClosed( ) method.
In the windowClosing( ) method we shut down the QTSession and dispose
of our window. In windowClosed( ) we call System.exit( ) which quits our
program.
You will notice that we did not do anything special to handle the quit
menu item. On Windows, choosing quit causes close messages to get sent
to any open windows (which in our case would close QTJ because of our
WindowAdapter class). On the Mac, where this isn't the case, a default
quit handler is installed for us by QTJ that automatically calls QTSession.close(
) as the application terminates.
Now the only task that remains is drawing our image.
Back to top
Drawing an image using QTJ
Now comes the fun part- opening an image file and using QuickTime to
display it in our window. First we need to import a few packages:
import java.awt.*;
Import java.awt.event.*;
|
Import java.io.IOException;
|
import quicktime.QTSession;
import quicktime.QTException;
|
import quicktime.app.image.GraphicsImporterDrawer;
import quicktime.app.display.QTCanvas;
import quicktime.app.QTFactory;
import quicktime.io.QTFile;
|
These are mostly QuickTime packages which we will be using to load and
draw our images. For example, the GraphicsImporterDrawer
class allows QuickTime to parse a number of image file formats and draw
the image. We also need IOException because QT will throw this type if
the file specified is not found. We will talk about these classes in the
code section below:
public class Zoo1 extends Frame
{
static public int WIDTH = 640;
static public int HEIGHT = 480;
public Zoo1( String s )
{
super(s);
setResizable( false );
setBounds( 0, 0, WIDTH, HEIGHT );
|
QTCanvas myQTCanvas = new QTCanvas(
QTCanvas.kInitialSize, 0.5F, 0.5F );
add( myQTCanvas );
|
Here we are adding drawing functionality to our window. For simplicity's
sake, we have decided to place this code directly in the constructor.
Our first step is to create a QTCanvas.
To present QuickTime content in a Java Applet or Application, you need
a mechanism for interacting with the display and the event system. The
QTCanvas is a specialized canvas that provides access to QuickTime's native
drawing mechanism within a Java window. It essentially punches a hole
in the Java display area and manages display of that region using QuickTime.
All QuickTime drawing must occur in a QTCanvas. When we create a QTCanvas
here, we are specifying an area of our window where QuickTime will perform
imaging. The constructor of the QTCanvas takes three arguments. The first
specifies how the QTCanvas should behave when its container resizes. In
this example, we have specified the constant QTCanvas.kInitialSize.
With this sizing flag, if our window grows larger than the size of the
canvas, the canvas does not scale. It remains at its original specified
size. Similarly, if the window becomes smaller than the canvas, the canvas
does not scale either. It will be clipped to the window size. There are
other flags that allow you to have free scaling as well as ones that allow
scaling but preserve aspect ratio. These are all defined in QTCanvas.
The second and third parameters of the QTCanvas constructor are for horizontal
and vertical alignment. These specify the default position of items drawn
within the canvas. We have specified the float value of 0.5 for both the
horizontal and vertical alignment parameter. This will center items within
the Canvas. A value of zero will specify the top or left and the value
of one will specify bottom or right.
For example, let's pretend that we are creating a window and placing
a QTCanvas inside it. Let's also pretend that our client (QT object that
is capable of drawing itself in a QTCanvas) is 125 by 125 pixels (which
is slightly smaller than the window we are creating). The following series
of images demonstrates the behavior we would see based on the values of
the parameters we pass the QTCanvas. Note that the QTCanvas is invisible.
It is the area that drawing may occur in. The green area with the text
QTCanvas is the client of the Canvas that is doing the drawing:
 |
QTCanvas( QTCanvas.kInitialSize,
0.5F, 0.5F )
Remember that the canvas is the same size as the window. The client
area is smaller than the QTCanvas it lives in. By specifying kInitial
size, no scaling occurs. The two float values specify that the client
is to be drawn centered within the QTCanvas. If our window was smaller
than the client, part of the client would not fit in the window.
No scaling would occur.
|
 |
QTCanvas( QTCanvas.kInitialSize,
0F, 0F )
This is very similar to the first example except that by specifying
0F, 0F, we are telling the canvas to draw it's client object in
the upper left hand corner. Like the previous example, if the window
were resized, no scaling would occur.
|
 |
QTCanvas( QTCanvas.kFreeResize,
0F, 0F )
In this example, we have specified the kFreeResize parameter. This
will cause the QTCanvas to resize with the window, and will also
cause the content area of the QTCanvas to freely scale as the QTCanvas
changes sizes.
|
For our application, we want the images to be centered in our window and
not scale (even if they are too big to fit in the window). These parameters
may easily be changed at runtime by calling the QTCanvas methods setAlignment(
), and setResizeFlag(
).
Once we have created the QTCanvas, we add it to the window by calling
add( ) from Component.
Now that we have a QTCanvas to draw into, we need to load the image files
and display them:
add( myQTCanvas );
|
try
{
QTFile imageFile = new QTFile(
QTFactory.findAbsolutePath(
"data/zebra/ZebraBackground.jpg" ));
|
We need to put all of our code in a try block, because QuickTime could
throw an exception if the media is not found. Now we create a new QTFile
object that represents the location of our zebra image. Since QTFile's
constructor can take a file, and we don't want to hard code an absolute
path (which will break if our application moves), we are using QTFactory's
findAbsolutePath( ) to locate the image file based on a relative path
and create a File object that we can pass to our QTFile constructor.
Now that we have a reference to the QuickTime media file, we need to
load it and draw it:
QTFile imageFile = new QTFile(
QTFactory.findAbsolutePath(
"data/zebra/ZebraBackground.jpg" ));
|
GraphicsImporterDrawer mapDrawer =
new GraphicsImporterDrawer( imageFile );
myQTCanvas.setClient( mapDrawer, true );
|
We declare a new GraphicsImporterDrawer
object. This object is responsible for examining the media file, decompressing
the file (if necessary), importing it into a native format that can be
easily drawn, and preparing it for drawing. If the media file is in one
of the many formats that QuickTime supports, the GraphicsImporterDrawer
object is capable of loading the file and displaying it in a QTCanvas.
Once we have loaded the media file, we call setClient(
) from the QTCanvas with that object as a parameter. This tells the
canvas that our GraphicsImporterDrawer is responsible for imaging the
contents of the QTCanvas. Thus, when the area of the window that contains
the QTCanvas needs to be redrawn, the QTCanvas will notify its client
(the GraphicsImporterDrawer) that it needs to draw.
That's all that needs to be done! The code that we just wrote is sufficient
to load and draw a media file regardless of the format! Now we will take
a step back and look at the whole constructor and add some exception handling:
public Zoo1( String s )
{
super(s);
setResizable( false );
setBounds( 0, 0, WIDTH, HEIGHT );
QTCanvas myQTCanvas = new QTCanvas(
QTCanvas.kInitialSize, 0.5F, 0.5F );
add( myQTCanvas );
try
{
QTFile imageFile = new QTFile(
QTFactory.findAbsolutePath(
"data/zebra/ZebraBackground.jpg" ));
GraphicsImporterDrawer mapDrawer =
new GraphicsImporterDrawer( imageFile );
myQTCanvas.setClient( mapDrawer, true );
|
}
catch ( IOException e )
{
e.printStackTrace();
}
catch ( QTException e )
{
e.printStackTrace();
}
|
addWindowListener( new WindowAdapter()
{
public void windowClosing( WindowEvent we )
{
QTSession.close();
dispose();
}
public void windowClosed( WindowEvent we )
{
System.exit( 0 );
}
});
}
|
Both the QTFile constructor, and the findAbsolutePath( ) routine can throw
IOExceptions. In addition, the GraphicsImporterDrawer constructor can
throw a QTException. Thus, it is vital that we catch these exceptions.
Note that we are not doing much if we get an error. We are just printing
a stack trace. In a real application, you would probably want to display
some kind of dialog to alert the user of the problem. For the purposes
of this tutorial, this level of error handling is sufficient.
That is all there is to it! Click on this link if you would like to see
the entire source file.
Back to top
Summary
This module represents our first foray into the world of QuickTime for
Java. As such, we are taking tentative steps. We learned how to initialize
QuickTime for Java, and how to shut down our QTSession when the user quits
or an error occurs. We learned how the QTCanvas works, and how to create
a canvas and install it in our window. Finally, we learned how to load
and display media files using a GraphicsImporterDrawer and set it to be
the client of the canvas. That is actually quite a bit for such a small
example.
Looking back, there is actually not very much code required to do what
we are doing. It would take about the same code to get similar functionality
using just the AWT, but the advantage of using QuickTime for Java is that
not only do we have the flexibility of a variety of image formats (which
there is no support for in Java such as PNG, Photoshop, Pict, Targa, TIFF,
and bmp to name a few), but we can also render the media very quickly.
That is the heart of Quicktime for Java- flexibility, and performance.
Back to top
Further
Exploration
The clever reader will realize quickly that the information that was
presented here only scratches the surface of what could be done using
QuickTime for Java. Although we will expand this example in future modules,
there are few things that you could do on your own as exploration that
will help you better understand how QuickTime for Java works.
Each one of these examples has a source code solution, so if you have
trouble, feel free to consult it.
Experiment with one or more of the following:
- Make the window resizable and see how it affects the way the image
displays
- Make the image resize as the window gets bigger
- Make the image scale proportionally if the window size is too small
to display the image, but don't allow the image to grow larger than
its original size.
- Display your own images instead of the zebra. (What happens if you
specify a movie instead of an image?)
|