Sequence Grabber Component Functions

This chapter describes the functions that are provided by sequence grabber components. These functions are described from the perspective of an application developer. If you are developing a sequence grabber component, your component must behave as described here.

Configuring Sequence Grabber Components

Sequence grabber components provide a number of functions that allow you to establish the environment for grabbing or previewing digitized data. Before you can start a record or a preview operation, you must initialize the sequence grabber component, establish the channels that will be used, define the display environment for the operation, and determine the optimum screen position for the sequence grabber. In addition, if you are performing a record operation, you must define a destination movie file. The following sequence grabber component functions allow you to perform these tasks:

Controlling Sequence Grabber Components

Sequence grabber components provide a full set of functions that allow your application to control the preview or record operation. You can use these functions to start and stop the operation, to pause data collection, and to retrieve a reference to the movie that is created during a record operation:

Working With Sequence Grabber Characteristics

The characteristics that govern a sequence grabber operation fall into two main categories: those that apply to the sequence grabber component, and those that apply to an individual channel that has been created for the sequence grabber. Sequence grabber components provide a number of functions in each category. The following functions allow you to configure the characteristics of the sequence grabber component. See Working With Channel Characteristics for information about functions that apply to a single channel.

Working With Channel Characteristics

Sequence grabber components use channel components to obtain digitized data from external media. After you create a channel for a sequence grabber component (by calling the SGNewChannel function), you must configure that channel before you start a preview or record operation. The sequence grabber component provides a number of functions that allow you to configure the characteristics of a channel component. Several of these functions work on any channel component. This section discusses these general channel configuration functions.

In addition, sequence grabber components provide functions that are specific to the channel type. Apple currently provides three types of channel components: video channel components, sound channel components, and text channel components. See Working With Video Channels for information about the sequence grabber configuration functions that work only with video channels. See Working With Sound Channels for information about the sequence grabber configuration functions that work only with sound channels. For information about text channels, see Text Channel Components.

Here are the principal functions that help you work with sequence grabber channel charateristics:

Working With Channel Devices

Sequence grabbers provide a number of functions that allow you to determine the device that is attached to a given sequence grabber channel. These devices allow the channel component to control the digitizing equipment. For example, video channels use video digitizer components, and sound channels use sound input drivers. Your application can use these routines to present a list of available devices to the user, allowing the user to select a specific device for each channel.

You may use the SGGetChannelDeviceList function to retrieve a list of devices that may be used with a specified channel. You dispose of this device list by calling the SGDisposeDeviceList function. You can place one or more device names into a menu by calling the SGAppendDeviceListToMenu function. You can use the SGSetChannelDevice function to assign a device to a channel.

The Device List Structure

Some of these functions use a device list structure to pass information about one or more channel devices. The SGDeviceListRecord data type defines the format of the device list structure.

typedef struct SGDeviceListRecord {
    short               count;                  /* count of devices */
    short               selectedIndex;          /* current device */
    long                reserved;               /* set to 0 */
    SGDeviceName        entry[1];               /* device names */
} SGDeviceListRecord, *SGDeviceListPtr, **SGDeviceList;

Field

Description

count

Indicates the number of devices described by this structure. The value of this field corresponds to the number of entries in the device name array defined by the entry field.

selectedIndex

Identifies the currently active device. The value of this field corresponds to the appropriate entry in the device name array defined by the entry field. Note that this value is 0-relative; that is, the first entry has an index number of 0, the second's value is 1, and so on.

reserved

Reserved for Apple. Always set to 0.

entry

Contains an array of device name structures. Each structure corresponds to one valid device. The count field indicates the number of entries in this array. The SGDeviceName data type defines the format of a device name structure.

The Device Name Structure

Device list structures contain an array of device name structures. Each device name structure identifies a single device that may be used by the channel. The SGDeviceName data type defines the format of a device name structure.

typedef struct SGDeviceName {
    Str63           name;                 /* device name */
    Handle          icon;                 /* device icon */
    long            flags;                /* flags */
    long            refCon;               /* set to 0 */
    long            reserved;             /* set to 0 */
} SGDeviceName;

Field

Description

name

Contains the name of the device. For video digitizer components, this field contains the component's name as specified in the component resource. For sound input drivers, this field contains the driver name.

icon

Contains a handle to the device's icon. Some devices may support an icon, which you may choose to present to the user. If the device does not support an icon, or if you choose not to retrieve this information (by setting the sgDeviceListWithIcons flag to 0 when you call the SGGetChannelDeviceList function), this field is set to nil.

flags

Reflects the current status of the device. The sequence grabber sets these flags when you retrieve a device list. The sgDeviceNameFlagDeviceUnavailable flag is defined. When set to 1, this flag indicates that this device is not currently available.

refCon

Reserved for Apple. Always set to 0.

reserved

Reserved for Apple. Always set to 0.

Working With Video Channels

Sequence grabber components provide a number of functions that allow you to configure the grabber’s video channels. This section describes these configuration functions, which you can use only with video channels. You can determine whether a channel has a visual representation by calling the SGGetChannelInfo function. If you want to configure a sound channel, use the functions described in Working With Sound Channels. If you want to configure general attributes of a channel, use the functions described in Working With Channel Characteristics.

The SGGetSrcVideoBounds function allows you to determine the coordinates of the source video boundary rectangle. This rectangle defines the size of the source video image being captured by the video channel. You can use the SGSetVideoRect function to specify a part of the source video boundary rectangle to be captured by the channel. The SGGetVideoRect function allows you to determine the active source video rectangle.

Typically, the sequence grabber component uses the Image Compression Manager to compress the video data it captures. You can control many aspects of this image- compression process. Use the SGSetVideoCompressorType function to specify the type of image compressor to use. You can determine the type of image compressor currently in use by calling the SGGetVideoCompressorType function. You can specify a particular image compressor and set many image-compression parameters by calling the SGSetVideoCompressor function. You can determine which image compressor is being used and its parameter settings by calling the SGGetVideoCompressor function.

The channel components that supply video data to a sequence grabber component typically work with a video digitizer component. (See About Video Digitizer Components for a description of video digitizer components.)

Sequence grabber components provide functions that allow you to work with a channel’s video digitizer component. You can use the SGGetVideoDigitizerComponent function to determine which video digitizer component is supplying data to a specified channel component. You can set a channel’s video digitizer by calling the SGSetVideoDigitizerComponent function. If you change any video digitizer settings by calling the video digitizer component directly, you should inform the sequence grabber component by calling the SGVideoDigitizerChanged function.

Some video source data may contain unacceptable levels of visual noise or artifacts. One technique for removing this noise is to capture the image and then reduce it in size. During the size reduction process, the noise can be filtered out. Sequence grabber components provide functions that allow you to filter the input video data. The SGSetCompressBuffer function sets a filter buffer for a video channel. The SGGetCompressBuffer function returns information about your filter buffer.

You can work with a video channel’s frame rate by calling the SGSetFrameRate and SGGetFrameRate functions. You can control whether a channel uses an offscreen buffer by calling the SGSetUseScreenBuffer and SGGetUseScreenBuffer functions.

Working With Sound Channels

Sequence grabber components provide a number of functions that allow you to configure the grabber’s sound channels. This section describes these configuration functions, which you can use only with sound channels. You can determine whether a channel has a sound representation by calling the SGGetChannelInfo function. If you want to configure a video channel, use the functions described in Working With Video Channels. If you want to configure general attributes of a channel, use the functions described in Working With Channel Characteristics.

Use the SGSetSoundInputDriver function to specify a channel’s sound input device. You can determine a channel’s sound input device by calling the SGGetSoundInputDriver function. If you change any attributes of the sound input device, you should notify the sequence grabber component by calling the SGSoundInputDriverChanged function. By default, the sequence grabber component uses the sound driver’s best settings.

You can control the amount of sound data the sequence grabber works with at one time by calling the SGSetSoundRecordChunkSize function. You can determine this value by calling the SGGetSoundRecordChunkSize function.

You can control the rate at which the sound channel samples the input data by calling the SGSetSoundInputRate function. You can determine the sample rate by calling the SGGetSoundInputRate function.

You can control other sound input parameters by using the SGSetSoundInputParameters and SGGetSoundInputParameters functions.

Video Channel Callback Functions

Sequence grabber components allow you to define a number of callback functions in your application. The sequence grabber calls your functions at specific points in the process of collecting, compressing, and displaying the source video data. By defining callback functions, you can control the process more precisely or customize the operation of the sequence grabber component.

For example, you could use a callback function to draw a frame number on each video frame as it is collected. You could use either a compress callback function or a grab-complete callback function to accomplish this. The compress callback function is called after each frame is collected, in order to compress the frame. The grab-complete callback function is called just before the compress callback function, as soon as the frame has been captured.

The SGSetVideoBottlenecks function lets you assign callback functions to a video channel. You can use the SGGetVideoBottlenecks function to determine the callback functions that have been assigned to a video channel.

The SGSetVideoBottlenecks function accepts a video bottlenecks structure that identifies the callback functions to be assigned to the channel. In addition, the SGGetVideoBottlenecks function contains a pointer to this structure.

The video bottlenecks structure is defined by the VideoBottles data type as follows:

struct VideoBottles {
    short                       procCount;
    GrabProc                    grabProc;
    GrabCompleteProc            grabCompleteProc;
    DisplayProc                 displayProc;
    CompressProc                compressProc;
    CompressCompleteProc        compressCompleteProc;
    AddFrameProc                addFrameProc;
    TransferFrameProc           transferFrameProc;
    GrabCompressCompleteProc    grabCompressCompleteProc;
    DisplayCompressProc         displayCompressProc;
};
typedef struct VideoBottles VideoBottles;

Field

Description

procCount

Specifies the number of callback functions that may be identified in the structure. Set this field to 9.

grabProc

Identifies the grab function. If you are setting a grab function, set this field so that it points to the function's entry point. If you are not setting a grab function, set this field to nil.

grabCompleteProc

Identifies the grab-complete function. If you are setting a grab-complete function, set this field so that it points to the function's entry point. If you are not setting a grab-complete function, set this field to nil.

displayProc

Identifies the display function. If you are setting a display function, set this field so that it points to the function's entry point. If you are not setting a display function, set this field to nil.

compressProc

Identifies the compress function. If you are setting a compress function, set this field so that it points to the function's entry point. If you are not setting a compress function, set this field to nil.

compressCompleteProc

Identifies the compress-complete function. If you are setting a compress-complete function, set this field so that it points to the function's entry point. If you are not setting a compress-complete function, set this field to nil.

addFrameProc

Identifies the add-frame function. If you are setting an add-frame function, set this field so that it points to the function's entry point. If you are not setting an add-frame function, set this field to nil.

transferFrameProc

Identifies the transfer-frame function. If you are setting a transfer-frame function, set this field so that it points to the function's entry point. If you are not setting a transfer-frame function, set this field to nil.

grabCompressCompleteProc

Identifies the grab-compress-complete function. If you are setting a grab-compress-complete function, set this field so that it points to the function's entry point. If you are not setting a grab-compress-complete function, set this field to nil.

displayCompressProc

Identifies the display-compress function. If you are setting a display-compress function, set this field so that it points to the function's entry point. If you are not setting a display-compress function, set this field to nil.

The callback functions listed above are described in Application-Defined Functions.

For information about utility functions that you can use with video channel callback functions, see Utility Functions for Sequence Grabber Channel Components.

Previewing and Recording Captured Data

You can use sequence grabber components in two ways: to play digitized data for the user or to save captured data in a QuickTime movie. The process of displaying data that is to be captured is called previewing; saving captured data in a movie is called recording. You can use previewing to allow the user to prepare to make a recording. If you do so, your application can move directly from the preview operation to a record operation, without stopping the process.

Previewing

Previewing captured data involves playing that data for the user as it is captured. For video data, this means displaying the video images on the computer screen. For audio data, this means playing the sound through the computer’s sound system.

Here are the steps you must follow to preview captured data:

  1. First, you must open a connection to the sequence grabber component. Use the Component Manager’s OpenDefaultComponent or OpenComponent function.

  2. Once you have a connection to a sequence grabber component, you must configure the component for the preview operation. Use the SGSetGWorld function to set the graphics world in which the preview is to be displayed. Allocate the appropriate channels by calling the SGNewChannel function. You must call this function once for each channel to be used by the sequence grabber component. Use the SGSetChannelUsage function to specify that each channel is to be used for previewing. You can then use the appropriate channel configuration functions to prepare the channel for the preview operation. For video channels, use the functions discussed in Working With Video Channels. For sound channels, use the functions discussed in Working With Sound Channels.

  3. You start the preview operation by calling the SGStartPreview function. The sequence grabber component then begins collecting data from the channels that you have created and plays that data appropriately. You can pause and restart the preview by calling the SGPause function. Use the SGStop function to stop the preview. During the preview operation, be sure to call the SGIdle function frequently, so that the sequence grabber and its channels can perform the operation.

  4. When you are done previewing, you can start recording or close your connection to the sequence grabber component. When you close the sequence grabber component, it automatically disposes of the channels you created.

Recording

During a record operation, a sequence grabber component collects the data it captures and formats that data into a QuickTime movie. During a record operation, the sequence grabber can also play the captured data for the user. However, the sequence grabber tries to prevent the playback from interfering with the quality of the recording process.

Here are the steps you must follow to record captured data:

  1. As with a preview operation, your application must establish a connection to a sequence grabber component. Use the Component Manager’s OpenDefaultComponent or OpenComponent function.

  2. Once you have a connection to a sequence grabber component, you must configure the component for the record operation. Use the SGSetGWorld function to set the graphics world in which the data is to be displayed. Allocate the appropriate channels by calling the SGNewChannel function. You must call this function once for each channel to be used by the sequence grabber component. Use the SGSetChannelUsage function to specify that each channel is to be used for recording. At this time, you can specify whether the sequence grabber is to play that channel’s data while recording. You can then use the appropriate channel configuration functions to prepare the channel for the record operation. For video channels, use the functions discussed in Working With Video Channels. For sound channels, use the functions discussed in Working With Sound Channels.

  3. You must specify a movie file for use by the sequence grabber during the record operation. Use the SGSetDataOutput function to specify this movie file. This function also allows you to control whether the sequence grabber adds the movie resource to the movie file and whether it replaces existing data or appends the new movie to the file.

  4. You can limit the amount of data that is captured during a record operation. The SGSetMaximumRecordTime function establishes a time limit for the record operation. The SGSetChannelMaxFrames function limits the number of frames of data that the sequence grabber collects from a specific channel.

  5. You start the record operation by calling the SGStartRecord function. The sequence grabber component then begins collecting data from the channels you have created, stores the data in a QuickTime movie, and, optionally, plays that data appropriately. You can pause and restart the record process by calling the SGPause function. During the record operation, be sure to call the SGIdle function frequently, so that the sequence grabber and its channels can perform the operation. Use the SGStop function to stop recording. At this time, the sequence grabber saves the movie in your movie file, if you have chosen to do so.

  6. When you are done recording, you can go back to previewing or close your connection to the sequence grabber component. When you close the sequence grabber component, it automatically disposes of the channels you created as well as any movies it has created.

Playing Captured Data and Saving It in a QuickTime Movie

This section supplies a sample program that shows how to use a sequence grabber component to preview and record captured data. The program is divided into groups of functions that do the following tasks:

Initializing a Sequence Grabber Component

Listing 3-1 provides a sample function that creates and initializes a default sequence grabber component for a specified window (using the OpenDefaultComponent and SGInitialize functions, respectively). It then sets the graphics world of the sequence grabber component to the specified window with the SGSetGWorld function. Note that the CloseComponent function is called for housekeeping purposes in case the sequence grabber component fails.

Listing 3-1  Initializing a sequence grabber component

SeqGrabComponent MakeSequenceGrabber (WindowPtr aWindow)
{
    SeqGrabComponent anSG;
    OSErr err = noErr;
 
    /* open up the default sequence grabber */
    anSG = OpenDefaultComponent (SeqGrabComponentType, 0);
    if (anSG) {
        /* initialize the default sequence grabber component */
        err = SGInitialize (anSG);
        if (!err) {
        /* set the sequence grabber's graphics world to the
            specified window */
            err = SGSetGWorld (anSG, (CGrafPtr) aWindow, nil);
        }
    }
    if (err && anSG) {
        /* clean up on failure */
        CloseComponent (anSG);
        anSG = nil;
    }
    return anSG;
}

Creating a Sound Channel and a Video Channel

Listing 3-2 supplies a sample function that attempts to create a video channel and a sound channel for the sequence grabber component that was created in Listing 3-1. The boundaries of the video channel are set to the specifications of the bounds parameter. The channel’s usage is always set to allow previewing. If the value of the willRecord parameter is true, then the usage of the channel is set to allow recording also.

The SGNewChannel function uses the VideoMediaType constant to create a video channel and the SoundMediaType constant to create a sound channel. The SGSetChannelBounds function specifies the boundaries of the video channel. The SGSetChannelUsage function specifies whether the video and the sound channels are used for preview or record operations. The SGDisposeChannel function cleans up upon failure for each of the channels.

Listing 3-2  Creating a sound channel and a video channel

void MakeGrabChannels (SeqGrabComponent anSG,
                                SGChannel *videoChannel,
                                SGChannel *soundChannel,
                                const Rect *bounds, Boolean willRecord)
{
    OSErr err;
    long usage;
    /* figure out the usage */
    usage = seqGrabPreview;                 /* always previewing */
    if (willRecord)
        usage |= seqGrabRecord;             /* sometimes recording */
 
    /* create a video channel */
    err = SGNewChannel (anSG, VideoMediaType, videoChannel);
    if (!err) {
    /* set boundaries for new video channel */
        err = SGSetChannelBounds (*videoChannel, bounds);
 
    /* set usage for new video channel */
        if (!err)
            err = SGSetChannelUsage (*videoChannel,
                                       usage | seqGrabPlayDuringRecord);
        if (err) {
            /* clean up on failure */
            SGDisposeChannel (anSG, *videoChannel);
            *videoChannel = nil;
        }
    }
 
    /* create a sound channel */
    err = SGNewChannel (anSG, SoundMediaType, soundChannel);
    if (!err) {
        /* set usage of new sound channel */
        err = SGSetChannelUsage (*soundChannel, usage);
        if (err) {
            /* clean up on failure */
            SGDisposeChannel(anSG, *soundChannel);
            *soundChannel = nil;
        }
    }
}

Previewing Sound and Video Sequences in a Window

Listing 3-3 shows how to use the sequence grabber component to preview sound and video sequences in a window. Clicking the content area of the window causes the sequence grabber to pause until the mouse button is released.

The Image Compression Manager’s GetBestDeviceRect function helps you determine the best monitor for the window. The SGStartPreview function begins the preview of the sound and video sequences. The SGIdle function grants the sequence grabber component the time it needs to preview data. The SGUpdate function informs the sequence grabber of the update event. The Window Manager’s BeginUpdate and EndUpdate functions respond to the event. The SGPause function instructs the sequence grabber to suspend and resume its preview operation. In this example, it is used to suspend the preview operation while the mouse button is held down. Finally, the SGStop function halts the action of the sequence grabber component. The Component Manager’s CloseComponent function closes the component connection. The Window Manager’s DisposeWindow function disposes of the window.

Listing 3-3  Previewing sound and video sequences in a window

void CheckError(OSErr error, Str255 displayString)
{
    if (error == noErr) return;
    if (displayString[0] > 0)
        DebugStr(displayString);
    ExitToShell();
}
Boolean IsQuickTimeInstalled (void)
{
    short       error;
    long        result;
    error = Gestalt (gestaltQuickTime, &result);
    return (error == noErr);
}
void initialize (void)
{
    OSErr err;
 
    InitGraf (&qd.thePort);
    InitFonts ();
    InitWindows ();
    InitMenus ();
    TEInit ();
    InitDialogs (nil);
    MaxApplZone();
    if (!IsQuickTimeInstalled())
        CheckError(-1,"\pPlease install QuickTime and try again");
    err = EnterMovies ();
    CheckError(err,"\pUnable to initialize Movie Toolbox");
}
 
WindowPtr makeWindow(void)
{
    WindowPtr aWindow;
    Rect windowRect = {0, 0, 120, 160};
    Rect bestRect;
    /* figure out the best monitor for the window */
    GetBestDeviceRect (nil, &bestRect);
    /* put the window in the top left corner of that monitor */
    OffsetRect(&windowRect, bestRect.left + 10, bestRect.top + 50);
    /* create the window */
    aWindow = NewCWindow (nil, &windowRect, "\pGrabber",
                                 true, noGrowDocProc, (WindowPtr)-1,
                                 true, 0);
    /* and set the port to the new window */
    SetPort(aWindow);
    return aWindow;
}
 
main (void)
{
    WindowPtr theWindow;
    SeqGrabComponent theSG;
    SGChannel videoChannel, soundChannel;
    Boolean done = false;
    OSErr err;
    initialize();
    theWindow = makeWindow();
    theSG = makeSequenceGrabber(theWindow);
    if (!theSG) return;
 
    makeGrabChannels(theSG, &videoChannel, &soundChannel,
                         &theWindow->portRect, false);
    if ((videoChannel == nil) && (soundChannel == nil))
        CheckError(-1,"\pNo sound or video available");
    err = SGStartPreview(theSG);
    CheckError(err, "\pCan't start preview");
    while (!done) {
        AlignmentProcRecord alignProc;
        short part;
        WindowPtr whichWindow;
        EventRecord theEvent;
 
        GetNextEvent(everyEvent, &theEvent);
        switch (theEvent.what) {
            case nullEvent:         /* give the sequence grabber time */
                    err = SGIdle (theSG);
                    if (err) done = true;
                    break;
            case updateEvt:if (theEvent.message == (long)theWindow) {
                                    /* inform the sequence grabber of the
                                        update */
                SGUpdate(theSG,((WindowPeek)
                                     theWindow)->updateRgn);
                /* and swallow the update event */
                BeginUpdate(theWindow);
                EndUpdate(theWindow);
            }
            break;
 
            case mouseDown:part = FindWindow (theEvent.where,
                                                 &whichWindow);
                    if (whichWindow != theWindow) break;
                    switch (part) {
                        case inContent:
                            /* pause until mouse button is
                                released */
                            SGPause (theSG, true);
                            while (StillDown())
                            ;
                            SGPause(theSG, false);
                            break;
                        case inGoAway:
                            done = TrackGoAway (theWindow,
                                                  theEvent.where);
                            break;
                        case inDrag:
                            /* pause when dragging window so video
                                doesn't draw in the wrong place */
                            SGPause (theSG, true);
                            SGGetAlignmentProc (theSG, &alignProc);
                            DragAlignedWindow (theWindow,
                                                     theEvent.where,
                                                     &screenBits.bounds,
                                                     nil, &alignProc);
                            SGPause (theSG, false);
                            break;
                        }
                        break;
        }
    }
    /* clean up */
    SGStop (theSG);
    CloseComponent (theSG);
    DisposeWindow (theWindow);
}

Capturing Sound and Video Data

Listing 3-4 uses the sequence grabber component to capture ten seconds of sound and video data. It prompts the user for the name of the file to create. The SGSettingsDialog function is issued to invoke the default sound and video capture settings dialog boxes. These default dialog boxes allow the user to configure the settings for the capture operations. The SGSetMaximumRecordTime function indicates how long the capture operations will last. The SGStartRecord function specifies the time at which the capture operations will begin. The SGIdle function grants the time needed to confirm the capture operations. Finally, the SGStop function and the Window Manager’s DisposeWindow routine are called in order to complete the capture of the sequences.

Listing 3-4  Capturing sound and video

main (void)
{
    WindowPtr theWindow;
    CGrafPort tempPort;
    SeqGrabComponent theSG;
    SGChannel videoChannel, soundChannel;
    OSErr err;
    initialize();
    theWindow = makeWindow();
    theSG = makeSequenceGrabber(theWindow);
    if (!theSG) return;
    err = setGrabFile(theSG);
    CheckError(err, "\pNo output file");
    makeGrabChannels (theSG, &videoChannel, &soundChannel,
                             &theWindow->portRect, true);
    if ((videoChannel == nil) && (soundChannel == nil))
        CheckError(-1,"\pNo sound or video available");
 
    if (videoChannel)
        SGSettingsDialog (theSG, videoChannel, 0, nil,
                                 DoTheRightThing, nil, 0);
    if (soundChannel)
        SGSettingsDialog(theSG, soundChannel, 0, nil,
                             DoTheRightThing, nil, 0);
    err = SGSetMaximumRecordTime(theSG, 10 * 60);
    CheckError(err, "\pCan't set max record time");
    err = SGStartRecord (theSG);
    CheckError(err, "\pCan't start record");
    while (!err)
        err = SGIdle (theSG);
    if (err == grabTimeComplete)
        err = noErr;
    CheckError(err, "\pError while recording");
    err = SGStop(theSG);
    CheckError(err, "\pError creating movie");
    CloseComponent(theSG);
    DisposeWindow(theWindow);
}

Setting Up the Video Bottleneck Functions

Listing 3-5 shows how to set up the video bottleneck functions of the sequence grabber video channel component. Inside the main event loop in Listing 3-4, you should add the following lines after you call the SGSetMaximumRecordTime function.

Listing 3-5  Setting up the video bottleneck functions

    if (videoChannel) {
        err = SGSetVideoBottlenecks (videoChannel, &tempPort);
        CheckError(err, "\pCouldn't set video bottlenecks");
    }

Drawing Information Over Video Frames During Capture

Listing 3-6 shows how to use the video bottleneck functions of the sequence grabber video channel component to draw the letters “QT” over each video frame as it is captured.

Listing 3-6  Drawing information over video frames during capture

pascal ComponentResult myGrabFrameComplete (SGChannel c,
                                             short bufferNum,
                                             Boolean *done,
                                             long refCon)
{
    ComponentResult err;
    /* call the default grab-complete function */
   err = SGGrabFrameComplete (c, bufferNum, done);
    if (*done) {
        /* frame is done */
        CGrafPtr savePort;
        GDHandle saveGD;
        PixMapHandle bufferPM, savePM;
        Rect bufferRect;
        CGrafPtr tempPort = (CGrafPtr)refCon;
        /* set to our temporary port */
        GetGWorld (&savePort, &saveGD);
        SetGWorld (tempPort, nil);
        /* find out about this buffer */
        err = SGGetBufferInfo (c, bufferNum, &bufferPM, &bufferRect,
                                        nil, nil);
        if (!err) {
            /* set up to draw into this buffer */
            savePM = tempPort->portPixMap;
            SetPortPix(bufferPM);
            /* draw some text into the buffer */
            TextMode (srcXor);
            MoveTo (bufferRect.right - 20, bufferRect.bottom - 14);
            DrawString ("\pQT");
            TextMode(srcOr);
            /* restore temporary port */
            SetPortPix (savePM);
        }
        SetGWorld (savePort, saveGD);
    }
    return err;
}
 
OSErr setupVideoBottlenecks (SGChannel videoChannel, WindowPtr w,
                                     CGrafPtr tempPort)
{
    OSErr err;
    err = SGSetChannelRefCon (videoChannel, (long)tempPort);
    if (!err) {
        VideoBottles vb;
        /* get the current bottlenecks */
        vb.procCount = 9;
        err = SGGetVideoBottlenecks (videoChannel, &vb);
        if (!err) {
            /* add our GrabFrameComplete function */
            vb.grabCompleteProc = myGrabFrameComplete;
            err = SGSetVideoBottlenecks (videoChannel, &vb);
            /* set up the temporary port */
            OpenCPort (tempPort);           /* create a temporary port
                                                for drawing */
            SetRectRgn (tempPort->visRgn, -32000, -32000, 32000,
                             32000);        /* with a wide open visible
                                                and clip region . . . */
            CopyRgn (tempPort->visRgn, tempPort->clipRgn);
                                            /* so that you can use it in
                                                any video buffer */
            PortChanged ((GrafPtr)tempPort);
                                            /* tell QuickDraw about the
                                                changes */
        }
    }
    return err;
}

Application-Defined Functions

This section describes the functions that your application may supply to sequence grabber components.

MyGrabFunction

The sequence grabber component calls your grab function in order to start capturing a frame of video data.

Your grab function must present the following interface:

pascal ComponentResult MyGrabFunction (SGChannel c,
                                         short bufferNum,
                                         long refCon);

Parameter

Description

c

Specifies the reference that identifies the channel for this operation.

bufferNum

Identifies the buffer for this operation. You can obtain information about this buffer by calling the SGGetBufferInfo function.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

Your grab function can use the sequence grabber component’s SGGrabFrame function to support the default behavior.

MyGrabCompleteFunction

The sequence grabber component calls your grab-complete function in order to determine whether the current frame-capture operation is complete. Once a frame has been completely captured, you can modify its contents to suit your needs. For example, you can overlay text onto the video image.

Your function must present the following interface:

pascal ComponentResult MyGrabCompleteFunction (SGChannel c,
                                          short bufferNum,
                                          Boolean *done,
                                          long refCon);

Parameter

Description

c

Specifies the reference that identifies the channel for this operation.

bufferNum

Identifies the buffer for this operation. You can obtain information about this buffer by calling the SGGetBufferInfo function.

done

Contains a pointer to a Boolean value. Your function sets this Boolean value to indicate whether the frame has been completely captured. Set the Boolean value to true if the capture is complete; set it to false if it is incomplete.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

Your grab-complete function can use the sequence grabber component’s SGGrabFrameComplete function to support the default behavior.

See Listing 3-6 for a sample grab-complete function. This function draws the letters “QT” over each video frame in the sequence.

MyDisplayFunction

The sequence grabber component calls your display function in order to transfer a captured video image in an offscreen buffer into the destination buffer for the video channel.

Your display function must support the following interface:

pascal ComponentResult MyDisplayFunction (SGChannel c,
                                          short bufferNum,
                                          MatrixRecord *mp,
                                          RgnHandle clipRgn,
                                          long refCon);

Parameter

Description

c

Specifies the reference that identifies the channel for this operation.

bufferNum

Identifies the buffer for this operation. You can obtain information about this buffer by calling the SGGetBufferInfo function.

mp

Contains a pointer to a transformation matrix for the display operation. If there is no matrix for the operation, this parameter is set to nil.

clipRgn

Contains a handle to the clipping region for the destination image. This region is defined in the destination coordinate system. Apply the clipping region after applying the transformation matrix. If there is no clipping region, this parameter is set to nil.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

Your application sets the destination buffer by calling the SGSetChannelBounds function.

Your display function can use the sequence grabber component’s SGDisplayFrame function to support the default behavior.

MyCompressFunction

The sequence grabber component calls your compress function in order to start compressing the captured video image.

Your compress function must support the following interface:

pascal ComponentResult MyCompressFunction (SGChannel c,
                                          short bufferNum,
                                          long refCon);

Parameter

Description

c

Specifies the reference that identifies the channel for this operation.

bufferNum

Identifies the buffer for this operation. You can obtain information about this buffer by calling the SGGetBufferInfo function.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

Your compress function can use the sequence grabber component’s SGCompressFrame function to support the default behavior. This function uses the Image Compression Manager to compress the video image.

MyCompressCompleteFunction

The sequence grabber component calls your compress-complete function in order to determine whether the current frame-compression operation is complete.

Your compress-complete function must support the following interface:

pascal ComponentResult MyCompressCompleteFunction (SGChannel c,
                                         short bufferNum,
                                         Boolean *done,
                                         SGCompressInfo *ci,
                                         long refCon);

Parameter

Description

c

Specifies the reference that identifies the channel for this operation.

bufferNum

Identifies the buffer for this operation. You can obtain information about this buffer by calling the SGGetBufferInfo function.

done

Contains a pointer to a Boolean value. Your function sets this Boolean value to indicate whether the frame has been completely compressed. Set the Boolean value to true if the compression is complete; set it to false if it is incomplete.

ci

Contains a pointer to a compression information structure (defined by the SGCompressInfo data type). If the compression is complete, your function must completely format this structure with information that is appropriate to the frame just compressed.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

See The Compression Information Structure, for a description of the SGCompressInfo data type.

Once a frame has been completely compressed, you can add it to the movie. Your compress-complete function can use the sequence grabber component’s SGCompressFrameComplete function to support the default behavior.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

MyAddFrameFunction

The sequence grabber component calls your add-frame function in order to add a frame to a movie. Your add-frame function must support the following interface:

pascal ComponentResult MyAddFrameFunction (SGChannel c,
                                         short bufferNum,
                                         TimeValue atTime,
                                         TimeScale scale,
                                         SGCompressInfo *ci,
                                         long refCon);

Parameter

Description

c

Specifies the reference that identifies the channel for this operation.

bufferNum

Identifies the buffer for this operation. You can obtain information about this buffer by calling the SGGetBufferInfo function.

atTime

Specifies the time at which the frame was captured, in the time scale specified by the scale parameter. Your add-frame function can change this value before adding the frame to the movie or before calling the SGAddFrame function. You can determine the duration of a frame by subtracting its capture time from the capture time of the next frame in the sequence.

scale

Specifies the time scale of the movie. You must not change this value.

ci

Contains a pointer to a compression information structure (defined by the SGCompressInfo data type). This structure contains information describing the compression characteristics of the image to be added to the movie.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

See The Compression Information Structure, for a description of the SGCompressInfo data type.

You can use your add-frame function to modify the contents of the frame before it is added to the movie. This can be useful if you want to place frame numbers onto frames you are recording.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

Your add-frame function can use the sequence grabber component’s SGAddFrame function to support the default behavior.

MyTransferFrameFunction

The sequence grabber component calls your transfer-frame function in order to move a video frame from the capture buffer into the channel’s filter buffer.

Your transfer-frame function must support the following interface:

pascal ComponentResult MyTransferFrameFunction (SGChannel c,
                                         short bufferNum,
                                         MatrixRecord *mp,
                                         RgnHandle clipRgn,
                                         long refCon);

Parameter

Description

c

Specifies the reference that identifies the channel for this operation.

bufferNum

Identifies the buffer for this operation. You can obtain information about this buffer by calling the SGGetBufferInfo function.

mp

Contains a pointer to a transformation matrix for the transfer operation. If there is no matrix for the operation, this parameter is set to nil.

clipRgn

Contains a handle to the clipping region for the destination image. This region is defined in the destination coordinate system. Apply the clipping region after applying the transformation matrix. If there is no clipping region, this parameter is set to nil.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

The sequence grabber component calls this function only when you are filtering the video data. By filtering the video data through a filter buffer, you can eliminate some visual artifacts that result from noisy input video sources. Your application sets a filter buffer by calling the SGSetCompressBuffer function.

If you are using a grab-complete function to determine when frames have been grabbed, you should also implement a grab-compress-complete function (described in the next section). Otherwise, the channel will decompress the specified image before calling your grab-complete function, which will result in significantly lower performance. For details on grab-complete functions, see MyGrabCompleteFunction.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

Your transfer-frame function can use the sequence grabber component’s SGTransferFrameForCompress function to support the default behavior.

MyGrabCompressCompleteFunction

The sequence grabber calls your grab-compress-complete function when it is working with a video digitizer that supports compressed source data. Your grab-compress-complete function is responsible for determining whether the current compressed frame has been completely captured and compressed, essentially combining your grab-complete, compress, and compress-complete functions into one function.

Your function must support the following interface:

pascal ComponentResult MyGrabCompressCompleteFunction
                                         (SGChannel c,
                                          Boolean *done,
                                          SGCompressInfo *ci,
                                          TimeRecord *tr,
                                          long refCon);

Parameter

Description

c

Identifies the channel for this operation.

done

Contains a pointer to a Boolean value. Set this Boolean value to indicate whether you are finished. Set it to true when you are done; set it to false if the operation is incomplete.

ci

Contains a pointer to a compression information structure. When the operation is complete, fill in this structure with information about the compression operation.

tr

Contains a pointer to a time record. When the operation is complete, fill in this structure with information indicating when the frame was grabbed.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

See The Compression Information Structure, for a description of the SGCompressInfo data type.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

Your grab-compress-complete function may use the sequence grabber’s SGGrabCompressComplete function to support the default behavior.

MyDisplayCompressFunction

The sequence grabber calls your display-compress function when it is working with a video digitizer component that supports compressed source data. Your display-compress function is responsible for decompressing and displaying a compressed image.

pascal ComponentResult MyDisplayCompressFunction (SGChannel c,
                                         Ptr dataPtr,
                                         ImageDescriptionHandle desc,
                                         MatrixRecord *mp,
                                         RgnHandle clipRgn,
                                         long refCon);

Parameter

Description

c

Identifies the channel for this operation. The sequence grabber provides this value to your display-compress function.

dataPtr

Contains a pointer to the compressed image data.

desc

Specifies a handle to the image description structure to use for the decompression operation.

mp

Contains a pointer to a matrix structure. This matrix structure contains the transformation matrix to use when displaying the image. If there is no matrix for the operation, this parameter is set to nil.

clipRgn

Contains a handle to the clipping region for the destination image. This region is defined in the destination coordinate system. Apply the clipping region after the transformation matrix. If there is no clipping region, this parameter is set to nil.

refCon

Contains a reference constant value. You can set this value by calling the SGSetChannelRefCon function.

Error constant

Value

Description

cantDoThatInCurrentMode

-9402

Request invalid in current mode

Your display-compress function may use the sequence grabber’s SGDisplayCompress function to support the default behavior.

MyDataFunction

The sequence grabber calls your data function whenever any of the grabber’s channels write digitized data to the destination movie file. You assign a data function to the sequence grabber by calling the SGSetDataProc function.

Your data function must support the following interface:

pascal OSErr MyDataFunction (SGChannel c, Ptr p, long len,
                                         long *offset, long chRefCon,
                                         TimeValue time, short writeType,
                                         long refCon);

Parameter

Description

c

Identifies the channel component that is writing the digitized data.

p

Contains a pointer to the digitized data.

len

Indicates the number of bytes of digitized data.

offset

Contains a pointer to a field that may specify where you are to write the digitized data, and that is to receive a value indicating where you wrote the data. You must update the field referred to by this parameter, supplying the value indicated by the writeType parameter.

chRefCon

Contains control information. The low-order 16 bits contain sample flags for use by the Movie Toolbox's AddMediaSample function. The sequence grabber sets these flags as appropriate. The high-order 16 bits are reserved for Apple and are always set to 0.

time

Identifies the starting time of the data, in the channel's time scale. You may use the SGGetChannelTimeScale function to retrieve the channel's time scale.

writeType

Indicates the type of write operation being performed.

refCon

Contains the reference constant you specified when you assigned your data function to the sequence grabber.

The following values are defined for the writeType parameter:

Constant

Description

seqGrabWriteAppend

Append the new data to the end of the file. Set the field referred to by the offset parameter to reflect the location at which you added the data.

seqGrabWriteReserve

Do not write any data to the output file. Instead, reserve space in the output file for the amount of data indicated by the len parameter. Set the field referred to by the offset parameter to the location of the reserved space.

seqGrabWriteFill

Write the data into the location specified by the field referred to by the offset parameter. Set that field to the location of the byte following the last byte you wrote. This option is used to fill the space reserved previously when the writeType parameter was set to seqGrabWriteReserve. Note that the sequence grabber may call your data function several times to fill a single reserved location.

The sequence grabber calls your data function whenever any channel component writes data to the destination movie. You may use your data function to store the digitized data in some format other than a QuickTime movie.

You can instruct the sequence grabber not to write its data to a QuickTime movie by calling the SGSetDataOutput function and setting the seqGrabDontMakeMovie flag to 1. This can save processing time in cases where you do not want to create or update a movie.

MyModalFilter

The SGSettingsDialog function causes the sequence grabber to present its settings dialog box to the user. This is a movable modal dialog box, so you must provide a filter function to handle update events in your window. You specify your filter function with the proc parameter.

A modal-dialog filter function whose address is passed to SGSettingsDialog should support the following interface:

pascal Boolean MyModalFilter (DialogPtr theDialog,
                                         EventRecord *theEvent,
                                         short *itemHit, long refCon);

Parameter

Description

theDialog

Points to the settings dialog box's dialog structure.

theEvent

Contains a pointer to an event structure. This event structure contains information identifying the nature of the event.

itemHit

Contains a pointer to a field that contains the item selected by the user. If you handle the event, you should update this field to reflect the item number of the selected item.

refCon

Contains a reference constant. You provide this reference constant to the sequence grabber in the procRefNum parameter of the SGSettingsDialog function.

Your modal-dialog filter function returns a Boolean value that indicates whether you handled the event. Set this value to true if you handled the event; otherwise, set it to false. If you handle the event, be sure to update the value of the field referred to by the itemHit parameter.

Data Types

This section describes the compression information structure and the sequence grabber frame information structure.

The Compression Information Structure

The compression information structure defines the characteristics of a buffer that contains a captured image that has been compressed. Callback functions use compression information structures to exchange information about compressed images. For example, the compress-complete function must format a compression information record whenever a video frame is compressed (see MyCompressCompleteFunction for more information about the compress-complete callback function). The SGCompressInfo data type defines a compression information structure.

Frame Information Structure

The frame information structure defines a frame for a sequence grabber component and its sequence grabber channel components. The SeqGrabExtendedFrameInfo data type defines the format of a frame information structure. SeqGrabExtendedFrameInfo is an extension of SeqGrabFrameInfo, adding a new frameOutput field and extending the frameOffset field to 64 bits.