Dec 99 Getting Started
Volume Number: 15
Issue Number: 12
Column Tag: Getting Started
Carrying On With QuickTime
by Dan Parks Sydow
More QuickTime fundamentals to prepare you for the
power of Apple's digital movie system software
In last month's Getting Started article I discussed the wealth of features available to
programmers interested in incorporating into their programs QuickTime capabilities.
If you aren't familiar with QuickTime movie playing techniques, you should be! Last
month you read about the basics of QuickTime programming. In particular, you learned
how your application can open a movie from a file and then place that movie - complete
with attached controller - in a window. That article closed with the mention that a
movie could also be used without a movie controller. That's a technique that may be
useful in a variety of circumstances, such as having a movie play in response to some
user action (a game or educational program might include this feature). In this article
we'll conclude our QuickTime introduction by providing the details of how your
program can play a movie without the aid of a movie controller. Here you'll also see
how to embed a movie in a dialog box and then use standard dialog box buttons to play
the movie.
QuickTime Basics Review
Before jumping into this month's code, a short review of the very basics of QuickTime
is in order. For more details, look over last month's Getting Started article. If you've
read that article, though, don't skip this section. Here I also throw in one or two new
tidbits of QuickTime-related code.
Apple has created the Movie Toolbox to help programmers give their applications the
ability to play movies. This toolbox is a large set of movie-related functions to the
system software. When you know the Movie Toolbox, you know QuickTime.
A program that makes use of QuickTime needs to verify that the user's machine has the
QuickTime extension available. It should also initialize the Movie Toolbox. The
following code takes care of these two tasks:
OSErr err;
long result;
err = Gestalt( gestaltQuickTime, &result );
if ( err != noErr )
DoError( "\pQuickTime not present" );
err = EnterMovies();
if ( err != noErr )
DoError( "\pError initializing the Movie Toolbox" );
Before playing a movie, a program needs to open the QuickTime file in which the movie
is stored, load the movie data to memory, and then close the file.
In last month's article you saw how to use the standard Open file dialog box to give the
user the opportunity to choose the movie to play. Here we'll elaborate on a technique
briefly mentioned in last month's article: how a program can easily carry out the
movie-opening chore "behind the scenes." That is, here we'll let the program select
the movie to open. A program of course doesn't randomly open a movie - the selection
is typically based on some user action. For instance, if the user chooses a Connection
Setup item from a Help menu, the program would know to open a movie titled
ConnectSetup.mov to display a movie that walks the user through some connection
process.
To specify a movie to open, your program needs a file system specification, or FSSpec,
for the file that holds the movie. The FSSpec specifies where on disk the movie file is
located. If the name of the file that holds the movie is known (you're supplying the
movie file, so it's name should certainly be known), and if the file is to reside in the
same folder as the application, then the code to create the file's FSSpec looks like this:
#define kApolloMovieName "\pMovieApollo
OSErr err;
FSSpec movieFileSpec;
err = FSMakeFSSpec( 0, 0, kApolloMovieName, &movieFileSpec );
In the above snippet the name of the file to open is MovieApollo (this month's example
program plays two movies, including one of the launch of the Apollo rocket). With the
first two arguments to FSMakeFSSpec() each set to 0, the movie is expected to be in
the same folder as the application. If you'd like to group all your program's movies in a
folder within the same folder that holds the application, then you'll need to include that
folder's name in the third argument to FSMakeFSSpec(). Use a colon between the folder
name and the file name to indicate that this is a pathname and that the first part of the
name is a folder and the second part is a file name. For instance, if stored alongside the
application was a folder named Movies, then the definition of the constant
kApolloMovieName would become:
#define kApolloMovieName "\pMovies:MovieApollo
The FSSpec returned by FSMakeFSSpec() is now used in a call to the Movie Toolbox
function OpenMovieFile(). The three arguments this function requires are a pointer to
the FSSpec, a pointer to a short variable that gets filled in with a file reference
number, and a file-opening permission level.
short movieRefNum;
err = OpenMovieFile( &movieFileSpec, &movieRefNum, fsRdPerm);
The movie file's data can now be loaded to memory. A call to the Movie Toolbox function
NewMovieFromFile() does that:
Movie theMovie;
OSErr err;
short movieRefNum;
short resID = 0;
Str255 name;
Boolean changed;
err = NewMovieFromFile( &theMovie, movieRefNum, &resID, name,
newMovieActive, &changed );
The parameter list to NewMovieFromFile() was described in detail last month. Here's
a brief recap. The first argument, theMovie, holds a pointer to the memory that
contains the movie data. The second argument, movieRefNum, is the reference number
returned by the call to OpenMovieFile(). The third argument, resID, is the resource
ID of the movie's moov resource (use 0 if a file holds a single movie, as is typically
the case). The fourth argument, name, gets filled in with the name of the moov
resource (this resource is often unnamed, in which case nil is returned). The fifth
argument, newMovieActive, is an Apple-defined constant that specifies that the newly
created movie be active (ready to play). Finally, the sixth argument, changed,
indicates whether or not the Movie Toolbox needed to alter any data as it loaded movie
data to memory (it shouldn't have to).
Your program now has the movie data in memory, so the movie file can be closed:
CloseMovieFile( movieRefNum );
Displaying a Movie in a Window (or Dialog Box)
Last month you saw how to display a movie and a movie controller in a window. Here
we'll simply display a movie in a window (or a dialog box - the process is similar).
First, open the window or dialog box:
WindowPtr theWindow;
theWindow = GetNewCWindow( kWINDResID, nil, (WindowPtr)-1L );
Next, set the movie's display coordinate system to that of the window or dialog box in
which the movie is to be played. As you saw last month, a call to the Movie Toolbox
function SetMovieGWorld() sets the display coordinate system of the movie named in
the first argument to that of the window named in the second argument:
SetMovieGWorld( theMovie, (CGrafPtr)theWindow, nil );
Next, get the dimensions of the movie in order to resize the window (or in order to
appropriately place the movie in a dialog box that's larger than the movie). A call to
the Movie Toolbox function GetMovieBox() provides that information in the form of a
rectangle that holds the dimensions of a movie:
Rect box;
GetMovieBox( theMovie, &box );
The left and top coordinates in the rectangle returned by GetMovieBox() may each be
0, in which case the right and bottom coordinates reveal the movies width and height,
respectively. However, the left and top coordinates might not each be 0. So it's wise to
make a call to OffsetRect() to force those two coordinates to 0:
OffsetRect( &box, -box.left, -box.top );
Now call the Movie Toolbox function SetMovieBox() to make the new, offset values the
boundaries for the rectangle that defines the size of the movie:
SetMovieBox( theMovie, &box );
If you want the window that's to display the movie to be the exact size of the movie,
then now's the time to resize that window - just call SizeWindow() using the right and
bottom coordinates of rectangle box as the width and height of the window. In our
example program we won't do that. Instead, we'll further adjust the coordinates of box
to position the movie in a dialog box that's larger than the movie.
Playing a Movie Without a Movie Controller
If you want to play a movie automatically after the movie is displayed, or if you want
to implement your own method of letting the user play a movie (as in displaying a
standard push button in a dialog box), then you'll forego the movie controller code
discussed in last month's article. Instead, you'll make use of a few Movie Toolbox
functions designed for just this type of movie playing.
When a movie is saved, it may or may not be saved with the first frame designated as
the frame to start the movie at. Before playing a movie you'll want to "rewind" the
movie by making a call to GoToBeginningOfMovie(). After that, the Movie Toolbox
function StartMovie() is called to prepare the movie for playing.
GoToBeginningOfMovie( theMovie );
StartMovie( theMovie );
StartMovie() takes care of a few tasks such as setting the movie's playback rate, but
this routine doesn't actually play the movie. To do that your program should repeatedly
call the Movie Toolbox function MoviesTask(). A single call to MoviesTask() processes
only a small part of a movie, updating the display of the movie by drawing a frame -
that's why this function is called from within a loop. The Movie Toolbox function
IsMovieDone() returns a value of true when the movie in question has completed, so
this function can be used to determine when the loop should end.
do
{
MoviesTask( theMovie, 0 );
}
while ( IsMovieDone( theMovie ) == false );
A single call to MoviesTask() is capable of servicing, or updating, more than one
movie. The first argument is the movie to service. If more than one movie is open, pass
a value of nil to specify that all open movies be updated. The second argument is the
number of milliseconds the program is willing to give the Movie Toolbox for its task of
servicing movies. This is the total amount of time to be spend updating the movie or
movies specified in the first argument. Passing a value of 0 here tells the Movie
Toolbox to service each active movie once.
When your program is finished with a movie it should call DisposeMovie() to release
the memory occupied by the movie data:
DisposeMovie( theMovie );
MoreQT
This month's program is MoreQT. As we did last month, here again the example
program isn't menu-driven. When run, MoreQT displays the dialog box shown in
Figure 1. Clicking the Apollo button results in the display and playing of a movie of the
Apollo liftoff - as shown in Figure 2. When the movie finishes, its last frame is left in
the movie-viewing area. To clear that area and return it to its original light gray
state, click the Clear button. To play a movie of the Venus space probe, click the Venus
button. To quit the program, you of course click the Quit button.
Figure 1. The MoreQT dialog box.
Figure 2. The MoreQT program as a movie is playing.
Creating the MoreQT Resources
Start the project by creating a new folder named MoreQT in your CodeWarrior
development folder. Launch ResEdit, then create a new resource file named
MoreQT.rsrc. Specify that the MoreQT folder as the resource file's destination. The
resource file will hold resources of the types shown in Figure 3.
Figure 3. The MoreQT resources.
ALRT 128 and DITL 128 resources are used to define the program's error-handling
alert. There's a second dialog item list resource - DITL 129 - that's paired with the
file's only DLOG resource. Figure 4 shows DLOG 129, while Figure 5 shows DITL 129.
Figure 4. The DLOG resource.
Figure 5. The DITL for the one dialog box.
As shown in Figure 3, there's one PICT resource. This picture is used to create the
three-dimensional look of the movie-playing area in the program's dialog box. To