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.
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.
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.
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.
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.
_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