Jan 00 QTToolkit
Volume Number: 16
Issue Number: 1
Column Tag: QuickTime Toolkit
QuickTime Toolkit
by Tim Monroe
Using Movie Controllers for QuickTime Movie
Playback and Editing
The State of the Art
It's been almost ten years since Apple introduced QuickTime, its software architecture
for creating and playing back multimedia content. In that time, QuickTime has
progressed from a Mac-only tool for playing movies roughly the size of postage stamps
into the de facto standard on Macintosh and Windows computers for the creation,
delivery, and playback of digital media, including video, sound, music, 3D graphics,
virtual reality, sprite animation, and more. The range of abilities that QuickTime has
gained over the years is truly staggering. It now supports real-time streaming of
audio and video over the Internet and LANs, a full-featured video effects and
transitions architecture, vector drawing capabilities, and the ability to associate
actions to sprites, text, hot spots in QuickTime VR movies, and 3D objects.
It's high time, then, that MacTech should begin to devote some regular space to
discussing QuickTime from a programmer's point of view, and this is the first of a
series of articles that will focus on using the QuickTime application programming
interfaces to create and play back digital media. Given the wide range of capabilities
provided by the QuickTime APIs, you can guess that there will be no shortage of topics
for us to investigate over the coming months and years. Admittedly, QuickTime can
perhaps be a bit overwhelming if you try to understand it all at once. So we'll adopt a
fairly leisurely approach and try to dissect the QuickTime architecture a little bit at a
time, starting with its high-level interfaces and then gradually working down to
lower-level capabilities to add to our knowledge of the entire architecture. Eventually,
we'll be creating QuickTime files and writing custom QuickTime components just like
the pros.
In this article, we'll learn how to open and display QuickTime movies, and how to
manage the user's interactions with those movies (such as starting and pausing
movies, zooming in and out in QuickTime VR movies, and similar operations). This is a
relatively simple task, and it's one that involves adding a fairly small amount of code
to a basic working application. So, to really earn our money today, we're going to
complicate things. In addition to showing how to support basic movie playback and
editing in a Macintosh application, we're also going to show how to do it in an
application that runs on Microsoft Windows. In other words, we want the code we write
here to compile and execute both on the Macintosh operating system and on the major
flavors of the Windows operating system (to wit: Windows 95, Windows 98, and
Windows NT).
Now this might sound like heresy, especially for a publication like MacTech whose
mission is to report on and facilitate software development for Macintosh computers.
But it's really just as consistent with MacTech's mission as its regular coverage of the
Internet or of Java and other cross-platform languages and tools. Moreover, it's fair to
say that the cross-platform parity exhibited by the QuickTime application
programming interfaces since version 3.0 is largely responsible for QuickTime's
recent explosion in popularity and is crucial for its continued success. So, everything
we're going to do in these articles will be completely cross-platform.
In point of fact, however, once you learn to pay attention to a few recurring issues like
the endianness of multi-byte data, writing QuickTime code that is compatible with
multiple platforms really isn't so hard. Indeed, part of the reason that QuickTime runs
so well on Windows is that a good bit of the Macintosh programming concepts
(including handles, resources, and file system specifications) were implemented on
Windows as part of the QuickTime Media Layer. The hardest part of getting your
QuickTime code to run on Windows may simply be creating a basic application shell or
framework to hold that code. Accordingly, we'll spend a good bit of time in this article
discussing just that issue.
Movie Controllers
Before we start looking at our source code, however, let's take a minute to make clear
what it is that we want to achieve. For the moment, we'll be content to build an
application that can open and display QuickTime movies in windows on the screen. The
Windows version of our basic application will have a "frame window" that contains a
menu bar and within which we can open and display one or more movie windows.
Figure 1 shows the appearance of the application's frame window before the user has
opened any QuickTime movies.
Figure 1.The frame window of the Windows application
A movie window will contain all the standard window parts (in particular, a title bar
and close box), the movie itself, and a special set of controls called the movie
controller bar. Figure 2 shows a typical Macintosh movie window, and Figure 3 shows
a Windows version of the same movie window. Both of these windows show the standard
movie controller bar along the bottom edge of the window.
Figure 2.A movie window on the Macintosh.
Figure 3.A movie window on Windows.
The movie controller bar allows the user to control the playback of the movie and to
navigate within it. For instance, the user can use the fast-forward button to play the
movie forward at an accelerated rate. Or, the user can drag the position thumb to set
the current location in the movie.
Some kinds of QuickTime movies use a different movie controller bar. For instance,
QuickTime VR movies are not typically played frame-by-frame in a linear fashion.
For these movies, you'll see the movie controller bar shown in Figure 4, which
contains controls that allow the user to zoom in and out and to perform other
operations on the movie.
Figure 4.The movie controller bar for a QuickTime VR movie.
The movie controller bar is created and managed by a software component called a
movie controller component (or, more briefly, a movie controller). Now here's the
really fun part: once you've opened a QuickTime movie (using a few simple QuickTime
functions), you can call a couple more functions to create and attach a movie controller
to your movie. Thereafter, the movie controller (and not your application) draws the
movie controller bar and manages all events associated with the movie. Your
application doesn't need to know how to jump to a new location in the movie or how to
start and stop the movie playback. It simply needs to pass any events it receives to the
movie controller component before acting on them itself. The movie controller
intercepts any events that apply to it and reacts to them appropriately.
So, the first lesson we need to take to heart is this: we can get all the basic movie
playback capabilities simply by creating a movie controller, attaching it to our movie,
and then giving it the first shot at handling any events we receive. And as if that
weren't enough, the movie controller also provides an extremely easy way for us to
perform basic editing operations on movies that support them. (Not all movies support
cutting, copying, or pasting of movie segments; for instance, QuickTime VR movies do
not.)
The Application Framework
Now it's time for a bit of a detour. As mentioned earlier, QuickTime provides an
extensive set of services for handling digital media like sound, video, sprite animation,
and the like. But of course we'll need to use other services to handle the basic graphical
user interface for our QuickTime-savvy application (windows, menus, dialog boxes,
and so forth). Since you're an experienced Macintosh programmer, you're already
familiar with the ideas underlying event-driven programming on the Macintosh.
Remember, though, that we want to support QuickTime programming on both
Macintosh and Windows systems. So we'll need to address separately the issues specific
to each operating system, while trying to factor out as much code as possible to share
between the two systems.
Our general approach will go like this: we'll create two files, MacFramework.c and
WinFramework.c, that handle the basic application services that are specific to the
Macintosh and Windows operating systems, respectively. These services include
starting up and shutting down the application, handling events and messages, creating
windows, opening files dropped onto the application icon, and so forth. We won't delve
very much into these framework files in this article, since there isn't very much in
them of interest for QuickTime programmers. Suffice it to say that the Macintosh
framework would look very familiar to anyone who cut their Mac programming
eyeteeth on MacTech's "Getting Started" series (or on some similarly good source); it
uses standard event-driven programming techniques to handle the user's actions. And
the Windows framework is a very straightforward implementation of the multiple
document interface (MDI) specification defined by Microsoft for creating and managing
one or more document windows within a general frame window.
What's distinctive about MacFramework.c and WinFramework.c is that they have been
carefully designed to call functions defined in a third file, ComFramework.c, for most
QuickTime services or other services that are not system-specific. ComFramework.c
also defines a number of functions that are substantially the same on both platforms
but which may require several short platform-dependent blocks (introduced by the
compiler flags TARGET_OS_MAC and TARGET_OS_WIN32).
Keep in mind that (in this article, at least) we want to support only the most basic
playback and editing of QuickTime movies, which is exactly what is provided by the
basic framework. In future articles, however, we'll want to add other capabilities to
our applications. For instance, we'll want to handle some new menus, in addition to the
standard File and Edit menus; and we'll want to perform some application-specific
tasks at idle time (perhaps change the pan angle of a QuickTime VR movie). To make it
easy to add such capabilities, we create yet another file, called ComApplication.c,
which defines a number of functions that are called at particular times by the basic
framework. For instance, after the framework does any necessary menu adjusting for
the File and Edit menus (enabling certain menu items and disabling others), it calls
the function QTApp_AdjustMenus, defined in ComApplication.c, to allow us to adjust
any application-specific menus. Since we don't have any application-specific tasks to
perform, for the moment at least, we can ignore ComApplication.c and instead turn our
attention to the file ComFramework.c.
Handling Movie Windows
To get a taste for how our basic framework works, let's begin by considering how we
want to manage our application's movie windows. On the Macintosh, a movie window is
of type WindowPtr; on Windows, a movie window is of type HWND. To simplify the
code that handles movie windows, we define a custom type that refers to either a
Macintosh movie window or a Windows movie window, like this:
#if TARGET_OS_MAC
typedef WindowPtr [TOKEN:22377]ndowReference;
#endif
#if TARGET_OS_WIN32
typedef HWND WindowReference;
#endif
We need to maintain some information for each movie window displayed by our
application. We'll use the standard technique of defining a structure to hold this
information and allocating an instance of that structure for each open movie window.
Let's call this instance a "window object record".
WindowReference [TOKEN:26199]indow;
Movie fMovie;
MovieController fController;
FSSpec fFileFSSpec;
short fFileResID;
short fFileRefNum;
Boolean fCanResizeWindow;
Boolean fIsDirty;
Boolean fIsQTVRMovie;
QTVRInstance fInstance;
OSType fObjectType;
Handle fAppData;
} WindowObjectRecord, *WindowObjectPtr, **WindowObject;
Notice that the first field of this structure, fWindow, is of type WindowReference,
which (as you've just seen) is a WindowPtr on the Mac and an HWND on Windows. The
fMovie and fController fields identify the movie and movie controller. The next three
fields maintain information about the location of the movie file on disk and in memory.
The three fields after that indicate whether the movie window can be resized (which is
almost always true), whether the movie data has changed since it was opened or last
saved, and whether the movie is a QuickTime VR movie. If the movie is a QuickTime VR
movie, the fInstance field holds the QTVR instance associated with the movie. The
fObjectType field holds an arbitrary identifier that is unique to our application; we
use this field just to make sure that we've got a valid window object. Finally, the
fAppData field holds a handle to any application-specific data. For now, we won't need
to use this field.
When the user selects a movie file to open, we need to allocate a window object record
and attach it to the window in which the movie is opened. The standard Macintosh way
to do this is to use the SetWRefCon function to set the window's reference constant, an
application-specific 32-bit value, to the handle to the window object record. Windows
provides a similar capability to attach an application-specific 32-bit value to a
window, with the SetWindowLong function. Listing 1 shows the code we use for
creating a window object.
Listing 1: Creating a window object
QTFrame_CreateWindowObject
void QTFrame_CreateWindowObject (WindowReference theWindow)
WindowObject myWindowObject = NULL;
if (theWindow == NULL)
return;
// allocate space for a window object record and fill in some of
its fields
myWindowObject =
(WindowObject)NewHandleClear(sizeof(WindowObjectRecord));
if (myWindowObject != NULL) {
(**myWindowObject).fWindow = theWindow;
(**myWindowObject).fController = NULL;
(**myWindowObject).fObjectType = kMovieControllerObject;
(**myWindowObject).fInstance = NULL;
(**myWindowObject).fCanResizeWindow = true;
(**myWindowObject).fIsDirty = false;
(**myWindowObject).fAppData = NULL;
}
// associate myWindowObject (which may be NULL) with the window
#if TARGET_OS_MAC
SetWRefCon(theWindow, (long)myWindowObject);
#endif
#if TARGET_OS_WIN32
SetWindowLong(theWindow, GWL_USERDATA, (LPARAM)myWindowObject);
// associate a GrafPort with this window
CreatePortAssociation(theWindow, NULL, 0L);
#endif
// set the current port to the new window
MacSetPort(QTFrame_GetPortFromWindowReference(theWindow));
}
Internally, QuickTime does some of its drawing using QuickDraw, the collection of
system software routines that perform graphic operations on the user's screen (and
elsewhere). And, as you know, QuickDraw does all of its drawing within the current
graphics port. On the Macintosh, there is a very close connection between a WindowPtr
and a graphics port, but there is no such connection between Windows HWNDs and
graphics ports. So, when our application is running on Windows, we need to call the
CreatePortAssociation function to create a connection between the HWND and a
graphics port (of type GrafPtr).
Once we've called CreatePortAssociation to associate a graphics port with an HWND, we
can subsequently call the GetNativeWindowPort function to get a pointer to the