Creating a Movie Data Exchange Component

This chapter discusses the details of creating a movie data exchange component. The chapter also includes source code for two simple movie data exchange components.

You should consider creating a movie data import component if you have data that you would like to place in a QuickTime movie and there are not currently facilities for placing that type of data into a movie. Similarly, if you want to work with data from a QuickTime movie without using QuickTime, you might consider creating a movie data export component that can convert the data into a format your program can understand.

Before reading this chapter, you should be familiar with how to create components. After reading this chapter, you should understand the special requirements of these components. Note that a single component may support only import or export functions, not both.

Apple has defined component type values for movie data exchange components. You can use the following constants to specify this component type:

Apple has defined a functional interface for movie data exchange components. You can use the following constants to refer to the request codes for each of the functions that your component must support:

#define MovieImportType 'eat '                  /* movie data import */
#define MovieExportType 'spit'                  /* movie data export */
enum {
    /* movie data import components */
    kMovieImportHandleSelect                = 1,  /* import from handle */
    kMovieImportFileSelect                  = 2,  /* import from file */
    kMovieImportSetSampleDurationSelect     = 3,  /* set sample duration */
    kMovieImportSetSampleDescriptionSelect  = 4,  /* set sample description */
    kMovieImportSetMediaFileSelect          = 5,  /* set media file */
    kMovieImportSetDimensionsSelect         = 6,  /* set track dimensions */
    kMovieImportSetChunkSizeSelect          = 7,  /* set chunk size */
    kMovieImportSetProgressProcSelect       = 8,  /* set progress function */
    kMovieImportSetAuxiliaryDataSelect      = 9,  /* set additional data */
    kMovieImportSetFromScrapSelect          = 10, /* data from scrap */
    kMovieImportDoUserDialogSelect          = 11, /* invoke user dialog box */
    kMovieImportSetDurationSelect           = 12  /* set paste duration */
 
    /* movie data export components */
    kMovieExportToHandleSelect              = 128, /* export to handle */
    kMovieExportToFileSelect                = 129, /* export to file */
    kMovieExportDoUserDialogSelect          = 130, /* invoke user dialog box */
    kMovieExportGetAuxiliaryDataSelect      = 131, /* get additional data */
    kMovieExportSetProgressProcSelect '     = 132  /* set progress function */
};

Importing Movie Data

Movie data import components may provide one or two functions that allow the Movie Toolbox to request a data conversion operation. The MovieImportHandle function instructs your component to retrieve the data that is to be imported from a specified handle. The MovieImportFile function instructs you to retrieve the data from a file. You should set the appropriate flags in your component’s componentFlags field to indicate which of these functions your component supports. Note that your component may support both functions.

Before the Movie Toolbox calls one of these functions, a requesting application may call one or more of your component’s configuration functions (see Configuring Movie Data Import Components for more information about these functions). However, your component should work properly even if none of these configuration functions is called.

Exporting Movie Data

Movie data export components may provide one or two functions that allow the Movie Toolbox to request a data conversion operation. The MovieExportToHandle function instructs your component to place the converted data into a specified handle. The MovieExportToFile function instructs you to put the data into a file. You should set the appropriate flags in your component’s componentFlags field to indicate which of these functions your component supports. Note that your component may support both functions.

Before the Movie Toolbox calls one of these functions, a requesting application may call one or more of your component’s configuration functions (see Configuring Movie Data Export Components for more information about these functions). However, your component should work properly even if none of these configuration functions is called.

Summary of Constants

/* component type values */
#define MovieImportType                   'eat '    /* movie data import */
#define MovieExportType                   'spit'    /* movie data export */
 
/*  componentFlags values for movie import and movie export components */
enum {
    canMovieImportHandles                = 1,    /* can import from handles */
    canMovieImportFiles                  = 2,    /* can import from files */
    hasMovieImportUserInterface          = 4,    /* import has user interface */
    canMovieExportHandles                = 8,    /* can export to handles */
    canMovieExportFiles                  = 16,   /* can export to files */
    hasMovieExportUserInterface          = 32,   /* export has user interface */
    dontAutoFileMovieImport              = 64    /* do not automatically
                                                      import movie files */
};
 
/*  flags for MovieImportHandle and MovieImportFile */
enum {
    movieImportCreateTrack                 = 1,    /* create a new track */
    movieImportInParallel                  = 2,    /* paste imported data */
    movieImportMustUseTrack                = 4     /* use specified track */
};
 
enum {
    movieImportResultUsedMultipleTracks    = 8,    /* component used
                                                      several tracks */
};
 
enum {
    /* movie data import components */
    kMovieImportHandleSelect                = 1,  /* import from handle */
    kMovieImportFileSelect                  = 2,  /* import from file */
    kMovieImportSetSampleDurationSelect     = 3,  /* set sample duration */
    kMovieImportSetSampleDescriptionSelect  = 4,  /* set sample description */
    kMovieImportSetMediaFileSelect          = 5,  /* set media file */
    kMovieImportSetDimensionsSelect         = 6,  /* set track dimensions */
    kMovieImportSetChunkSizeSelect          = 7,  /* set chunk size */
    kMovieImportSetProgressProcSelect       = 8,  /* set progress func */
    kMovieImportSetAuxiliaryDataSelect      = 9,  /* set additional data */
    kMovieImportSetFromScrapSelect          = 10, /* data from scrap */
    kMovieImportDoUserDialogSelect          = 11, /* invoke user dialog */
    kMovieImportSetDurationSelect           = 12  /* set paste duration */
 
    /* movie data export components */
    kMovieExportToHandleSelect             = 128,  /* export to handle */
    kMovieExportToFileSelect               = 129,  /* export to file */
    kMovieExportDoUserDialogSelect         = 130,  /* invoke user dialog */
    kMovieExportGetAuxiliaryDataSelect     = 131,  /* get additional data */
    kMovieExportSetProgressProcSelect      = 132   /* set progress function */
};

Result Codes

Constant

Value

Description

invalidTrack

-2009

Specified track cannot receive imported data

unsupportedAuxiliaryImportData

-2057

Cannot work with specified handle type

badComponentSelector

0x80008002

Function not supported

A Sample Movie Import Component

This section describes how to create a movie import component. First, you implement the required functions. Then, you instruct your component to obtain the movie data from a handle or a file. This section supplies a sample program that implements a movie data exchange component that imports a Scrapbook file containing QuickDraw PICT images.

Your movie data import component may provide a user dialog box. You may use this dialog box in any way that is appropriate for your component; for example, to obtain certain parameter information governing the import operation, such as the image-compression method.

In addition, the requesting application may use one or more of the configuration functions to establish parameters for the import operation.

You should not rely on any outside configuration information. Your component should work properly knowing only the source data and the target movie. The Movie Toolbox supplies this information to your component when it calls your MovieImportHandle function or MovieImportFile function.

Your movie data import component may implement either one or both of these functions, which allow the Movie Toolbox to request that data be converted into a format for use in a QuickTime movie.

If the data is to be imported from a handle, the MovieImportHandle function is used.

If data is to be imported from a file, the MovieImportFile function is used.

Set the appropriate flags in your component’s componentFlags field to indicate which of these functions your component supports. Note that your component may support both functions.

Implementing the Required Import Component Functions

Listing 6-1 supplies a sample program that implements a movie data exchange component that imports a Scrapbook file containing QuickDraw PICT images. The sample program also provides the dispatchers for the movie import component together with the required functions.

Listing 6-1  Implementing the required import functions

#define kMediaTimeScale 600
 
typedef struct {
    ComponentInstance       self
    TimeValue               frameDuration;
} ImportScrapbookGlobalsRecord, **ImportScrapbookGlobals;
 
/* entry point for all Component Manager requests */
pascal ComponentResult ImportScrapbookDispatcher (ComponentParameters *params,
        Handle storage)
{
    OSErr err = badComponentSelector;
    ComponentFunction componentProc = 0;
    switch (params->what) {
        case kComponentOpenSelect:
            componentProc = ImportScrapbookOpen; break;
 
        case kComponentCloseSelect:
            componentProc = ImportScrapbookClose; break;
 
        case kComponentCanDoSelect:
            componentProc = ImportScrapbookCanDo; break;
 
        case kComponentVersionSelect:
            componentProc = ImportScrapbookVersion; break;
 
        case kMovieImportFileSelect:
            componentProc = ImportScrapbookFile; break;
 
        case kMovieImportSetSampleDurationSelect:
            componentProc = ImportScrapbookSetSampleDuration; break;
    }
 
    if (componentProc)
        err = CallComponentFunctionWithStorage (storage,
     params,
          componentProc);
    return err;
}
 
pascal ComponentResult ImportScrapbookCanDo
                 (ImportScrapbookGlobals storage, short ftnNumber)
{
    switch (ftnNumber) {
        case kComponentOpenSelect:
        case kComponentCloseSelect:
        case kComponentCanDoSelect:
        case kComponentVersionSelect:
        case kMovieImportFileSelect:
        case kMovieImportSetSampleDurationSelect:
            return true;
        default:
            return false;
    }
}
 
pascal ComponentResult ImportScrapbookVersion
                                        (ImportScrapbookGlobals storage)
{
    return 0x00010001;
}
 
pascal ComponentResult ImportScrapbookOpen
                                    (ImportScrapbookGlobals storage,
                                     ComponentInstance self)
{
    storage = (ImportScrapbookGlobals) NewHandleClear
                            (sizeof (ImportScrapbookGlobalsRecord));
    if (!storage) return MemError();
    (**storage).self = self;
    SetComponentInstanceStorage (self, (Handle)storage);
    return noErr;
}
 
pascal ComponentResult ImportScrapbookClose
                                         (ImportScrapbookGlobals storage,
                                             ComponentInstance self)
{
    if (storage) DisposeHandle((Handle)storage);
    return noErr;
}

Importing a Scrapbook File

Before the import operation begins, the client may set the duration of samples to be added by the movie data import component by calling the MovieImportSetDuration function.

The MovieImportFile function performs the import operation. The tasks involved in importing the data include

  • opening the source file

  • retrieving the first sample in order to determine the track dimension

  • creating a new track and media

  • determining the frame duration

  • setting up a sample description structure

  • cycling through all the frames in the Scrapbook and adding them to the new media

  • adding the new media to the track

  • closing the source file

Listing 6-2 supplies an example in which a Scrapbook file is imported.

Listing 6-2  Importing a Scrapbook file

/* If this function is called, it provides a hint from the caller as to the
desired sample (frame) duration in the new media */
 
pascal ComponentResult ImportScrapbookSetSampleDuration
                                         (ImportScrapbookGlobals storage,
                                            TimeValue duration,
                                            TimeScale scale)
{
    TimeRecord tr;
    tr.value.lo = duration;
    tr.value.hi = 0;
    tr.scale = 0;
    tr.base = nil;
    ConvertTimeScale (&tr, kMediaTimeScale);
                    /* your new media will have a time scale of 600 */
    (**storage).frameDuration = tr.value.lo;
 
    return noErr;
}
pascal ComponentResult ImportScrapbookFile
                                    (ImportScrapbookGlobals storage,
                                     FSSpec *theFile, Movie theMovie,
                                     Track targetTrack, Track *usedTrack,
                                     TimeValue atTime,
                                     TimeValue *addedTime,
                                     long inFlags, long *outFlags)
{
    OSErr err;
    short resRef = 0, saveRes = CurResFile();
    PicHandle thePict;
    Rect trackRect;
    short pageIndex = 0;
    Track newTrack = 0;
    Media newMedia;
    Boolean endMediaEdits = false;
    TimeValue frameDuration;
    SampleDescriptionHandle sampleDesc = 0;
 
    *outFlags = 0;
    if (inFlags & movieImportMustUseTrack)
        return invalidTrack;
 
    /* open source file */
    resRef = FSpOpenResFile (theFile, fsRdPerm);
    if (err = ResError()) goto bail;
    UseResFile(resRef);
 
    /* get the first PICT to determine the track size */
    thePict = (PicHandle)Get1IndResource ('PICT', 1);
    if (!thePict) {
        err = ResError();
        goto bail;
    }
    trackRect = (**thePict).picFrame;
    OffsetRect(&trackRect, -trackRect.left, -trackRect.top);
 
    /* create a track and PICT media */
    newTrack = NewMovieTrack (theMovie, trackRect.right << 16,
                                     trackRect.bottom << 16, kNoVolume);
    if (err = GetMoviesError()) goto bail;
    newMedia = NewTrackMedia (newTrack, 'PICT', kMediaTimeScale, nil, 0);
    if (err = GetMoviesError()) goto bail;
    if (err = BeginMediaEdits (newMedia)) goto bail;
    endMediaEdits = true;
 
    /* determine the frame duration (check the hint you may
        have been called with) */
    frameDuration = (**storage).frameDuration;
    if (!frameDuration) frameDuration = kMediaTimeScale/5;
                                        /* default is 1/5th second */
 
    /* set up a simple sample description */
    sampleDesc = (SampleDescriptionHandle) NewHandleClear
                                            (sizeof (SampleDescription));
    (**sampleDesc).descSize = sizeof(SampleDescription);
    (**sampleDesc).dataFormat = 'PICT';
 
    /* cycle through all source frames and add them to the media */
    do {
        Handle thePict;
        short resID = pageToMapIndex (++pageIndex,
                                      *GetResource ('SMAP', 0));
 
        if (resID == 0) break;
        thePict = Get1Resource ('PICT', resID);
        if (thePict == nil) continue;           /* some pages may not
                                                    contain a 'PICT' */
 
        err = AddMediaSample(newMedia, thePict, 0,
                                    GetHandleSize (thePict),
                                     frameDuration, sampleDesc, 1, 0, nil);
 
        ReleaseResource (thePict);
        DisposeHandle (thePict);
    } while (!err);
    if (err) goto bail;
 
    /* add the new media to the track */
    err = InsertMediaIntoTrack (newTrack, 0, 0,
                                GetMediaDuration (newMedia), kFix1);
 
bail:
    if (resRef) CloseResFile (resRef);
    if (endMediaEdits) EndMediaEdits (newMedia);
    if (err && newTrack) {
        DisposeMovieTrack (newTrack);
        newTrack = 0;
    }
    UseResFile (saveRes);
    if (sampleDesc) DisposeHandle ((Handle)sampleDesc);
    *usedTrack = newTrack;
 
    return err;
}
 
/* map from a Scrapbook page number to a resource ID */
short pageToMapIndex (short page, Ptr map)
{
    short mapIndex;
    for (mapIndex = 0; mapIndex < 256; mapIndex++)
        if (*map++ == page)
            return mapIndex | 0x8000;
    return 0;
}

A Sample Movie Export Component

As with movie data import components, the movie data export component should not rely on any configuration information beyond that which is supplied by the Movie Toolbox when it calls the MovieExportToHandle or MovieExportToFile function, respectively.

This section provides an implementation of a movie data exchange component that exports a movie or movie’s track to a PICS animation file.

Implementing the Required Export Component Functions

Listing 6-3 provides the component dispatchers for the movie export component together with the required functions.

Listing 6-3  Implementing the required export functions

typedef struct {
    ComponentInstance self;
} ExportPICSGlobalsRecord, *ExportPICSGlobals;
 
/* entry point for all Component Manager requests */
pascal ComponentResult ExportPICSDispatcher
                                        (ComponentParameters *params,
                                         Handle storage)
{
    OSErr err = badComponentSelector;
    ComponentFunction componentProc = 0;
 
    switch (params->what) {
        case kComponentOpenSelect:
            componentProc = ExportPICSOpen; break;
        case kComponentCloseSelect:
            componentProc = ExportPICSClose; break;
        case kComponentCanDoSelect:
            componentProc = ExportPICSCanDo; break;
        case kComponentVersionSelect:
            componentProc = ExportPICSVersion; break;
        case kMovieExportToFileSelect:
            componentProc = ExportPICSToFile; break;
    }
    if (componentProc)
        err = CallComponentFunctionWithStorage (storage, params,
                                                componentProc);
    return err;
}
 
pascal ComponentResult ExportPICSCanDo (ExportPICSGlobals store,
                                                     short ftnNumber)
{
    switch (ftnNumber) {
        case kComponentOpenSelect:
        case kComponentCloseSelect:
        case kComponentCanDoSelect:
        case kComponentVersionSelect:
        case kMovieExportToFileSelect:
            return true;
            break;
        default:
            return false;
            break;
    }
}
 
pascal ComponentResult ExportPICSVersion (ExportPICSGlobals store)
{
    return 0x00010001;
}
 
pascal ComponentResult ExportPICSOpen (ExportPICSGlobals store,
                                                     ComponentInstance self)
{
    OSErr err;
    store = (ExportPICSGlobals) NewPtrClear
                (sizeof(ExportPICSGlobalsRecord));
    if (err = MemError()) goto bail;
    store->self = self;
    SetComponentInstanceStorage(self, (Handle)store);
 
bail:
    return err;
}
 
pascal ComponentResult ExportPICSClose (ExportPICSGlobals store,
                                        ComponentInstance self)
{
    if (store) DisposPtr((Ptr)store);
    return noErr;
}

Exporting Data to a PICS File

To export data to a PICS file, your component must allow the Movie Toolbox to call the MovieExportToFile function in order to export movie data into a file read the data from the track or movie perform appropriate conversions on that data place the data into the specified file (the file’s type corresponds to the component subtype of your movie data export component)

Listing 6-4 provides an implementation of these tasks in a movie export component. The

ExportPICSToFile function performs the export operation by opening the resource fork of the PICS file and cycling through the movie time segment, adding a frame to the PICS file.

Listing 6-4  Exporting a frame of movie data to a PICS file

pascal ComponentResult ExportPICSToFile (ExportPICSGlobals store,
                                                        const FSSpec *theFile,
                                                        Movie m,
                                                        Track onlyThisTrack,
                                                        TimeValue startTime,
                                                        TimeValue duration)
{
    OSErr err = noErr;
    short resRef = 0;
    short saveResRef = CurResFile();
    short resID = 128;
    PicHandle thePict = nil;
 
    /* open the resource fork of the PICS file
        (the caller is responsible for creating the file) */
    resRef = FSpOpenResFile (theFile, fsRdWrPerm);
    if (err = ResError()) goto bail;
 
    UseResFile(resRef);
 
    /* cycle through the movie time segment you were given */
    while (startTime < duration) {
        Byte c = 0;
 
        if (onlyThisTrack)
            thePict = GetTrackPict (onlyThisTrack, startTime);
        else
            thePict = GetMoviePict(m, startTime);
        if (!thePict) continue;
 
        /* add a frame to the PICS file */
        AddResource ((Handle)thePict, 'PICT', resID++, &c);
        err = ResError();
        WriteResource ((Handle)thePict);
        DetachResource ((Handle)thePict);
        KillPicture (thePict);
        thePict = nil;
        if (err) break;
 
        /* find the time of the next frame */
        do {
            TimeValue nextTime;
            if (onlyThisTrack)
                GetTrackNextInterestingTime (onlyThisTrack,
                                             nextTimeMediaSample, startTime,
                                             kFix1, &nextTime, nil);
            else {
                OSType mediaType = VisualMediaCharacteristic;
 
                GetMovieNextInterestingTime (m, nextTimeMediaSample,
                                                        1, &mediaType,
                                                        startTime, kFix1,
                                                        &nextTime, nil);
            }
 
            if (GetMoviesError ()) goto bail;
            if (nextTime != startTime) {
                startTime = nextTime;
                break;
            }
        } while (++startTime < duration);
    }
 
bail:
    if (thePict) KillPicture (thePict);
    if (resRef) CloseResFile (resRef);
    UseResFile (saveResRef);
    return err;
}

Save-and-Restore Component Routines

If you are writing movie data exchange components, and would like your components' settings to be saved and restored, you need to implement two additional component routines in order to allow your components to have their settings saved and restored. A component’s settings are stored in a QuickTime QT atom container. The data stored in the QT atom container is private to the particular component but should be stored so that it is possible to read the data on all platforms supported by QuickTime, thus allowing the same settings to be used anywhere.

For each type of movie data exchange component, there is one routine to return a QT atom container holding the settings and another routine to configure the component from previously-saved settings. For more information about QT atom containers, see QuickTime Movie Basics.

Import component developers need to implement the MovieImportGetSettingsAsAtomContainer and MovieImportSetSettingsFromAtomContainer routines. For MovieImportGetSettingsAsAtomContainer, the component should allocate a new QT atom container, stuff current settings into it, and return it to the caller. For MovieImportSetSettingsFromAtomContainer, the component should accept a QT atom container, extract the settings in which it is interested, and change its internal state.

Export component developers need to implement the MovieExportGetSettingsAsAtomContainer and MovieExportSetSettingsFromAtomContainer routines. Like import components, the component’s MovieExportGetSettingsAsAtomContainer routine allocates and returns a QT atom container holding the component’s settings. For MovieExportSetSettingsFromAtomContainer, the component accepts a QT atom container, extracts the settings, and updates its internal state.

Settings Container Format and Guidelines

The particular atoms stored within the component’s settings QT atom container are private to that component type. However, there are some guidelines that need to be followed. These include:

In all SetSettingsFromAtomContainer routines, the QT atom container belongs to the caller. The component should not dispose of the passed QT atom container.

The settings QT atom container should contain one or more top-level atoms. These top-level atoms can contain either leaf data or other atoms. Each atom has both a type (QTAtomType) and an ID. Choosing an atom type that is mnemonic is helpful in indicating how it is used. For example, QuickTime stores video compression settings in atoms of type 'vide'. Sound compression settings are stored in 'soun' atoms. The text components use 'text' for their atom types.

Several of QuickTime’s export components use the standard compression component to allow the user to configure compression settings for exported files. When one of these components is asked to return its settings atom container, the export component first requests that the standard compression component return its settings using the SCGetSettingsAsAtomContainer function described above. To the QT atom container it receives, the export component adds any of its own settings. When the export component’s SetSettingsFromAtomContainer is called, the exporter calls

SCSetSettingsFromAtomContainer with the passed atom container. The standard compression component extracts only those settings it expects, ignoring all other, and configures itself. The exporter then looks for its own settings in the same atom container and configures itself.

This is possible because the standard compression and data exchange components both use QT atom containers to hold their settings. Because many third-party developers do the same, there must be a mechanism so that QuickTime’s own top-level atom types and those of third parties don’t collide. To achieve this, Apple Computer reserves all top-level atom types consisting exclusively of lowercase letters with or without numerals. For example, 'vide' is reserved by Apple, but 'Vide' is not. There is no restriction on the atom types for atoms stored within these top-level atoms.

Apple recommends that you store all of your component settings under a single top-level atom. However, there is no requirement to do so.

The data within an atom should be stored in a canonical form on all platforms. It should be always in big-endian format or always in little-endian format. Different types of atoms can be stored in different endian orders but for a single type of atom, it should always use the same order. This allows the settings to be created in the Mac OS and read in Windows or vice-versa.

In either MovieImportSetSettingsFromAtomContainer or MovieExportSetSettingsFromAtomContainer, you should not necessarily expect all atoms to be included in the atom container you receive. This allows another developer, for example, to create a settings atom container, add atoms and data for only those parts of the settings that should be changed, and then pass this incomplete atom container to the component. The component then only changes those particular settings, leaving other settings alone. QuickTime’s own components use this approach.

If nil is passed for the settings to the component routines, return paramErr.

If your component does not have configurable settings, you do not need to implement the settings-related routines.

Registering Movie Data Export Components

QuickTime allows more than one export ('spit') component to be registered for the same type of file and the same export source (the movie or the particular track type). This is accomplished in a way that preserves compatibility with third-party components that may have already been written using the former rules.

The Registration Mechanism

QuickTime provides a movie export component routine that returns the same information that would have been previously stored in the componentManufacturer field of the registered 'spit' components. An export-specific component flag indicates that the export component implements the new protocol. By implementing the routine, the export component’s componentManufacturer field can be used to differentiate components.

The routine is MovieExportGetSourceMediaType. This routine returns an OSType value through its mediaType parameter, which is interpreted in exactly the same way that the componentManufacturer was previously interpreted. If the export component requires a particular type of track to exist in a movie, it returns that media handler type (e.g., VideoMediaType, SoundMediaType, etc.) through the mediaType argument. If the export component works for an entire movie, it returns 0 through this parameter.

EXTERN_API( ComponentResult )
MovieExportGetSourceMediaType (MovieExportComponent  ci, OSType * mediaType);

The following component flag indicates that this routine is implemented:

movieExportMustGetSourceMediaType = 1L << 19,

If you implement the MovieExportGetSourceMediaType routine, you must register the component with this flag. Otherwise, the Movie Toolbox will not know to call the routine and will assume the older semantics for the componentManufacturer field.

Using this mechanism does not replace the need for implementing Validate in your export components. The mechanism is only used to find candidate components.

Export Registration Mechanism

Both the Movie and the DVC export components use the export registration mechanism. The components are registered as shown below.

componentType  'spit'
componentSubType  'MooV'
componentManufacturer  'appl'
componentFlags  canMovieExportFiles  + canMovieExportFromProcedures  +
                hasMovieExportUserInterface   + canMovieExportValidateMovie
                + movieExportMustGetSourceMediaType
 
componentType 'spit'
componentSubType   'dvc!'
componentManufacturer 'appl'
componentFlags  canMovieExportFiles  + canMovieExportFromProcedures
                + hasMovieExportUserInterface  + canMovieExportValidateMovie
                + movieExportMustGetSourceMediaType

Because the DVC component uses the QuickTime Movie export component, it searches for the 'MooV' exporter, using the following ComponentDescription values:

cd.componentType = 'spit';
cd.componentSubType = MovieFileType;
cd.componentManufacturer = 'appl';
cd.componentFlags = canMovieExportFromProcedures
                    + movieExportMustGetSourceMediaType;
cd.componentFlagsMask = cd.componentFlags;

If you are working with export components (either writing them, or trying to enumerate or otherwise match up components with source media types) you need to understand this registration mechanism.

Implementing Movie Data Export Components

You can implement a movie data export component by calling the MovieExportFromProceduresToDataRef function.

Because many existing applications expect to be able to perform an export operation from a movie or track, export components should support MovieExportToFile, MovieExportFromProceduresToDataRef and MovieExportToDataRef.

Listing 6-5 shows how to implement MovieExportToFile so that it simply calls MovieExportToDataRef.

Listing 6-5  Calling MovieExportToDataRef from MovieExportToFile

pascal ComponentResult MovieExportToFile(Globals store, const FSSpec *theFile, Movie m, Track onlyThisTrack, TimeValue startTime, TimeValue duration) { ComponentResult err;
     AliasHandle alias;
     err = QTNewAlias (theFile, &alias, true);
     err = MovieExportToDataRef(store->self, (Handle)alias, rAliasType, m, onlyThisTrack, startTime,
           duration);
     DisposeHandle((Handle)alias); }

Listing 6-6 shows how to use the utility routines provided by the QuickTime movie data export component to implement MovieExportToDataRef by calling MovieExportFromProceduresToDataRef. Your implementation may differ, depending on the types of data to be exported. For example, the number and type of data sources created may change. This example creates a single sound data source, and is appropriate for any movie data export component that exports audio only.

Listing 6-6  Calling MovieExportFromProceduresToDataRef from MovieExportToDataRef

pascal ComponentResult MovieExportToDataRef(Globals store, Handle dataRef,
  OSType dataRefType, Movie m, Track onlyThisTrack, TimeValue startTime,
  TimeValue duration) {
     ComponentResult err;
     ComponentDescription cd;
     ComponentInstance ci;
     TimeScale scale;
     MovieExportGetPropertyUPP getPropertyProc = nil;
     MovieExportGetDataUPP getDataProc = nil;
     void *refCon;
     long trackID;
     cd.componentType = MovieExportType;
     cd.componentSubType = 'MooV';
     cd.componentManufacturer = 0;
     cd.componentFlags = canMovieExportFromProcedures;
     cd.componentFlagsMask = canMovieExportFromProcedures;
     err = OpenAComponent(FindNextComponent(nil, &cd), &ci);
     err = MovieExportNewGetDataAndPropertiesProcs(ci, SoundMediaType,
           &scale, m, onlyThisTrack,  startTime, duration, &getPropertyProc,
           &getDataProc, &refCon);
     err = MovieExportAddDataSource(store->self, SoundMediaType, scale,
           &trackID, getPropertyProc, getDataProc, refCon);
     err = MovieExportFromProceduresToDataRef(store->self, dataRef, dataRefType);
}

The code in Listing 6-6 retrieves default property and data procedures, instead of providing them, by using the QuickTime Movie export component. It also must dispose of these procedures. They take care of interpreting the tracks and returning media properties and data.