Oct 00 QTToolkit
Volume Number: 16
Issue Number: 10
Column Tag: QuickTime Toolkit
Somewhere I'll Find You
by Tim Monroe
Working With Data References and Data Handlers
In previous QuickTime Toolkit articles, we've learned that a movie is composed of
tracks and that each track is associated with one specific kind of media data. During
movie playback, QuickTime uses a media handler (a component of type
MediaHandlerType) to interpret the media data and present it to the user in the
appropriate manner. For example, if the media data for some particular track consists
of compressed video data, the video media handler calls the decompressor specified in
the image description and then draws the decompressed frames in the appropriate
location. A media handler, however, does not typically concern itself with reading the
media data from the movie file (or from wherever else the media data is stored).
Instead, the media handler gets that data from a data handler (a component of type
DataHandlerType), which is responsible for reading and writing a media's data. In
other words, a data handler provides data input and output services for a media handler
and for other parts of QuickTime.
We identify a source of media data to a data handler by providing a data reference. As
we'll see shortly, QuickTime currently includes four standard data handlers. Each data
handler works with one specific sort of data reference. For instance, the URL data
handler expects data references that are handles to URLs. That is to say, a URL data
reference is a handle to a block of data that contains a NULL-terminated string of
characters. The other data handlers expect their references in other forms.
A typical movie file contains data references to its media data in a data information
atom, which is nested deep inside each track atom in the movie atom. In the previous
article ("The Atomic Café" in MacTech, August 2000), we saw that a shortcut movie
file contains a data reference atom that identifies an entire movie file. Data references
can in fact pick out any kind of data, not just media data. In general, if we want to tell
QuickTime where to find some data or where to put some data, we'll use a data
reference to do so.
In this article, we're going to see how to work with each of the four standard data
handlers. At the very simplest level, this involves nothing more than creating an
appropriate data reference and putting that reference into a file (as we did last time)
or passing it to a Movie Toolbox function like NewMovieFromDataRef. So we'll begin by
learning how to create data references and call a few of the "FromDataRef" functions.
Then we'll take a look at some of the functions that we can use to work with data
handlers directly. These are fairly low-level functions that we won't need to use very
often. Here, for fun, we'll see how to use them to write a general-purpose
asynchronous file-transfer utility that relies solely on QuickTime APIs. Finally,
toward the end of this article, we'll take a brief look at data reference extensions,
which are blocks of additional data that we can associate with a data reference to assist
QuickTime in working with the data picked out by the data reference. As we'll see, data
reference extensions can be especially useful to graphics importers, to help them
avoid having to take the time to validate some block of image data.
Our sample application this month is called QTDataRef; it illustrates how to work with
data references and data handlers. Figure 1 shows the Test menu from QTDataRef.
Figure 1. The Test menu of QTDataRef.
Data Handler Overview
When QuickTime was first released, it included only one data handler, the file data
handler, which can read and write data stored in files on a local file system. QuickTime
version 2.0 introduced the handle data handler, which allows the Movie Toolbox to
work with data stored in memory rather than in a file. QuickTime version 2.5 added
the resource data handler, which can read data stored in a file's resource fork. More
recently, QuickTime 3.0 added the URL data handler to support reading data from
locations specified using uniform resource locators (URLs).
All data handlers are components of type DataHandlerType. Data handlers are
distinguished from one another by their component subtypes. The file Movies.h defines
constants for three of the four data handler subtypes:
HandleDataHandlerSubType =
FOUR_CHAR_CODE('hndl'),
ResourceDataHandlerSubType =
FOUR_CHAR_CODE('rsrc'),
URLDataHandlerSubType =
FOUR_CHAR_CODE('url ')
And the file Aliases.h defines the constant used for the file data handler:
= FOUR_CHAR_CODE('alis')
QuickTime also includes several other data handlers that it uses for its own private
purposes and for which there is currently no public API. We won't consider these
private data handlers in this article, but you should at least know that they exist (so
that, for example, you aren't surprised if you iterate through all components of type
DataHandlerType and find more than four of them).
A data reference is a handle to a block of memory that uniquely identifies the location
of some media data for a QuickTime movie or some other data that QuickTime can
manage. QuickTime currently provides support for four types of data references, one
for each of the available data handlers. To let the cat out of the bag:
• A file data reference is a handle to an alias record that specifies a file on a
local storage volume (or on a remote storage volume mounted on the local
machine). That is to say, a file data reference is an alias handle. Because the
file data handler is of subtype rAliasType and uses alias handles as its data
references, it is often called the alias data handler. In addition, because it
originally handled files on the Macintosh hierarchical file system (HFS), it is
also sometimes called the HFS data handler.
• A handle data reference is a handle to a 4-byte block of memory that holds
a handle to some other block of data. That is to say, a handle data reference is a
handle to a handle.
• A resource data reference is a handle to an alias record to which two
pieces of information have been appended, a resource type and a resource ID.
The target data is found in the resource fork of the file specified by that alias
record, in the resource of the specified type and ID.
• A URL data reference is a handle to a NULL-terminated string of
characters that comprise the URL. The string of characters should conform to
the relevant IETF specifications; in particular, some non-alphanumeric
characters may need to be encoded using the hexadecimal equivalents of their
ASCII codes (for instance, the space character should be encoded as "%20").
In the following four sections, we'll investigate these four types of data references in
more detail. Before we begin, however, let's introduce a bit of terminology that will be
useful throughout this article. Let's call the block of memory to which a data reference
is a handle the referring data. And let's call the data picked out by the data reference
the target data (or just the target) of the data reference. So, for example, the
referring data of a URL data reference is the string of characters, and the target data of
a URL data reference is the data in the file picked out by that URL.
The File Data Handler
We can use the file data handler to open movies that are specified using a file data
reference, which is an alias handle. Listing 1 shows how to create a file data reference
for a given file.
Listing 1: Creating a file data reference
QTDR_MakeFileDataRef
Handle QTDR_MakeFileDataRef (FSSpecPtr theFile)
Handle myDataRef = NULL;
QTNewAlias(theFile, (AliasHandle *)&myDataRef, true);
return(myDataRef);
}
QTDR_MakeFileDataRef consists mainly of a call to the Movie Toolbox function
QTNewAlias, which returns, in the location specified by the second parameter, an alias
handle for the file specified by the first parameter. The third parameter is a Boolean
value that indicates whether to create a minimal or a full alias record. (A minimal
alias record contains only the minimum information needed to find a file; it's generally
much faster to find the target of a minimal alias record, so that's what we'll use here.)
We could also have used the Alias Manager functions NewAlias (for a full alias record)
or NewAliasMinimal (for a minimal alias record) to create the file data reference. The
principal advantage of using QTNewAlias is that it returns an alias handle even if the
file specified by theFile doesn't yet exist. Both NewAlias and NewAliasMinimal return
an error (fnfErr) - and do not create an alias record - if the specified file does not
exist. As we'll see below, we sometimes want to create data references for files that we
haven't yet created. (The Alias Manager does provide the
NewAliasMinimalFromFullPath function that can create alias records for files that
don't exist, but we'd rather not have to deal with full pathnames just to do that.)
Opening a Movie File
Let's consider a few examples of using file data references. First, we can pass a file
data reference to the NewMovieFromDataRef function, to achieve exactly the same
effect as calling the OpenMovieFile and NewMovieFromFile functions. Listing 2 defines
the function QTDR_GetMovieFromFile, which creates a file data reference and then
retrieves the movie in the specified movie file.
Listing 2: Opening a file specified by a file data reference
QTDR_GetMovieFromFile
Movie QTDR_GetMovieFromFile (FSSpecPtr theFile)
Movie myMovie = NULL;
Handle myDataRef = NULL;
myDataRef = QTDR_MakeFileDataRef(theFile);
NewMovieFromDataRef(&myMovie, newMovieActive, NULL,
myDataRef, rAliasType);
DisposeHandle(myDataRef);
}
return(myMovie);
}
The last two parameters to the NewMovieFromDataRef function specify, respectively,
the data reference and the data reference type. Since we are passing it a file data
reference, we set the data reference type to rAliasType. Note that we call
DisposeHandle to dispose of the data reference once we are finished using it. By the
time NewMovieFromDataRef returns, the Movie Toolbox will have made a copy of the
data reference, if necessary.
Creating a Reference Movie File
Now let's turn to a more interesting example of using file data references. We learned
in an earlier article ("Making Movies" in MacTech, June 2000) that a movie file
might not contain the media data referenced by the movie's tracks; instead, that media
data might be contained in some other file. A file that contains some media data is a
media file, and the file that contains the movie atom is the movie file. (For simplicity,
let's assume that our movies have only one track, a video track.) When the movie file
and the media file are different files, the movie file is a reference movie file (because
it refers to its media data and does not contain it). When the movie file and the media
file are the same file, the movie file is a self-contained movie file. While generally we
prefer to create self-contained movie files, it's useful to know how to create a
reference movie file. (Reference movie files can be useful, for instance, if we want to
share some media data among several QuickTime movies.)
One way to do this is to pass a file data reference to the NewTrackMedia function. Recall
that NewTrackMedia is declared essentially like this:
Media NewTrackMedia ( Track theTrack,
mediaType,
TimeScale
timeScale,
dataRef,
dataRefType);
The fourth and fifth parameters specify the data reference and the data reference type
of the file (or other container) that is to hold the media data for the specified track.
Previously, whenever we called NewTrackMedia, we passed NULL and 0 in those
parameters, to indicate that the media file should be the same as the movie file. Now
we'll create a reference movie file simply by specifying a media file that is different
from the movie file.
We're going to define a function QTDR_CreateReferenceCopy that we can use to create a
copy of an existing movie that contains the first video track in the original movie. The
copy's movie atom will be contained in one file (theDstMovieFile) and its media data
will be contained in some other file (theDstMediaFile). QTDR_CreateReferenceCopy
has this prototype:
OSErr QTDR_CreateReferenceCopy (Movie theSrcMovie,
FSSpecPtr theDstMovieFile, FSSpecPtr theDstMediaFile);