Sep 00 QTToolkit
Volume Number: 16
Issue Number: 9
Column Tag: QuickTime Toolkit
The Atomic Cafe
by Tim Monroe
Working With Atoms and Atom Containers
In the past two QuickTime Toolkit articles, we've been concerned at least in part with
atoms, the basic building-blocks of QuickTime movie files. Atoms are utterly simple
in structure (a 4-byte length field, followed by a 4-byte type field, followed by some
data), and this utter simplicity means that atoms can be used for a very wide range of
tasks. Indeed, the atom-based structure used by QuickTime movie files is so general
and so flexible that it has been adopted by the International Standards Organization
(ISO) as the basis for the development of a unified digital media storage format for the
emerging MPEG-4 specification.
In this article, we're going to continue investigating the basic structure of QuickTime
files as sequences of atoms. You might recall that in the previous article we left some
unfinished business lying around. Namely, we need to see how to replace an existing
atom of a particular type instead of just adding a new atom of that type. It turns out
that handling this task in the general case is reasonably difficult, since we can't safely
move the movie data atom around in a movie file without doing a lot of work. For the
present, we'll be content to see how to amend the QTInfo_MakeFilePreview function we
developed last time so that there is at most one 'pnot' atom in a QuickTime movie file.
Because an atom can contain any kind of data whatsoever, it can contain data that
consists of one or more other atoms. So, atoms can be arranged hierarchically. We'll
take a few moments to consider the hierarchical arrangement of a movie atom (the
main repository of bookkeeping information in a QuickTime movie file). Then we'll
show how to put our atom-fusing powers to work to create a shortcut movie file, a
QuickTime movie file that does nothing more than point to some other QuickTime movie
file.
Once we've played with atoms for a while, we're going to shift gears rather abruptly to
consider a second kind of atom-based structure, which we'll call an atom container.
Atom containers are structures of type QTAtomContainer that are often used inside of
QuickTime movie data atoms to store various kinds of information (for example, media
samples). They were developed primarily to address some of the shortcomings of
atoms. In particular, the Movie Toolbox provides an extensive API for working with
atom containers; among other things, this API makes it easy to create and access data
arranged hierarchically within atom containers.
We'll get some hands-on experience with atom containers in two ways. First, we'll see
how to get and set the user's Internet connection speed preference, which is a piece of
information that QuickTime stores internally and happily gives, in the form of an atom
container, to anyone who asks. Second, we'll see how to add a movie track to a
QuickTime movie. By using movie tracks, we can embed entire QuickTime movies
inside of other QuickTime movies. The movie media handler, which manages movie
tracks, is one of the most exciting new features of QuickTime 4.1. Once we understand
how to work with atom containers, it'll be easy to add movie tracks to an existing
QuickTime movie.
Before we begin, though, a word about terminology. As you've been warned, this article
is going to discuss two different ways of organizing data, both of which are (for better
or worse) called "atoms". The first kind of atom is the one that's used to define the
basic structure of QuickTime movie files. The second kind of atom is the one that was
introduced in QuickTime 2.1 for storing data in some kinds of media samples (and for
other tasks as well); these kinds of atoms are structures of type QTAtom that are
stored inside of an atom container.
Some of Apple's QuickTime documentation refers to the first kind of atom as a classic
atom (perhaps in the same spirit that one refers to a classic car: it's been around a
while) and to the second kind of atom as a QT atom (drawing of course on the data type
QTAtom). Some other documentation refers to the first kind of atom as a chunk atom
(perhaps because it's just a chunk of data?). I'm not particularly happy with any of
these terms, so I'm going to refer to the first kind of atom simply as an atom and to the
second kind as an atom container atom. In other words, an atom container atom (of type
QTAtom) is always found inside of an atom container (of type QTAtomContainer).
Generally, here and in the future, the context will make it clear which kind of atom
we're considering, so we can usually get by just talking about atoms.
File Previews: The Sequel
In the previous article ("The Informant" in MacTech, July 2000), we saw how to add a
'pnot' atom to a QuickTime movie file, to create a single-fork movie file with a file
preview. (A file preview is the movie clip, image, text, or other data that appears in
the preview pane of the file-opening dialog box displayed by a call to
StandardGetFilePreview or NavGetFile.) Our strategy was simple: each time the user
saves a movie, add a preview atom and (if necessary) a preview data atom to the
QuickTime movie file. But we recognized that ultimately we would need to refine this
strategy to avoid ending up with multiple preview atoms and preview data atoms. It's
time to make some changes to our QTInfo application. In this section, we'll see how to
upgrade QTInfo into a nearly-identical application, called QTInfoPlus, that handles file
preview atoms correctly.
Removing Existing Previews
In fact, we can solve this little problem by adding a single line of code to the
QTInfo_MakeFilePreview function. Immediately after determining that the file
reference number passed to QTInfo_MakeFilePreview picks out a data file, we can
execute this code:
QTInfo_RemoveAllPreviewsFromFile(theRefNum);
The QTInfo_RemoveAllPreviewsFromFile function looks through the specified open
data file and removes any existing preview atoms (that is, atoms of type 'pnot') from
that file. In addition, this function removes any preview data atoms referenced by
those preview atoms, unless the preview data atoms are of type 'moov'. (We don't want
to remove atoms of type 'moov', of course, since they contain essential information
about the movie file.) QTInfo_RemoveAllPreviewsFromFile is defined in Listing 1.
Listing 1: Removing all preview atoms from a QuickTime movie file
QTInfo_RemoveAllPreviewsFromFile
OSErr QTInfo_RemoveAllPreviewsFromFile (short theRefNum)
long myAtomType = 0L;
short myAtomIndex = 0;
short myCount = 0;
OSErr myErr = noErr;
// count the preview atoms in the file
myCount = QTInfo_CountAtomsOfTypeInFile(theRefNum, 0L,
ShowFilePreviewComponentType);
// get the preview data atom targeted by this preview atom
myAtomType = ShowFilePreviewComponentType;
myAtomIndex = myCount;
myErr = QTInfo_FindPreviewAtomTarget(theRefNum,
&myAtomType, &myAtomIndex);
// if the preview data atom is the last atom in the file,
remove it
// (unless it's a 'moov' atom)
if (myErr == noErr)
if (myAtomType != MovieAID)
if (QTInfo_IsLastAtomInFile(theRefNum,
myAtomType, myAtomIndex))
QTInfo_RemoveAtomFromFile(theRefNum,
myAtomType, myAtomIndex);
// remove or free the preview atom
if (QTInfo_IsLastAtomInFile(theRefNum,
ShowFilePreviewComponentType, myCount))
QTInfo_RemoveAtomFromFile(theRefNum,
ShowFilePreviewComponentType, myCount);
else
QTInfo_FreeAtomInFile(theRefNum,
ShowFilePreviewComponentType, myCount);
// if the preview data atom still exists, remove or free it
(unless it's a 'moov' atom)
if (myErr == noErr)
if (myAtomType != MovieAID)
if (QTInfo_IsLastAtomInFile(theRefNum,
myAtomType, myAtomIndex))
QTInfo_RemoveAtomFromFile(theRefNum,
myAtomType, myAtomIndex);
else
QTInfo_FreeAtomInFile(theRefNum,
myAtomType, myAtomIndex);
myCount-;
}
return(myErr);
}
As you can see, QTInfo_RemoveAllPreviewsFromFile calls a handful of other functions
defined by our application. These other functions do things like count the number of
existing preview atoms, find the preview data atom that is the target of a preview
atom, determine whether a given atom is the last atom in the file, and so forth.
QTInfo_RemoveAllPreviewsFromFile puts these functions to work like this: for each
preview atom in the file (starting with the one nearest the end of the file), find the
preview data atom that is referenced by the preview atom. If that target atom isn't a
movie atom and it's the last atom in the file, remove it from the file. Then, if the
preview atom is the last atom in the file, remove it as well. If the preview atom isn't
the last atom in the file, then change it into a free atom (that is, an atom whose type is
FreeAtomType). By changing the atom type, we're converting the preview atom into a
block of unused space at its current location in the movie file. QuickTime simply
ignores any atoms of type FreeAtomType that it encounters when reading through a
movie file.
You might think that we could just remove a preview atom from the file and, if it isn't
the last atom in the file, move any following atoms up in the file. This would avoid
creating "islands" of unused space in our file, but it would be a dangerous thing to do.
That's because some atoms in a QuickTime file reference data in other atoms by storing
offsets from the beginning of the file. In general, we want to avoid moving atoms
around in a QuickTime movie file. It's safer just to convert any unwanted atoms that
are not at the end of the file into free atoms.
Once we've removed a preview atom (if it's the last atom in the file) or converted it
into a free atom (if it isn't), we then look once again to see if the preview data atom is
the last item in the file. This might happen if the preview data atom originally
preceded the preview atom and the preview atom was the last atom in the file. If the
preview data atom is now the last atom in the file, it's removed; otherwise, it's
converted into a free atom.
The net result of all this is to remove any existing preview and preview data atoms
from the file, either by truncating the file to exclude those atoms or by converting
them into free atoms. At this point, QTInfo_MakeFilePreview can safely add a new
preview atom and (if necessary) a preview data atom to that file. So, when all is said
and done, the QuickTime movie file will end up with exactly one preview atom and one
preview data atom. In the next few subsections, we'll consider how to define the
various QTInfoPlus functions called by QTInfo_RemoveAllPreviewsFromFile.
Finding and Counting Atoms
The most fundamental thing we need to be able to do, when working with a file that's
composed of atoms, is find an atom of a specific type and index in that file. For
instance, we might need to find the first movie atom, or the third preview atom, or the
third 'PICT' atom in the file. So we want to devise a function that takes an atom type and
an index and then returns to us the position in the file at which that atom begins, if
there is an atom of that type and index in the file. Otherwise, the function should
return some error.
This task is reasonably straightforward. All we need to do is start at the beginning of
the file (or at some other offset in the file specified by the caller) and inspect the type
of the atom at that location. If the desired index is 1 and the desired atom type is the
type of that atom, we're done: we've found the desired atom. Otherwise, we need to keep
looking. We can find the next atom in the file by moving forward in the file by the size
of the atom currently under consideration. We continue inspecting each atom and
moving forward in the file until we find the atom of the specified type and index or
until we reach the end of the file. Listing 2 defines the function
QTInfo_FindAtomOfTypeAndIndexInFile, which is our basic atom-finding tool.
Listing 2: Finding an atom in a QuickTime movie file
QTInfo_FindAtomOfTypeAndIndexInFile
OSErr QTInfo_FindAtomOfTypeAndIndexInFile (short theRefNum,
long *theOffset, long theAtomType, short
theIndex,
long *theDataSize, Ptr *theDataPtr)
{
short myIndex = 1;
long myFileSize;
long myFilePos = 0L;
long myAtomHeader[2];
long mySize = 0L;
OSType myType = 0L;
Ptr myDataPtr = NULL;
Boolean isAtomFound = false;
OSErr myErr = paramErr;
if (theOffset == NULL)
goto bail;
if (QTInfo_IsRefNumOfResourceFork(theRefNum))
goto bail;