Creating Animation for MMPM/2

by Darren Dobkin

An exciting new development in the world of MMPM/2 is support for animation files. What this means to you is that the same unsurpassed performance, resource management, synchronization, device-independence, file format-independence, and data compression-independence is now available for animation, as it has been for still images, audio, and digital video.

This article provides a short overview of this new technology, and then shows you how you can use the existing MMPM/2 APIs to create animation in your multimedia presentations.

Overview
The great majority of all PC-based animation uses the FLI and FLC file formats designed by AutoDesk. FLI is the format used by older animation applications like AutoDesk Animator. A superset of FLI, FLC was introduced with AnimatorPro. Collectively, files in either of these formats are referred to as FLICs.

FLICs contain data that is organized into frames. The data is compressed, both within a frame and across frames. You can create new FLICs with AnimatorPro or a compatible animation design tool. AnimatorPro has many tools for painting individual still frames with a variety of color- and texture-effects, including ray-tracing functions. AnimatorPro also has a tweening tool that lets you generate a sequence of frames between any two still frames, creating an illusion of motion or transformation. Finally, AnimatorPro will compress and convert your frames into a .FLC file.

Using MMPM/2 APIs with Animation
FLIC animation is implemented in MMPM/2 by introducing both a new file format and a new compression type to MMPM/2. In the MMPM/2 architecture, file format- and compression type-specific code is isolated in subsystem extensions called I/O Procedures (IOProcs). The new FLIC IOProc contains code necessary to read FLIC files and to decompress FLIC data into displayable frames.

By providing all necessary function for FLICs in an IOProc, MMPM/2 lets all existing applications that use IOProcs read FLIC data. For example, if you already have an application that reads frames from an Ultimotion AVI file using the Multimedia I/O (MMIO) API, no code changes are needed to start reading FLIC animation frames. The MMIO subsystem automatically identifies your .FLI or .FLC files as FLIC animations, loads the FLIC IOProc for you, and calls the FLIC decompressor to provide you with frame data.

Logical device functions, such as loading files, playing, pausing, seeking, and stepping, are implemented in the MMPM/2 architecture by Media Control Devices (MCDs). The OS/2 2.1 operating system introduced an MMPM/2 MCD that lets you display sequences of video frames - the Software Motion Video MCD. Just as the Software Motion Video MCD gets video frames from .AVI files using the AVI IOProc, it now can get animation frames from the FLIC IOProc. Because no changes were necessary at the MCD level of the architecture, applications that have been using the Software Motion Video MCD to play Ultimotion and Indeo movies now can use the same interface to play FLIC animations.

And, just as existing applications that use IOProcs benefit by the creation of a FLIC IOProc, MCDs that obtain data from IOProcs and display that data as video now can show animation.

The Software Motion Video MCD lets your animation attain its intended frame rate by bypassing OS/2's Presentation Manager and writing directly to the video RAM (VRAM). Because FLIC animation decompression is very fast, you can attain frame rates well in excess of 30 frames per second.

The MCD sends visible region information to the FLIC IOProc so that the IOProc decompressor can clip the animation to the visible region. Visible region clipping ensures that your animation will not accidentally overwrite another application window that overlays it.

Direct VRAM access, combined with visible region clipping, means that your application can participate in a windowed environment without being restricted by the slower performance of Presentation Manager (PM) blitting. However, on playback systems (such as VGA) where VRAM access is not permitted, the MCD will use PM blitting. The bottom line is that your animation application acquires display hardware-independence, achieving high frame rates wherever possible.

Easy as Open, Play, Close
The Media Control Interface API gives your application easy access to the logical device functions of the Software Motion Video MCD. Playing an animation can be done in as little as three lines of code: Open, Play, Close. The Media Control Interface API comes in two flavors, a structured command interface and a string interface. Most functions are available using either interface, although the string interface generally is recommended because it's easy to use when prototyping and developing applications.

To use the Media Control Interface, start with MCI_OPEN. The following sample code shows you how to open the Software Motion Video MCD explicitly: MCI_OPEN_PARMS         SMVOpen;                /* Structure for MCI_OPEN */ WORD                   wSMVID;                 /* Device ID returned     */ SMVOpen.lpstrDeviceType    =                   /* Device type            */ (LPSTR)(MAKEULONG(MCI_DEVTYPE_DIGITAL_VIDEO,1)); SMVOpen.dwCallback         = (DWORD) hwnd;     /* Application window     */ SMVOpen.lpstrAlias         = NULL; SMVOpen.lpstrElementName   = NULL; rcode = mciSendCommand(0,                      /* Wait for device to open */                        MCI_OPEN,                        MCI_WAIT | MCI_OPEN_TYPE_ID,                        (DWORD) 0); wSMVID= SMVOpen.wDeviceID;                     /* Save device ID          */

MCI_OPEN returns a device ID for an instance of the Software Motion Video MCD that you'll use for all future Media Control Interface calls. You can improve the responsiveness of your program while opening the device by specifying MCI_NOTIFY, instead of MCI_WAIT. If MCI_NOTIFY is specified, mciSendCommand returns immediately, allowing your program to continue with other work. When the device instance completes its initialization, your application window receives an MM_MCINOTIFY message asynchronously.

Furthermore, if you know the name of an animation file that you want to load at device-open-time, you can specify the filename in the lpstrElementName field and use the MCI_OPEN_ELEMENT</tt> flag. Otherwise, you can load an animation file into the device instance later by using MCI_LOAD</tt>. Again, using MCI_NOTIFY</tt> on the load and processing the resultant MM_MCINOTIFY</tt> message will make your application more responsive during load time.

By default, the Software Motion Video MCD displays your animation in its own frame window. You can, instead, play the animation within the context of your own window by using an MCI_WINDOW</tt> call to pass your window handle as follows: MCI_DGV_WINDOW_PARMS SMVWindow;                /* Structure for MCI_WINDOW */ SMVWindow.hwndDest = hwndPlayback;             /* Pass window handle */ rcode = mciSendCommand(wSMVID,                 /* Set playback hwnd */                        MCI_WINDOW,                        MCI_WAIT | MCI_DGV_WINDOW_HWND,                        (DWORD) 0);

You can precisely position your animation within your application's window by providing coordinates with an MCI_PUT</tt> command. For example, you set the animation to play at some position in your window (x,y) as follows: MCI_DGV_RECT_PARMS   SMVPut;           /* Structure for MCI_PUT         */ SMVPUT.rc.xLeft = x1;                  /* Horizontal window coordinates */ SMVPUT.rc.xRight = x2; SMVPUT.rc.yBottom = y1;                /* Vertical window coordinates   */ SMVPUT.rc.yTop = y2; rcode = mciSendCommand(wSMVID,         /* Position animation in window  */                        MCI_PUT,                        MCI_WAIT | MCI_DGV_PUT_DESTINATION |                        MCI_DGV_PUT_RECT,                        (DWORD) 0);

Eventually, you can play your animation with an MCI_PLAY</tt> command: MCI_DGV_PLAY_PARMS   SMVPlay;              /* Structure for MCI_PLAY */ SMVPlay.dwCallback   = (DWORD) hwnd;       /* Application window     */ rcode = mciSendCommand(wSMVID,             /* Play the animation */                        MCI_PLAY,                        MCI_NOTIFY,                        (DWORD) 0);

Your application window will receive an MM_MCINOTIFY</tt> message when the playback is complete. When your application no longer has an immediate need for animation playback and you want to deallocate the resources associated with the SMV device, issue an MCI_CLOSE</tt>, as follows: rcode = mciSendCommand(wSMVID,         /* Close the device */                        MCI_CLOSE,                        MCI_NOTIFY,                        (DWORD) NULL, 0);

Even Easier...
The Media Control Interface string interface simplifies the previous process. That is, you can replace every call to mciSendCommand</tt> with a call to mciSendString</tt>. The corresponding strings would be: OPEN DIGITALVIDEO ALIAS SMV WAIT LOAD SMV filename.fll WAIT WINDOW SMV HANDLE nnnnnnnnn WAIT PUT SMV DESTINATION AT x1 y1 x2 y2 WAIT PLAY SMV NOTIFY CLOSE SMV NOTIFY

nnnnnnnnn is your application window handle as a decimal string; (x1,y1), (x2,y2) are the window coordinates for the bottom-left/top-right rectangle where the animation will be played.

If your application requires more direct information about an animation file, you can read the file header with the MMIO API. First, open the file with mmioOpen</tt>: MMIOINFO       mmioinfo;                         /* mmioOpen structure */ HMMIO          hmmio;                            /* MMIO file handle   */ memset(0, sizeof(MMIOINFO)); mmioinfo.dwTranslate = MMIO_TRANSLATEDATA; hmmio = mmioOpen(filename, MMIO_READ); /* Open the file     */

The MMIO_TRANSLATEHEADER</tt> flag determines what header lengths and headers that mmioQueryHeaderLength</tt> and mmioGetHeader</tt> will return. If you omit the MMIO_TRANSLATEHEADER</tt> flag from the <tt>mmioinfo.dwTranslate</tt> field, MMIO provides the actual FLIC header; otherwise, the standard MMPM/2 movie and video track headers are returned.

If you omit the <tt>MMIO_TRANSLATEHEADER</tt> flag, you get the FLIC header as follows: LONG           lSize;                           /* Size of header         */ char           *pFLICHeader;                    /* Buffer for FLIC header */ LONG           lBytes;                          /* # of bytes read        */ rc = mmioQueryHeaderLength(hmmio, 0, 0); /* Get header length     */ pFLICHeader = malloc(lSize);                    /* Allocate buffer        */ rc = mmioGetHeader( hmmio,                      /* Get the FLIC header    */                   (PVOID)pFLICHeader,                   lSize, 0, 0);

The FLIC header consists of an <tt>FLC_FILE_CHUNK</tt> structure, which provides useful information such as the number of frames, frame height and width, frame duration in milliseconds, and (for .FLC files only) the aspect ratio.

Summary
The wide assortment of APIs provided by the MMPM/2 Media Control Interface and MMIO subsystems let your animation application remain device-independent and file-format-independent. Some APIs, such as <tt>mmioGetHeader</tt>, give you file-format-dependent information when necessary.

If you're already using MMPM/2 in your applications for audio, MIDI, laserdisc, or digital video, your learning curve for incorporating animation is practically nil. If you are just starting your multimedia development with animation, but expect to integrate other media later, using MMPM/2 will simplify your integration work by providing the same set of APIs for all media.

About the Author - Darren Dobkin
Darren Dobkin is a Staff Programmer in the Personal Operating System Toolkit Development Department. He has worked for IBM for 11 years as a programmer on a variety of projects including satellite communications, videotext, text editing, and multimedia. His recent work includes software motion video playback for OS/2 2.1, software motion video recording and editing for Ultimedia Video In, and MIDI editing.