This chapter discusses QuickTime atoms, a basic structure for storing information in QuickTime. It also describes the functions used to create, dispose of, read from, and store to QuickTime atom containers. The QT atom is an enhancement of the existing atom data structure. Most QuickTime data structures (movies, tracks, media) are built of atoms. Newer QuickTime data structures (timecode tracks, sprites) are implemented using QT atoms.
Atom Structures and IDs
Creating and Disposing of Atom Containers
Creating New Atoms
Copying Existing Atoms
Retrieving Atoms From an Atom Container
Modifying Atoms
Removing Atoms From an Atom Container
Creating and Modifying QT Atom Containers
Retrieving Atoms and Atom Data
Constants for QT Atom Functions
A QT atom container is a basic memory structure for storing information in QuickTime. You can use a QT atom container to construct arbitrarily complex hierarchical data structures. You can think of a newly-created QT atom container as the root of a tree structure that contains no children. A QT atom container contains QT atoms (Figure 7-1). Each QT atom contains either data or other atoms. If a QT atom contains other atoms, it is a parent atom and the atoms it contains are its child atoms. If a QT atom contains data, it is called a leaf atom.
Each QT atom has an offset that describes the atom’s position within the QT atom container. In addition, each QT atom has a type and an ID. The atom type describes the kind of information the atom represents. The atom ID is used to differentiate child atoms of the same type with the same parent; an atom’s ID must be unique for a given parent and type. In addition to the atom ID, each atom has a 1-based index that describes its order relative to other child atoms of the same parent. You can uniquely identify a QT atom in three ways:
by its offset within its QT atom container
by its parent atom, type, and index
by its parent atom, type, and ID
You can store and retrieve atoms in a QT atom container by index, ID, or both. For example, to use a QT atom container as a dynamic array or tree structure, you can store and retrieve atoms by index. To use a QT atom container as a database, you can store and retrieve atoms by ID. You can also create, store, and retrieve atoms using both ID and index to create an arbitrarily complex, extensible data structure.
Warning: Since QT atoms are offsets into a data structure, they can be changed during editing operations on QT atom containers, such as inserting or deleting atoms. For a given atom, editing child atoms is safe, but editing sibling or parent atoms invalidates that atom’s offset.
Figure 7-2 shows a QT atom container that has two child atoms. The first child atom (offset = 10) is a leaf atom that has an atom type of 'abcd'
, an ID of 1000, and an index of 1. The second child atom (offset = 20) has an atom type of 'abcd'
, an ID of 900, and an index of 2. Because the two child atoms have the same type, they must have different IDs. The second child atom is also a parent atom of three atoms.
The first child atom (offset = 30) has an atom type of 'abcd'
, an ID of 100, and an index of 1. It does not have any children, nor does it have data. The second child atom (offset = 40) has an atom type of 'word'
, an ID of 100, and an index of 1. The atom has data, so it is a leaf atom. The second atom (offset = 40) has the same ID as the first atom (offset = 30), but a different atom type. The third child atom (offset = 50) has an atom type of 'abcd'
, an ID of 1000, and an index of 2. Its atom type and ID are the same as that of another atom (offset = 20) with a different parent.
As a developer, you do not need to parse QT atoms yourself. Instead, you can use the QT atom functions to create atom containers, add atoms to and remove atoms from atom containers, search for atoms in atom containers, and retrieve data from atoms in atom containers.
Most QT atom functions take two parameters to specify a particular atom: the atom container that contains the atom and the offset of the atom in the atom container data structure. You obtain an atom’s offset by calling either QTFindChildByID
or QTFindChildByIndex
. An atom’s offset may be invalidated if the QT atom container that contains it is modified.
When calling any QT atom function for which you specify a parent atom as a parameter, you can pass the constant kParentAtomIsContainer
as an atom offset to indicate that the specified parent atom is the atom container itself. For example, you would call the QTFindChildByIndex
function and pass kParentAtomIsContainer
constant for the parent atom parameter to indicate that the requested child atom is a child of the atom container itself.
Before you can add atoms to an atom container, you must first create the container by calling QTNewAtomContainer
. The code sample shown in Listing 7-1 calls QTNewAtomContainer
to create an atom container.
Listing 7-1 Creating a new atom container
QTAtomContainer spriteData; |
OSErr err |
// create an atom container to hold a sprite's data |
err=QTNewAtomContainer (&spriteData); |
When you have finished using an atom container, you should dispose of it by calling the QTDisposeAtomContainer
function. The sample code shown in Listing 7-2 calls QTDisposeAtomContainer
to dispose of the spriteData
atom container.
Listing 7-2 Disposing of an atom container
if (spriteData) |
QTDisposeAtomContainer (spriteData); |
You can use the QTInsertChild
function to create new atoms and insert them in a QT atom container. The QTInsertChild
function creates a new child atom for a parent atom. The caller specifies an atom type and atom ID for the new atom. If you specify a value of 0 for the atom ID, QTInsertChild
assigns a unique ID to the atom.
QTInsertChild
inserts the atom in the parent’s child list at the index specified by the index
parameter; any existing atoms at the same index or greater are moved toward the end of the child list. If you specify a value of 0 for the index
parameter, QTInsertChild
inserts the atom at the end of the child list.
The code sample in Listing 7-3 creates a new QT atom container and calls QTInsertChild
to add an atom. The resulting QT atom container is shown in Figure 7-3. The offset value 10 is returned in the firstAtom
parameter.
Listing 7-3 Creating a new QT atom container and calling QTInsertChild to add an atom
QTAtom firstAtom; |
QTAtomContainer container; |
OSErr err |
err = QTNewAtomContainer (&container); |
if (!err) |
err = QTInsertChild (container, kParentAtomIsContainer, 'abcd', |
1000, 1, 0, nil, &firstAtom); |
The following code sample calls QTInsertChild
to create a second child atom. Because a value of 1 is specified for the index
parameter, the second atom is inserted in front of the first atom in the child list; the index of the first atom is changed to 2. The resulting QT atom container is shown in Figure 7-4.
QTAtom secondAtom; |
FailOSErr (QTInsertChild (container, kParentAtomIsContainer, 'abcd', |
2000, 1, 0, nil, &secondAtom)); |
You can call the QTFindChildByID
function to retrieve the changed offset of the first atom that was inserted, as shown in the following example. In this example, the QTFindChildByID
function returns an offset of 20.
firstAtom = QTFindChildByID (container, kParentAtomIsContainer, 'abcd', |
1000, nil); |
Listing 7-4 shows how the QTInsertChild
function inserts a leaf atom into the atom container sprite
. The new leaf atom contains a sprite image index as its data.
Listing 7-4 Inserting a child atom
if ((propertyAtom = QTFindChildByIndex (sprite, kParentAtomIsContainer, |
kSpritePropertyImageIndex, 1, nil)) == 0) |
FailOSErr (QTInsertChild (sprite, kParentAtomIsContainer, |
kSpritePropertyImageIndex, 1, 1, sizeof(short),&imageIndex, |
nil)); |
QuickTime provides several functions for copying existing atoms within an atom container. The QTInsertChildren
function inserts a container of atoms as children of a parent atom in another atom container. Figure 7-5 shows two example QT atom containers, A and B.
The following code sample calls QTFindChildByID
to retrieve the offset of the atom in container A. Then, the code sample calls the QTInsertChildren
function to insert the atoms in container B as children of the atom in container A. Figure 7-6 shows what container A looks like after the atoms from container B have been inserted.
QTAtom targetAtom; |
targetAtom = QTFindChildByID (containerA, kParentAtomIsContainer, 'abcd', |
1000, nil); |
FailOSErr (QTInsertChildren (containerA, targetAtom, containerB)); |
In Listing 7-5, the QTInsertChild
function inserts a parent atom into the atom container theSample
. Then, the code calls QTInsertChildren
to insert the container theSprite
into the container theSample
. The parent atom is newSpriteAtomMovie
Data Types.
Listing 7-5 Inserting a container into another container
FailOSErr (QTInsertChild (theSample, kParentAtomIsContainer, |
kSpriteAtomType, spriteID, 0, 0, nil, &newSpriteAtom)); |
FailOSErr (QTInsertChildren (theSample, newSpriteAtom, theSprite)); |
QuickTime provides three other functions you can use to manipulate atoms in an atom container. The QTReplaceAtom
function replaces an atom and its children with a different atom and its children. You can call the QTSwapAtoms
function to swap the contents of two atoms in an atom container; after swapping, the ID and index of each atom remains the same. The QTCopyAtom
function copies an atom and its children to a new atom container.
QuickTime provides functions you can use to retrieve information about the types of a parent atom’s children, to search for a specific atom, and to retrieve a leaf atom’s data.
You can use the QTCountChildrenOfType
and QTGetNextChildType
functions to retrieve information about the types of an atom’s children. The QTCountChildrenOfType
function returns the number of children of a given atom type for a parent atom. The QTGetNextChildType
function returns the next atom type in the child list of a parent atom.
You can use the QTFindChildByIndex
, QTFindChildByID
, and QTNextChildAnyType
functions to retrieve an atom. You call the QTFindChildByIndex
function to search for and retrieve a parent atom’s child by its type and index within that type.
Listing 7-6 shows the sample code function SetSpriteData
, which updates an atom container that describes a sprite. For each property of the sprite that needs to be updated, SetSpriteData
calls QTFindChildByIndex
to retrieve the appropriate atom from the atom container. If the atom is found, SetSpriteData
calls QTSetAtomData
to replace the atom’s data with the new value of the property. If the atom is not found, SetSpriteData
calls QTInsertChild
to add a new atom for the property.
Listing 7-6 Finding a child atom by index
OSErr SetSpriteData (QTAtomContainer sprite, Point *location, |
short *visible, short *layer, short *imageIndex) |
{ |
OSErr err = noErr; |
QTAtom propertyAtom; |
// if the sprite's visible property has a new value |
if (visible) |
{ |
// retrieve the atom for the visible property -- |
// if none exists, insert one |
if ((propertyAtom = QTFindChildByIndex (sprite, |
kParentAtomIsContainer, kSpritePropertyVisible, 1, |
nil)) == 0) |
FailOSErr (QTInsertChild (sprite, kParentAtomIsContainer, |
kSpritePropertyVisible, 1, 1, sizeof(short), visible, |
nil)) |
// if an atom does exist, update its data |
else |
FailOSErr (QTSetAtomData (sprite, propertyAtom, |
sizeof(short), visible)); |
} |
// ... |
// handle other sprite properties |
// ... |
} |
You can call the QTFindChildByID
function to search for and retrieve a parent atom’s child by its type and ID. The sample code function AddSpriteToSample
, shown in Listing 7-7, adds a sprite, represented by an atom container, to a key sample, represented by another atom container. AddSpriteToSample
calls QTFindChildByID
to determine whether the atom container theSample
contains an atom of type kSpriteAtomType
with the ID spriteIDMovie
Data Types. If not, AddSpriteToSample
calls QTInsertChild
to insert an atom with that type and ID. A value of 0 is passed for the index
parameter to indicate that the atom should be inserted at the end of the child list. A value of 0 is passed for the dataSize
parameter to indicate that the atom does not have any data. Then, AddSpriteToSample
calls QTInsertChildren
to insert the atoms in the container theSprite
as children of the new atom. FailIf
and FailOSErr
are macros that exit the current function when an error occurs.
Listing 7-7 Finding a child atom by ID
OSErr AddSpriteToSample (QTAtomContainer theSample, |
QTAtomContainer theSprite, short spriteID) |
{ |
OSErr err = noErr; |
QTAtom newSpriteAtom; |
FailIf (QTFindChildByID (theSample, kParentAtomIsContainer, |
kSpriteAtomType, spriteID, nil), paramErr); |
FailOSErr (QTInsertChild (theSample, kParentAtomIsContainer, |
kSpriteAtomType, spriteID, 0, 0, nil, &newSpriteAtom)); |
FailOSErr (QTInsertChildren (theSample, newSpriteAtom, theSprite)); |
} |
Once you have retrieved a child atom, you can call QTNextChildAnyType
function to retrieve subsequent children of a parent atom. QTNextChildAnyType
returns an offset to the next atom of any type in a parent atom’s child list. This function is useful for iterating through a parent atom’s children quickly.
QuickTime also provides functions for retrieving an atom’s type, ID, and data. You can call QTGetAtomTypeAndID
function to retrieve an atom’s type and ID. You can access an atom’s data in one of three ways.
To copy an atom’s data to a handle, you can use the QTCopyAtomDataToHandle
function.
To copy an atom’s data to a pointer, you can use the QTCopyAtomDataToPtr
function.
To access an atom’s data directly, you should lock the atom container in memory by calling QTLockContainerMovie
Data Types. Once the container is locked, you can call QTGetAtomDataPtr
to retrieve a pointer to an atom’s data. When you have finished accessing the atom’s data, you should call the QTUnlockContainer
function to unlock the container in memory.
QuickTime provides functions that you can call to modify attributes or data associated with an atom in an atom container. To modify an atom’s ID, you call the function QTSetAtomIDMovie
Data Types.
You use the QTSetAtomData
function to update the data associated with a leaf atom in an atom container. The QTSetAtomData
function replaces a leaf atom’s data with new data. The code sample in Listing 7-8 calls QTFindChildByIndex
to determine whether an atom container contains a sprite’s visible property. If so, the sample calls QTSetAtomData
to replace the atom’s data with a new visible property.
Listing 7-8 Modifying an atom's data
QTAtom propertyAtom; |
// if the atom isn't in the container, add it |
if ((propertyAtom = QTFindChildByIndex (sprite, kParentAtomIsContainer, |
kSpritePropertyVisible, 1, nil)) == 0) |
FailOSErr (QTInsertChild (sprite, kParentAtomIsContainer, |
kSpritePropertyVisible, 1, 0, sizeof(short), visible, nil)) |
// if the atom is in the container, replace its data |
else |
FailOSErr (QTSetAtomData (sprite, propertyAtom, sizeof(short), |
visible)); |
To remove atoms from an atom container, you can use the QTRemoveAtom
and QTRemoveChildren
functions. The QTRemoveAtom
function removes an atom and its children, if any, from a container. The QTRemoveChildren
function removes an atom’s children from a container, but does not remove the atom itself. You can also use QTRemoveChildren
to remove all the atoms in an atom container. To do so, you should pass the constant kParentAtomIsContainer
for the atom
parameter.
The code sample shown in Listing 7-9 adds override samples to a sprite track to animate the sprites in the sprite track. The sample
and spriteData
variables are atom containers. The spriteData
atom container contains atoms that describe a single sprite. The sample
atom container contains atoms that describes an override sample.
Each iteration of the for
loop calls QTRemoveChildren
to remove all atoms from both the sample
and the spriteData
containers. The sample code updates the index of the image to be used for the sprite and the sprite’s location and calls SetSpriteData
, which adds the appropriate atoms to the spriteData
atom container. Then, the sample code calls AddSpriteToSample
to add the spriteData
atom container to the sample
atom container. Finally, when all the sprites have been updated, the sample code calls AddSpriteSampleToMedia
to add the override sample to the sprite track.
Listing 7-9 Removing atoms from a container
QTAtomContainer sample, spriteData; |
// ... |
// add the sprite key sample |
// ... |
// add override samples to make the sprites spin and move |
for (i = 1; i <= kNumOverrideSamples; i++) |
{ |
QTRemoveChildren (sample, kParentAtomIsContainer); |
QTRemoveChildren (spriteData, kParentAtomIsContainer); |
// ... |
// update the sprite: |
// - update the imageIndex |
// - update the location |
// ... |
// add atoms to spriteData atom container |
SetSpriteData (spriteData, &location, nil, nil, &imageIndex); |
// add the spriteData atom container to sample |
err = AddSpriteToSample (sample, spriteData, 2); |
// ... |
// update other sprites |
// ... |
// add the sample to the media |
err = AddSpriteSampleToMedia (newMedia, sample, |
kSpriteMediaFrameDuration, false); |
} |
The following functions can be used to create and modify QT atom containers:
This following functions can be used to retrieve QT Atoms and atom data. Each QT atom contains either data or other atoms.
You can pass the kParentAtomIsContainer
constant to QT atom functions that take an atom container and a parent atom as parameters. When passed in place of the parent atom, this constant indicates that the parent atom is the atom container itself.
enum { |
kParentAtomIsContainer = 0 |
}; |
The QTAtom
data type represents the offset of an atom within an atom container.
typedef long QTAtom; |
The QTAtomType
data type represents the type of a QT atom. To be valid, a QT atom’s type must have a nonzero value.
typedef long QTAtomType; |
The QTAtomID
data type represents the ID of a QT atom. To be valid, a QT atom’s ID must have a nonzero value.
typedef long QTAtomID; |
The QTAtomContainer
data type is a handle to a QT atom container. Your application never modifies the contents of a QT atom container directly. Instead, you use the functions provided by QuickTime for creating and manipulating QT atom containers.
typedef Handle QTAtomContainer; |
© 2005, 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-01-10)