Mar 00 QTToolkit
Volume Number: 16
Issue Number: 3
Column Tag: QuickTime Toolkit
Opening the Toolbox
by Tim Monroe
Controlling QuickTime Movies With the Movie Toolbox
In the previous two QuickTime Toolkit articles, we've focused almost exclusively on
playing QuickTime movies using movie controllers. This is by far the easiest way to
manage movies and to support the standard types of user interaction with movies.
Indeed, some types of QuickTime movies cannot be presented to or managed by the user
without a movie controller. But there are other types of QuickTime movies (most
notably, standard linear movies) that can be played back without using a movie
controller. In this article, we'll see how to accomplish this. In effect, our goal will be
to provide all the capabilities that are provided by our basic application QTShell - for
linear movies at least - but without the assistance of movie controllers. We'll show
how to start and stop movies, provide basic editing services, and set various looping
states.
Why might we want to do this? Well, there are situations where you want to play
QuickTime movies but don't necessarily want the user to be able to manipulate those
movies in the ways typically afforded by movie controllers. Perhaps the simplest
example of this is found quite often in games, where you might play what's called a cut
movie or a cut scene when the user completes one level and advances to the next. The
cut movie serves as a transition from one level to the next, and you generally don't
want the user interrupting or (heaven forbid!) editing your cut movie. So using the
full-fledged movie controller interface is definitely not the best way to handle
QuickTime movie playback here.
There are also more complicated examples of using controller-less QuickTime movies.
For instance, you might want to use a linear QuickTime movie as a texture for a 3D
object that's being displayed by your application. Figure 1 shows a window of the
application TextureEyes, which uses QuickDraw 3D to define and render
three-dimensional objects. In this window, we've created a cube and set its surface
texture to be a QuickTime movie.
Figure 1.A QuickTime movie used as a texture on a 3D object.
Similarly, we might want to embed a QuickTime movie within another QuickTime
movie. For instance, QuickTime VR allows us to draw into a panoramic node. We can
use this capability to draw the individual frames of a QuickTime movie into a
panorama, as shown in Figure 2. Here, the toy train on the table isn't part of the
original panorama. Rather, we're drawing the frames of a standard QuickTime movie
(one of those frames in shown in Figure 3) into the panorama, using the QuickTime VR
application programming interfaces and some QuickDraw routines to drop out the
black background. The net effect is that the train appears to travel in a circle on the
table, thereby imparting some motion and sound to the otherwise static and silent
panorama.
Figure 2.A QuickTime movie embedded into a QuickTime VR panorama.
Figure 3.The QuickTime movie embedded in Figure 2.
We might also want to embed a linear QuickTime movie into another linear QuickTime
movie, as shown in Figure 4.
Figure 4.A QuickTime movie embedded into a QuickTime movie.
The point is not that we couldn't have used movie controllers to help us display and
manage the cut scenes or the textured or embedded QuickTime movies in these sorts of
cases. Rather, the point is that we don't need to do so. In the examples we've mentioned,
we just need a way to manage the display of the movie frames at the appropriate time
(and to play back the movie's sound track, if it has one). So in those cases the only
services we really need are QuickTime's imaging, sound producing, and timing
services. We don't need the robust user-interaction services provided by the standard
linear movie controller.
It's really a question of levels: the movie controller interface for controlling movies is
a higher-level interface than what we need to accomplish the tasks outlined above. Now
we'll drop down a level and directly use the services of that part of QuickTime known
as the Movie Toolbox. The Movie Toolbox is really the heart and soul of QuickTime. It
provides services for opening movie files and extracting movies from them, playing
movies, editing movies, creating movies, and much more. Indeed, the original Inside
Macintosh documentation for the Movie Toolbox (that is, circa 1993) ran to well over
400 printed pages. To a large degree, programming with QuickTime is using the Movie
Toolbox.
So here's what we want to accomplish in this article: we want to modify our basic
application QTShell so that it can open and play back linear QuickTime movies without
using movie controllers. Let's call this new application QTMooVToolbox, in honor of the
fact that we'll be doing everything using the Movie Toolbox. Since we're not using a
movie controller, there will be no movie controller bar in our movie windows. A
typical movie window will therefore look like the one in Figure 5.
Figure 5.A movie window displayed by QTMooVToolbox.
As indicated above, we want to support the standard movie editing capabilities, so our
application's Edit menu will be unchanged (though of course the code providing the
editing capabilities will change quite a bit). We also want to provide the user a way to
start and stop the movie and to set normal and palindrome looping. Figure 6 shows the
Test menu that we want to support.
Figure 6.The Test menu for QTMooVToolbox.
The last menu item here ("Add Picture In Picture...") is particularly interesting. It
allows us to embed one linear QuickTime movie inside of another linear QuickTime
movie (as shown in Figure 4). As we'll see, it's fairly easy to use the Movie Toolbox to
accomplish this.
Setting Up to Use QuickTime
Before moving forward with our QTMooVToolbox application, we need to back up a step
or two to fill in a few details that we skipped over in the first two articles. In order to
use any of the QuickTime services, we need to make sure that the QuickTime client
software is available on the target machine. On Windows computers, we can do this by
trying to initialize the QuickTime Media Layer, like this:
myErr = InitializeQTML(0L);
If InitializeQTML completes successfully (that is, if myErr is noErr), then we know
that QuickTime is available. On the other hand, if InitializeQTML doesn't complete
successfully, then either QuickTime or some other essential component is missing, in
which case we will just exit the application (perhaps after informing the user that
QuickTime needs to be installed).
On Macintosh computers, we can call the Gestalt function to determine whether
QuickTime is available, as shown in Listing 1:
Listing 1: Determining whether QuickTime is available.
______________________________
QTUtils_IsQuickTimeInstalled
Boolean QTUtils_IsQuickTimeInstalled (void)
OSErr myErr = noErr;
myErr = Gestalt(gestaltQuickTime, &myAttrs);
return(myErr == noErr);
}
On either Windows or Macintosh computers, once we've determined that QuickTime is
indeed available in the current operating environment, we need to call the EnterMovies
function, like so:
myErr = EnterMovies();
EnterMovies initializes the Movie Toolbox, performing any necessary set-up for our
application (such as allocating any private storage it might need to use on our behalf).
As with InitializeQTML, we will check the value returned by EnterMovies and exit the
application if an error occurs.
When we are finished using QuickTime services - usually, when our application is
about to terminate - we should undo the work done by EnterMovies and (on Windows)
InitializeQTML. We can call the ExitMovies function to terminate our connection with
the Movie Toolbox. On Windows, we also need to call TerminateQTML to unregister our
application from the QuickTime Media Layer.
Using Application-Specific Data
Suppose now that our application is up and running, that we've successfully initialized
QuickTime and the Movie Toolbox, and that the user has decided to open a movie file
(perhaps using the Open command in the File menu). Our basic cross-platform
framework calls the QTFrame_OpenMovieInWindow function to prompt the user for
the movie file to open, create a window object record to hold basic information about
the movie and the file it is contained in, open a new window on the screen, adjust the
size of that window to fit the movie's natural size, and do some other useful set-up
before the user can begin to interact with the movie.
QTFrame_OpenMovieInWindow also calls the function QTApp_SetupWindowObject,
defined in the file ComApplication.c, to allow our application to perform any
application-specific actions on the new movie, before it is displayed to the user. Both
of our previous applications (namely, QTShell and QTController) have not put any code
at all into QTApp_SetupWindowObject because the default configuration was sufficient
for our purposes then. Here, for QTMooVToolbox, we'll need to change that, for two
reasons. First, we need to maintain some information for each open movie window
beyond what is contained in the window object record. Second, since we are not using
movie controllers in the QTMooVToolbox application, we need to do some additional
work to prepare our movies for playback. Let's tackle the first task here and defer the
second until a bit later, in "Preparing Movies For Playback".
We already have the apparatus in place to support adding application-specific data to
our movie windows. As you may recall, the window object record includes the field
fAppData, which is declared to be of type Handle. We put this field there precisely to
provide ourselves a place to store any information that is specific to a particular
application (since we didn't want to be changing the framework files all the time). For
QTMooVToolbox, we'll define a structure of type ApplicationDataRecord that contains
three fields:
typedef struct ApplicationDataRecord {
MovieEditState fMovieEditState; // the edit state of the movie
Movie fPIPMovie; // the
picture-in-picture movie
Rect [TOKEN:26192]IPRect;
// the picture-in-picture rectangle
} ApplicationDataRecord, *ApplicationDataPtr, **ApplicationDataHdl;
We don't need to explain these fields quite yet (though the comments should give you a
good enough idea of what they are for). But we do need to show how the application data
record gets created and attached to the window object. We do this in the function
QTApp_SetupWindowObject defined in Listing 2.
Listing 2: Setting up a window object.
______________________________
QTApp_SetupWindowObject
void QTApp_SetupWindowObject (WindowObject theWindowObject)
Movie myMovie =
NULL;
ApplicationDataHdl [TOKEN:28025]AppData = NULL;
OSErr myErr =
noErr;
if (theWindowObject != NULL) {
// get rid of the movie controller that's already been
attached to this window object;
// we do this to illustrate how to manage movies without
using a movie controller
if ((**theWindowObject).fController != NULL) {
MCSetActionFilterWithRefCon((**theWindowObject)
.fController, NULL, 0L);
DisposeMovieController((**theWindowObject).fController);
(**theWindowObject).fController = NULL;
}
// allocate an application-specific data record
myAppData =
(ApplicationDataHdl)NewHandleClear
(sizeof(ApplicationDataRecord));
(**theWindowObject).fAppData = (Handle)myAppData;
(**myAppData).fMovieEditState = NULL;
(**myAppData).fPIPMovie = NULL;
MacSetRect(&(**myAppData).fPIPRect, 0, 0, 0, 0);
}
// some lines omitted here
}
}
Notice that the first thing we do in QTApp_SetupWindowObject is get rid of the movie
controller that has already been created by the time QTApp_SetupWindowObject is
called. That's because (as you already know) we want to illustrate how to manage
movies without using a movie controller. We might as well just dispose of the
controller, so we're sure it's not doing any work for us.