Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Database

Programming Quicktime


JUL92: PROGRAMMING QUICKTIME

PROGRAMMING QUICKTIME

Multimedia to the Macs

This article contains the following executables: QUICKT.ARC

Aaron E. Walsh

Aaron is a consultant for Boston College, where he is currently engaged with projects involving client-server programming, interapplication communications, and e-mail APIs. He can be reached via AppleLink (A03), Bitnet (Walshag@BCVMS), or Internet ([email protected]).


Apple's QuickTime is a system-wide architecture for handling sophisticated data elements that provides standard access to "time-based" data in typical Macintosh fashion: Cut, Copy, and Paste. Time-based (or "dynamic") data is any data type that can be stored and retrieved as values over time. In other words, if the data changes over time, it is dynamic. Examples of time-based data are video, sound, animation, or a graph of laboratory data over time.

When activated, time-based data appears to move, or play, just as movies or music are played in the real world. Using QuickTime, a word processor can incorporate into its documents video segments that come to life when selected. These video segments can be copied and pasted into other applications, such as spreadsheets or databases. When selected, the segment will display video and play any sound it contains. In short, walking, talking documents have come to the Macintosh.

In QuickTime, the term "movie" is used to describe a file which contains time-based data. A movie is simply a repository for dynamic data--sound, video, animation, or other. Movies may contain multiple sources of dynamic data: A video sequence combined with music and animation is not uncommon.

As Figure 1 illustrates, each movie contains one or more "tracks," which are actually pointers to data structures known as "media." A media is responsible for the video, sound, or animation elements of a movie. When played, a QuickTime movie resembles its cinematic counterpart: Video, animation, and sound are played simultaneously. Thus, the media work together.

QuickTime access is not exclusive to the Macintosh, however. The movie-format specifications have been fully published by Apple, encouraging the development of QuickTime applications on other platforms. Apple further reinforced the cross-platform design of QuickTime by providing programming routines which aid in the transfer of Macintosh movie files to non-Macintosh computers.

QuickTime Toolbox Managers

At the system-software level, QuickTime consists of three major groups of calls (Managers) that add over 500 new software routines to the Mac's already considerable repertoire.

Movie Toolbox is a set of high-level system-software calls that load, play, record, edit, and store dynamic data.

Component Manager provides applications access to external services without requiring the application to have detailed knowledge of the resource (digitizer card, VCR, software extension, and so on) providing those services. These resources, known as "components," are allowed to register their capabilities at run time. An application can then ask for a device with specific capabilities (for example, a compression algorithm capable of lossless compression), and expect the Component Manager to locate and communicate with a corresponding device, if one exists.

Image Compression Manager (ICM) handles the interaction among the components responsible for compressing and decompressing image data. The ICM hides the actual algorithms, allowing developers to take advantage of numerous compression schemes (JPEG, MPEG, Group 3 Fax) without becoming immersed in implementation details.

QuickTime File Formats

At the core of QuickTime is its support for two file formats: one to handle QuickTime data, the other an extension to the PICT file.

The movie format is the standard way in which QuickTime data is stored and manipulated, created specifically to handle the inherent complexity and large file sizes of dynamic data. A movie contains one or more tracks, which in turn access a single media. The media is responsible for handling the raw data samples (video, animation, or sound) associated with the track. A media's raw data may be stored in the movie file itself, on separate disk files, CD-ROMS, remote volumes, or other storage devices. In short, a movie contains one or more tracks which use media to access raw data samples.

When a track becomes active, the data associated with its media is accessed and played by QuickTime. Each track describes how its media data is to respond when activated: playback speed, screen location, default volume, duration, and so on. These parameters can vary from track to track, providing independent control over a movie's various data elements.

When a movie is played, the media associated with each track is located and played according to type: Graphics are displayed on screen; sound is played through the speaker(s). If the media data represents a compressed image, the ICM is called upon to decompress the data into usable form. The ICM, in turn, asks the Component Manager for an appropriate decompression component (which may exist in either hardware or software). If available, it is used by the ICM to decompress the media data. The final image is handed to the Movie Toolbox to be displayed on screen (see Figure 2).

Each data type has an associated "media handler." The media handler is responsible for randomly accessing media data segments and playing those segments at a rate specified by the movie track. Each media may have a time-coordinate system different than that of the movie, in which case the media handler is responsible for mapping between time-coordinate systems. The Movie Toolbox shields programmers from details of this level, handling these interactions transparently when a movie is played.

Essentially, a movie track consists of a pointer to media samples, the appearance of which, when played, is controlled via track parameters. Utilizing pointers and parameters, a movie file is easily edited. Setting a track pointer to reference a media located on disk, rather than copying the entire media into a movie file, significantly speeds the process.

More Details.

In addition to introducing the new movie file format, QuickTime extends the functionality of the familiar PICT file format. While additional code is needed to access movies, applications which view PICT files using the DrawPicture() QuickDraw routine automatically have access to QuickTime-compressed PICT files, without need for modification. When DrawPicture() is called, the ICM is automatically invoked, transparently decompressing the PICT and passing it to the calling application in the form it expects.

The Movie Toolbox also provides new Standard File toolbox routines which allow the user to create and view document previews. The original routines displayed only the names of files in a selected directory, which meant the user had to remember the filename when retrieving documents. The new routines display a preview of the document, typically a small thumbnail PICT image, along with the traditional document name, as shown in Figure 3. This thumbnail preview provides the user with a visual representation of the file prior to opening it.

Previews are not limited to movie and graphic documents. Nongraphic files might contain previews which give descriptions of their content, size, or creation/modification dates.

If a file is selected for which no preview is available, the system will generate one upon request, if possible. To create a new preview for a file, click on the Create button located on the left side of the dialog box. This button is enabled only if the preview is missing but can be created by the system. For detailed information on utilizing previews and preview dialogs, refer to the sample code and documentation.

Programming QuickTime

Apple defines an application as Quick-Time "literate" if it supports two or more of the following five features:

  • Playback of movies using the standard controller component.
  • Standard File Preview dialog box.
  • Still-image compression.
  • Storing data as a QuickTime movie.
  • Cut, copy, and pasting of movie data.
The two programs accompanying this article illustrate the process of playing and creating QuickTime movies. Movie-Player (
Listing One, page 102) uses the Movie Toolbox and Component Manager to view an already-created movie. MovieMaker (Listing Two, page 104) uses the Movie Toolbox, Component Manager, and Image Compression Manager to create a QuickTime movie.

Both applications use the Gestalt Manager to determine the runtime environment, ensuring that QuickTime is available. Generically, QuickTime refers to the Movie Toolbox, the Component Manager, and the Image Compression Manager. At startup, the QuickTime INIT installs the programming routines for each manager. Together, the three managers provide a rich environment for managing time-based data, known simply as QuickTime. If QuickTime is not installed, ExitToShell() is called, which aborts program execution and returns to the Finder. Basic Macintosh programming skills are assumed and will not be covered in this article.

MoviePlayer

MoviePlayer (Listing One) illustrates programming techniques for playing QuickTime movies. The program presents the user with a preview dialog box to select an existing movie file from disk. The selected movie is displayed on screen in a window, which has a standard movie controller attached to it. The controller can be thought of as a remote-control unit attached to the movie window, allowing the user to start the movie, pause the movie, play the movie in reverse, adjust the volume, or jump from section to section. See Figure 4.

Although MoviePlayer is not a full-featured Macintosh application (it does not have a menu, nor does it allow you to drag the movie window to a new screen location), it does illustrate the general framework all movie players require:

  • Use the Gestalt Manager to determine if QuickTime is available.
  • Initialize the Movie Toolbox, providing access to QuickTime.
  • Select a movie file using the StandardGetFilePreview().
  • Open the selected movie file, preparing it for play.
  • Create a window in which to view the movie.
  • Use the Component Manager to obtain a standard movie controller.
  • Attach the controller to the movie display window.
  • Play/control the movie utilizing the controller.
  • Dispose of movie data structures when finished playing.
  • Exit QuickTime, freeing storage allocated by the Movie Toolbox.
Before calling any of the more than 500 QuickTime routines, you must first ensure that QuickTime is indeed available on the machine running your application. The most straightforward way of testing for information of this type is with the Gestalt Manager. Testing for the presence of QuickTime is not enough, however. It must then be initialized for use with a call to Enter-Movies(). Only after initialization are you able to call QuickTime routines.

MoviePlayer then calls StandardGetFilePreview(), prompting the user to select a movie file from disk. Once selected, the movie file must be prepared for play. This involves opening the disk file and creating from it a movie in memory.

To display the movie, you must create a window in which to play it. Additionally, you may provide a controller to allow user interaction with the movie. MoviePlayer demonstrates both techniques. The user is able to perform all the standard actions expected of a QuickTime movie: play, pause, fast-forward, and reverse.

Once the movie has finished playing, the display window and the controller are released from memory. The disk file is then closed, and the user is prompted to select another movie to view. This select-play-dispose loop is repeated until the user selects Cancel from the StandardGetFilePreview() dialog box. At this point, the Movie Toolbox is exited, balancing the previous initialization of QuickTime with EnterMovies(), and the MoviePlayer application is terminated. (More detailed programmer's notes are available electronically.)

MovieMaker

MovieMaker (Listing Two) is similar to MoviePlayer in that it tests for the presence of and initializes QuickTime at start-up. It also disposes of allocated movie components before terminating execution. The major difference between MoviePlayer and MovieMaker is the main loop, which creates a QuickTime movie both on disk and in memory. MovieMaker demonstrates how to:

  • Create a movie file using StandardPutFile() and CreateMovieFile().
  • Create a movie track.
  • Create a media associated with the movie track.
  • Prepare a movie graphics environment.
  • Allocate a frame buffer using information supplied by the ICM.
  • Create individual movie frames, compress them, and then add each one to a media.
Instead of asking the user to select an existing movie file from disk, MovieMaker prompts the user to specify where on disk he/she would like to store the movie to be created. Once specified (using StandardPutFile()), an empty movie file is created.

This empty file is then given a single track and an associated media. At this point the media references no data; the movie file can be thought of as a blank VHS tape ready for exposure. However, adding frames to a QuickTime movie is a little more complicated in the preparation stage when compared to ease of popping tape into your familiar camcorder: Each frame must be created, compressed, and added to the movie media one at a time.

Two buffers are allocated for the movie frames. One, known as the Graphics World (or GWorld) is used to store individual images as they are created with standard QuickDraw routines. The other, a "frame buffer," is used to store the compressed version of an image after it is drawn into the GWorld.

Each QuickDraw-generated frame is drawn into the movie GWorld, compressed, and added to the frame buffer, and finally added to the movie media. These steps are repeated until the last frame is generated, at which point the movie preview is created. The movie file is then closed, allocated storage is released, and the MovieMaker application is terminated.

Conclusion

QuickTime is a scaled technology--it is as powerful as the machine it runs on. As computers become faster, movie playback speeds will improve, decompression/compression times will decrease, and movies will seem even more realistic. Add-on boards are already available that allow a Macintosh to display QuickTime movies at 30 frames per second, the NTSC broadcast standard for video.

QuickTime revolutionizes the way people manipulate complex data on their computers, bringing Cut, Copy, and Paste to dynamic data.

Multimedia Human-Interface Guidelines

Human-interface guidelines provide developers a common set of rules for developing multimedia software. The end result is an application that is more intuitive to use, easier to learn, and more quickly mastered than an application developed without following the Human-interface guidelines. Apple is developing a set of Human-interface guidelines specifically for QuickTime, but which have applicability for all multimedia platforms. Among the QuickTime Human-interface tenets are:

  • The user should be able to look at a screen and discern which objects are movies. Movies not in play should look like a PICT image, with the addition of a controller or a "badge." A badge is a small graphic superimposed on a stopped movie. It is used to identify a movie when the controller is not visible, and should disappear when the movie is in play.
  • When first opened, a movie should display its first frame or poster. In this state, the movie is not playing. The user is responsible for beginning movie play.
  • When played, the movie will start from the current frame. For a newly opened movie, this should be the first frame or poster; for a paused movie, it will be the frame at which the movie was paused.
  • Movies should be cut, copied, pasted, and resized in the same way as PICT files.
  • Resizable movies should maintain their original aspect ratios by default.
  • The controls for playing a movie should be easily accessible to the user. A mechanism for play and stop should always be available and intuitive to use.
  • In the absence of a volume-control mechanism, there must at least be a mute control. A volume-control mechanism is preferred to an off/on mute control, however.
  • Generally, single-clicking a movie will select that movie, not play it. A selected movie may be cut or copied (depending on the application). Resizing, hiding controls, getting information about the movie, or various other operations may be applied to a selected movie, depending on the application.
  • If double-clicking plays a movie, a second click or double-click must suspend play.
  • If single-clicking does not select a movie, it may play the movie as long as a second single-click suspends play.
  • When printed, a movie will print its currently displayed frame. A movie will print with controller or badge visible, distinguishing a printed movie from a printed graphic.
--A.W.



_PROGRAMMING QUICKTIME_
by Aaron Walsh


[LISTING ONE]
<a name="0192_000d">

/***************************************************************************
* MoviePlayer Application -- This QuickTime program demonstrates how to open
*   a movie file using a file preview dialog, add a movie controller to the
*   movie, and play a movie in a window. Author: Aaron E. Walsh
*   Developed using Think C 5.0, & QuickTime headers
***************************************************************************/

#include <Movies.h>
#include <QuickTimeComponents.h>
#include <GestaltEqu.h>
#include <Quickdraw.h>
#include <StandardFile.h>
#include <OSEvents.h>

/***** Global Variables ******/
Boolean     gSys7Preview;   /* is System 7  Preview routine available*/

Movie           theMovie;       /* info about the movie returned by OpenMovie*/
Rect            dispBounds;
MovieController myMovieController;  /* controller component for movie*/

WindowPtr       movieWindow;    /* window to play the movie in */
OSErr           error;

/* Variables used in opening a movie file: */
FSSpec      mySpec;     /* File System record for System 6 */
short           resRefNum;  /* Resource reference # of selected file */

SFTypeList      ypes  = {'MooV'}; /* show files of type 'Moov' */
short           numtypes = 1;

StandardFileReply   fReply;  /* Standard File Reply (Sys. 7/QuickTime) */
SFReply         oldfReply; /* old style (Sys.6) Standard File Reply */

EventRecord     *theEvent;

/****** Prototypes ******/
Boolean         QuickTimeCapable(void);  /* is system QT capable? */
StandardFileReply   GetMovie(void);  /* user select movie file to play */
void            PlayMovie(void); /* play the movie */
void            MakeMovieController(void); /* find controller . */
void            ShowMovieController(void); /* attach controller */

/***************************************************************************
* main() -- Initialize standard Macintosh toolbox managers, check if QuickTime
*   is available, execute small loop prompting user for movies to play. Exit
*   when user selects "Cancel" from preview dialog.
***************************************************************************/
main()
{
    /* Initialize Toolbox Managers and data structures: */
    MaxApplZone();
    InitGraf(&qd.thePort);
    FlushEvents(everyEvent, 0);
    InitWindows();
    InitCursor();
if (QuickTimeCapable()) {
    do {
        fReply = GetMovie(); /* prompt user for a movie file to play */
        if (fReply.sfGood)
            PlayMovie(); /* play the selected movie. */
            } while (fReply.sfGood);
        ExitMovies(); }
    else
        ;   /* QuickTime is not available.  Normally you would
            /* put up an error message for user. */
}

/***************************************************************************
* GetMovie() -- Allow user to select a movie file to play (file of type
*    'Moov') using StandardGetFilePreview routine.
***************************************************************************/
StandardFileReply GetMovie()
{
    Point   where;      /* for System 6 preview */
if (gSys7Preview)
StandardGetFilePreview(0, numtypes, types, &fReply); /* Sys 7 preview dialog */

else {                  /* using Sys 6 */
    where.h = where.v = -2; /* center dialog on screen w/"best" display */
    SFGetFilePreview(where, 0l, 0l, numtypes, types, 0l, &oldfReply);
    fReply.sfGood = oldfReply.good;
    if (fReply.sfGood)       /* convert the reply record into an FSSpec: */
        FSMakeFSSpec(oldfReply.vRefNum, 0, oldfReply.fName, &mySpec);
    }
return (fReply);
}

/***************************************************************************
* MakeMovieController() -- Uses the Component Manager to locate the default
*   movie controller, where it is then displayed at bottom of movie window.
***************************************************************************/
void MakeMovieController()
{
    Component           standardMovieController;
    ComponentDescription    controllerDescription;
    ComponentResult     theErr;
    Point               thePoint;
    Rect                controllerBox;
    /* Fill in component descriptor fields.  This info is used by the
     * Component Manager to locate a corresponding component. We are
         * looking for the standard movie controller component:  */
    controllerDescription.componentType = 'play';
    controllerDescription.componentSubType = 0;
    controllerDescription.componentManufacturer = 0;
    controllerDescription.componentFlags = 0;
    controllerDescription.componentFlagsMask = 0;

standardMovieController = FindNextComponent( (Component) 0,
                                                      &controllerDescription);
    /* Get the controller */
    myMovieController = OpenComponent(standardMovieController);

    if(myMovieController == 0l)
        return;            /* return to caller if this is the case */
    /* Place controller in the movie window */
    thePoint.h = movieWindow->portRect.left;
    thePoint.v = movieWindow->portRect.top;

theErr = MCNewAttachedController(myMovieController,theMovie, movieWindow,
                                                                    thePoint);
    if (theErr != 0)
        return;
    ShowMovieController();
}

/***************************************************************************
* ShowMovieController() -- Adjusts size of movie window so movie and movie
*   controller are viewable
***************************************************************************/
void ShowMovieController()
{
    Rect    movieBox, controllerBox;
/* Adjust size of movie window to accomodate both movie and movie controller */
MCGetControllerBoundsRect(myMovieController,&controllerBox);
/* Adjust movieBox to accomodate controller: */
UnionRect(&movieBox,&controllerBox,&movieBox);
/* Resize movie window: */
SizeWindow( movieWindow,movieBox.right,movieBox.bottom,true);
}

/**************************************************************************
* PlayMovie() -- Opens the appropriate movie file (file of type 'MooV'),
*   create a window large enough to fit the movie, and play the movie.
***************************************************************************/
void PlayMovie()
{
    FSSpec  movieFSSpec;
    /* First open the movie file */
    if (gSys7Preview)
        movieFSSpec = fReply.sfFile;
    else
        movieFSSpec = mySpec;
    if ((error = OpenMovieFile(&movieFSSpec, &resRefNum, 0)) != noErr)
        return; /* if error occured, exit PlayMovie() */
    if ((error = NewMovieFromFile( &theMovie,resRefNum, nil, nil,0, nil ))
                                                                      != noErr)
        return; /* if error occured, exit  PlayMovie() */
    /* Find movie bounds and set top left to 0,0 so */
    /* the movie will be properly postioned in our window   */
    GetMovieBox(theMovie, &dispBounds);
    OffsetRect(&dispBounds,-dispBounds.left,-dispBounds.top);
    SetMovieBox(theMovie, &dispBounds);

    OffsetRect(&dispBounds,50,50);  /* window rect can't hit menu bar */
    movieWindow = NewCWindow(0L,&dispBounds,0l,true,0,(WindowPtr)-1L,
                                    false,0L);     /* window for our movie*/
    SetPort(movieWindow);
    SetMovieGWorld(theMovie,nil,nil);
    MakeMovieController();  /* routine for creating standard controller */
    /* After setup, play the movie: */
    GoToBeginningOfMovie(theMovie); /* rewind movie to beginning */
    PrerollMovie(theMovie,0,0); /* preload portions of movie */
    SetMovieActive(theMovie,true);  /* set movie to active for servicing */

    /* Use controller to play movie until it is finished. Events are
           passed to MCIsPlayerEvent which handles controller events: */
    while ( !IsMovieDone(theMovie)) {
        GetNextEvent(everyEvent, theEvent);
        MCIsPlayerEvent(myMovieController, theEvent);
        }
    /* dispose of storage, and return */
    DisposeMovie(theMovie);        /* movie */
    CloseMovieFile(resRefNum);     /* reference to movie file */
    CloseComponent(myMovieController); /* movie controller */
    DisposeWindow(movieWindow);
}

/**************************************************************************
* QuickTimeCapable() -- Uses Gestalt Manager to check if QuickTime is
*   available at runtime. If not, return error.
***************************************************************************/
Boolean QuickTimeCapable()
{
    long    response;
        /* Test if QuickTime is available: */
    error = Gestalt(gestaltQuickTime, &response);
        if (error != 0)   /* error=0 if OK, else error has occured */
            return false;   /* if error, return */

/* if no error finding QuickTime, check for ICM so we can use Stand.Preview  */
    error = Gestalt(gestaltCompressionMgr, &response);
        if (error != 0)
          return false; /* Can't use Stand.Preview routines */
    error = Gestalt(gestaltStandardFileAttr, &response);
    if (error != 0)    /* if not available, we're playing under System 6 */
        gSys7Preview = false;
    else
        gSys7Preview = true;       /* use System 7 standard preview */
    error = EnterMovies();  /* Initialize Movie Toolbox & return result */
    if (error != 0)
        return false;   /* error initalizing QuickTime  */
    else
        return true;    /* QuickTimes available, ready to play movies*/
}





<a name="0192_000e">
<a name="0192_000f">
[LISTING TWO]
<a name="0192_000f">

/***************************************************************************
* MovieMaker Application -- This QuickTime program demonstrates how to create
*   a QuickTime movie with associated track and media. The Movie Toolbox,
*   Component Manager, and Image Compression Manager (ICM) are demonstrated.
*   Author:  Aaron E. Walsh -- Developed using Think C 5.0, & QuickTime headers
***************************************************************************/

#include <Movies.h>
#include <QuickTimeComponents.h>
#include <ImageCompression.h>
#include <GestaltEqu.h>
#include <Quickdraw.h>

/****** defines ******/
#define kFrameX 150 /* x-coord/width*/
#define kFrameY 125 /* y-coord/height */
#define kPixelDepth 32  /* depth for GWorld */
#define kFrameTotal 30  /* total frames in movie */
#define kTimeScale 15   /* desired frames per second    */
#define kFrameRate (Fixed) 1<<16 /* fixed point 1.00 = our frame rate */

/****** Types and globals ******/
/*  general:    */
OSErr           error;
Rect            frmRect;
/* movie file:  */
Movie           gMovie;         /* our movie,   */
Track           gTrack;         /* track,   */
Media           gMedia;         /* and media    */
short           resRefNum;
StandardFileReply   fReply;
FSSpec      movieFSSpec;        /* FFSpec reference to movie file */
/* image data: */
char            **frameDatabitsH;   /* buffer for compressed frames */
ImageDescription    **imageDescriptionH;/* image info used by compressor */
PixMap      *pixMap,**pixMapH;      /* offscreen pixmaps */

/* graphics world: */
GWorldPtr       movieGWorld,oldGWorld;  /* offscreen grapics worlds */
GDHandle        oldGDevice;
/* compressor:*/
long            compressedFrameSize;  /* size of compressed frame */
CodecType       codecType;        /* desired codec */
CompressorComponent codecID;          /* variation of codecType */
short           colorDepth;       /* depth to compress image to*/
CodecQ              imageQuality;         /* desired compression quality*/

/* media sample: */
TimeValue       sampTime;   /* generated when adding sample to media */

/****** Prototypes ******/
void        BuildMovie(void);     /* main routine to assemble a movie */
void        MakeMovieFile(void);  /* create movie file and movie itself */
void        MakeMovieGWorld(void);/* allocate offscreen graphics environ */
void        AllocateMovieBuffer(void); /* allocate storage for frames*/
void        MakeMovieFrames(void);  /* loop to create all movie frames */
void        AddMovieFrame(void);  /* compress & add single frame to media*/
void        CleanUp(void);       /* free allocated storage, make preview */
void        main(void);

/***************************************************************************
* main() -- Initialize standard Macintosh toolbox managers, check if QuickTime
   capable and call BuildMovie() to create our movie.
****************************************************************************/
void main(void)
{
    /* Initialize Toolbox Managers and data structures: */
    MaxApplZone();
    InitGraf(&qd.thePort);
    FlushEvents(everyEvent, 0);
    InitWindows();
    InitCursor();
    if (QuickTimeCapable()) {
        BuildMovie();       /* create the movie */
    }
}

/***************************************************************************
* BuildMovie() -- Sets up display window for movie frames, and calls
*   appropriate routines for creating movie.
***************************************************************************/
void BuildMovie(void)
{
    WindowPtr displayWind;
    Rect windRect;
    windRect.left = windRect.top = 0;
    windRect.right = kFrameX;
    windRect.bottom = kFrameY;
    OffsetRect(&windRect,150,50);
displayWind = NewCWindow(0,&windRect,(StringPtr)"\pMovie Window",true,0,
                                                         (WindowPtr)-1,true,0);
    SetPort(displayWind);
    ClearMoviesStickyError();   /* clear any old movie errors   */
    while (!GetMoviesStickyError()) {
        MakeMovieFile();    /*create actual movie file on disk */
        MakeMovieGWorld();  /*set up graphics devices for images */
        AllocateMovieBuffer();  /*allocate buffer space */
        MakeMovieFrames();  /*create, compress & add frames */
        CleanUp();          /* create preview & release storage */
    }
    CloseWindow(displayWind);   /* close display window */
}

/***************************************************************************
* MakeMovieFile() -- Create a new movie file (gMovie) with associated track
*   (gTrack) and media (gMedia).
***************************************************************************/
void    MakeMovieFile(void) {
/* StandardPutFile prompt; create movie file */
StandardPutFile((StringPtr) "\pCreate Movie File:",
                                            (StringPtr)"\pNew Movie",&fReply);
    if (!fReply.sfGood)
        return;
    movieFSSpec = fReply.sfFile;    /* reference to our movie file*/
error = CreateMovieFile( &movieFSSpec,'MPLA',0,createMovieFileDeleteCurFile,
                                                           &resRefNum,&gMovie);
    if (error) ExitToShell();
/* Create track and media */
    gTrack = NewMovieTrack(gMovie,(long)kFrameX<<16,(long)kFrameY<<16,0);
    error = GetMoviesError();
    if (error) ExitToShell();
gMedia = NewTrackMedia(gTrack, VideoMediaType, kTimeScale, nil,(OSType) nil);
    error = GetMoviesError();
    if (error) ExitToShell();
    error = BeginMediaEdits( gMedia ); /* needed to add samples to media */
    if (error) ExitToShell();
}

/***************************************************************************
* MakeMovieGWorld() -- Make a GWorld (offscreen graphics world) for movie.
***************************************************************************/
void MakeMovieGWorld(void) {
    GetGWorld(&oldGWorld,&oldGDevice);/* save old graphics world/device*/
    frmRect.left = frmRect.top = 0; /* setup size of frame*/
    frmRect.right = (short)(kFrameX);
    frmRect.bottom = (short)(kFrameY);
/*create movieGWorld: */
    error = NewGWorld(&movieGWorld,kPixelDepth,&frmRect,nil,nil,0);
    if (error) ExitToShell();
 /* get handle to pixMap of movieGWorld: */
    pixMapH = GetGWorldPixMap(movieGWorld);
/* lock offscreen pixMap in memory:*/
    LockPixels(pixMapH);
/* lock handle to prevent dangling reference:*/
    HLock((Handle)pixMapH);
    pixMap = *pixMapH;  /* make pointer (pixMap) to pixel-map*/
}

/***************************************************************************
* AllocateMovieBuffer() -- Allocate frame buffer according to our requested
*   compression level.
***************************************************************************/
void AllocateMovieBuffer(void) {
long        maxCompressedFrameSize; /* Max size of a compressed frame*/

/*  compressor info: */
    codecID = anyCodec;
    codecType = (CodecType) 'rpza'; /* use video compression */
    colorDepth = 1;         /* compress to 1 bit depth */
    imageQuality = codecNormalQuality;/* quality range is 0x100 to 0x300 */
    imageDescriptionH = (ImageDescription **)NewHandle( 4 );
/* find needed buffer size: */
    error = GetMaxCompressionSize(&pixMap,&frmRect,colorDepth,imageQuality,
        codecType,codecID,&maxCompressedFrameSize);
    if (error) ExitToShell();
/* Allocate frame buffer */
    frameDatabitsH = NewHandle(maxCompressedFrameSize);
    if (!frameDatabitsH) ExitToShell();
    HLock(frameDatabitsH);      /* lock handle to buffer */
}

/***************************************************************************
* MakeMovieFrames() -- Create a unique series of movie frames using simple
*  QuickDraw calls. Compress and add each frame to movie (gMovie). Stop when
*  max # of frames is reached
****************************************************/
void MakeMovieFrames(void)
{
    long        i;     /* loop control */
    Rect        r2, r3;    /* rects used in creating graphics image */

    for(i = 0; i<kFrameTotal; i++) /* loop until max# frames is created */
    {
        if(error!= noErr)
            ExitToShell();

/* Draw a single frame. Uses QuickDraw calls to create graphics image */
        SetGWorld(movieGWorld,nil);
        EraseRect(&frmRect);    /* erase whole area to white */
        r2 = frmRect;
        r2.bottom = (short)((long)r2.bottom * i / (kFrameTotal-1));
        InvertRect(&r2);
        r2 = frmRect;
        FillOval(&r2, black);
        InsetRect (&r2,i,i);
        FillOval(&r2, white);
        InsetRect (&r2,i+2,i+2);
        InvertOval(&r2);
    SetRect(&r3,(frmRect.right - frmRect.left) / 2,(frmRect.bottom -
                                                             frmRect.top) / 2,
        (frmRect.right - frmRect.left) / 2,(frmRect.bottom -
                                                            frmRect.top) / 2 );
        InsetRect(&r3,-(i*2),-(i*2));
        FillOval(&r3,white);
/* draw frame into the old Gworld so creation process can be viewed.
* done for visual feedback */
        SetGWorld(oldGWorld,oldGDevice);
        CopyBits((BitMap *) pixMap,(BitMap *) *(PixMapHandle)
                        (qd.thePort->portBits.baseAddr),&frmRect,&frmRect,0,0);
/* compress and add current frame to movie: */
        AddMovieFrame();
    }
}

/***************************************************************************
* AddMovieFrame() -- Compress current frame then add it to our movies media.
*  This is done for each frame.
***************************************************************************/
void AddMovieFrame(void) {
/* compress frame:  */
error = CompressImage(pixMapH,&frmRect, imageQuality,,codecType
                           imageDescriptionH, StripAddress(*frameDatabitsH) );
    compressedFrameSize = (**imageDescriptionH).dataSize;
    if (error) ExitToShell();
/* add single frame to media:*/
error = AddMediaSample(gMedia, frameDatabitsH, 0L, compressedFrameSize,
  (TimeValue)1, (SampleDescriptionHandle) imageDescriptionH, 1L, 0, &sampTime);
    if (error) ExitToShell();
}

/***************************************************************************
* QuickTimeCapable() -- Uses the Gestalt Manager to check if QuickTime is
*   available at runtime.  If not, return error.
***************************************************************************/
Boolean QuickTimeCapable()
{
    long    response;
    /* Test if QuickTime is available: */
    error = Gestalt(gestaltQuickTime, &response);
    if (error != 0)     /* error=0 if OK, else an error has occured */
        return false;   /* if error, not QuickTime capable */
/* if no error finding QuickTime, check for the ICM  */
    error = Gestalt(gestaltCompressionMgr, &response);
    if (error != 0)
        return false;
    error = EnterMovies();/* Initialize Movie Toolbox */
    if (error != 0)
        return false;   /* error initalizing QuickTime */
    else
        return true;    /* QuickTime capable; ready to play movie */
}

/***************************************************************************
* CleanUp() -- Create a new movie file (gMovie) with associated track (gTrack)
*    and media (gMedia). Return error code if unable to complete process.
***************************************************************************/
void CleanUp()
{
    short       resourceId = 1;
    error = EndMediaEdits( gMedia );    /* finished adding samples */
    if (error) ExitToShell();
error = InsertMediaIntoTrack(gTrack,0L,0L,GetMediaDuration(gMedia),kFrameRate);
    if (error) ExitToShell();
error = AddMovieResource( gMovie, resRefNum, &resourceId, movieFSSpec.name );
    if (error) ExitToShell();
error = MakeFilePreview(resRefNum, (ProgressProcRecordPtr) -1);
    error = CloseMovieFile( resRefNum );
    if (error) ExitToShell();
    DisposeMovie(gMovie);       /* We don't need the movie anymore */
    DisposHandle(frameDatabitsH);   /* dispose frame buffer memory */
    DisposHandle((Handle)imageDescriptionH); /* and other storage: */
    DisposeGWorld(movieGWorld);
    ExitMovies();
}


Copyright © 1992, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.