It has been several years since Microsoft Windows became a platform of choice for multimedia applications. Since the introduction of multimedia capabilities in Windows 3.1, hardware prices have dropped significantly. Just a few short years ago, multimedia, video, and sound required expensive workstations; now it is available on most home computers. CD-ROM drives, rapidly growing in popularity, provide the means to deliver multimedia presentations to most PCs. In short, multimedia can no longer be ignored by most programmers.
Although multimedia programming under Windows can be quite complex, applications can accomplish many things by a few simple function calls. Nothing demonstrates this better than the MCIWndCreate function, which can be used to replay videos in a single function call. Our review of Windows multimedia programming begins with a closer look at this function; for many simple applications, you may not need anything more sophisticated.
The program shown in Listing 37.1 can perhaps be viewed as the Windows multimedia equivalent of a Hello, World application. This program takes a single command-line parameter, the name of a multimedia file such as an AVI video file, and plays it back in its window. It can also be launched without a parameter; in that case, use the button controls that appear in its window (Figure 37.1) to open a file for playback.
All right, I cheated. In addition to the single function call, we also have a few lines of code handling notification messages. By responding to MCIWNDM_NOTIFYPOS and MCIWNDM_NOTIFYSIZE messages, we can ensure that the application's main window is automatically resized. That way, we end up with a window that is properly sized for video playback. Nevertheless, the application demonstrates the power of MCIWndCreate well; with that one function call, we have been able to create a full-featured video playback application.
This simple program can be compiled from the command line by typing cl hello.c user32.lib vfw32.lib.
MCIWndCreate is not the only simple function that can be used for media playback. Another such function is the Playback function; this function can be used to play waveform audio files.
Multimedia represents the operating system's ability to record and play video and sound. Multimedia programming is accomplished through a series of interfaces, or APIs that applications can utilize for this purpose.
Waveform audio is sampled, digitized audio data. Waveform audio is typically stored in files with the .wav extension. Windows recognizes waveform audio files with mono and stereo data, a variety of sampling rates, and sampling depths. There are also several different compression methods used for the efficient storage of waveform data.
MIDI is the acronym for Musical Instrument Digital Interface. This international standard specifies a protocol for interfacing computers and electronic musical instruments. MIDI sequences are data that can be played back on MIDI-compatible instruments. MIDI data is stored under windows in files with the .mid extension. MIDI files can be played back on external devices connected to the computer, or on built-in synthesizers that support MIDI capabilities.
AVI is the Audio Video Interleaved file format. AVI files can be used to store a motion video stream and one or more audio channels. Windows recognizes video data at varying resolutions, color depths, and refresh rates. There are also several compression formats that are in wide use.
Although presently Windows does not support recording or playback of MPEG format video files, Microsoft has announced plans to do so in the future.
Windows multimedia functions can also be used for audio-CD playback. Using third-party drivers, recording and playback of other multimedia formats is also possible.
Depending on your programming needs, you can choose one of three interface levels to interact with the multimedia subsystem in Windows.
The high-level interface is based on the MCIWnd window class. In the example program shown in Listing 37.1, it was an MCIWnd function that enabled us to do video playback with only a single function call.
The mid-level interface is the Media Control Interface, or MCI. MCI provides a device-independent command-message and command-string interface for the playback and recording of audio and visual data.
At the lowest level, there are several interfaces for waveform audio, AVI video, and MIDI recording and playback. Additional interfaces provide audio mixer capabilities, buffered file I/O, and joystick and timer control.
All these interfaces serve but one purpose: to provide a programming interface between end-user applications on the one hand, and drivers for multimedia hardware on the other. Through these drivers, device-independence in Windows is achieved.
Which interface should you choose for your application? That depends on the needs and requirements of your project.
Applications that require only simple playback capabilities are good candidates for using the MCIWnd services. For example, an encyclopedia application that offers video clips accompanying some articles may use an MCIWnd window for video playback.
An example for a more demanding multimedia program is an audio recording and playback application. Such a program should probably rely on the services of the Media Control Interface to implement its functionality.
A sophisticated multimedia application, such as a video capture and mixer application, would probably require using low-level video and file services.
The MCIWnd window class represents the simplest, highest level multimedia programming interface in Windows. Applications that require simple playback capabilities can utilize MCIWnd windows for this purpose; such a window can be created with a single function call, as demonstrated by the program in Listing 37.1.
The Play and Stop buttons can be used to start and stop playback. Playback starts at the current position indicated by the trackbar. Special playback effects can be utilized by holding down the Shift or Control keys while clicking on the Play button; holding down the Control key results in full-screen video playback, while holding down the Shift key results in backwards play.
The Menu button can be used to invoke a popup menu. The options in this menu are specific to the type of the media file currently selected. For example, if an AVI file is loaded, the popup menu includes options to set the video playback speed, sound volume, zoom, and video configuration. Other commands enable you to copy the current data to the Windows clipboard and to open another file. Yet another menu option enables you to send an MCI command string directly to the currently active multimedia device.
The Record button can be made available for devices that can record.
The trackbar is used to display the current playback or recording position relative to the size of the file. The trackbar can also be used to move to different locations in the file during playback.
Optimal video playback performance requires that the playback window be aligned on a four-pixel boundary. Normally, Windows aligns the playback window automatically.
An MCIWnd window is created by a call to MCIWndCreate. A call to this function registers the MCIWnd class and creates an MCIWnd window.
In addition to specifying the handle of the parent window and an instance handle, parameters to this function also specify a set of window styles and an optional filename.
The window style settings control which elements of the MCIWnd window are visible, and how it interacts with the user on the one hand and the application code on the other. For example, by specifying the MCIWNDF_RECORD window style, you can create an MCIWnd window with a visible Record button. Specifying the MCIWNDF_NOTIFYSIZE causes the MCIWnd window to send notification messages to its parent whenever the window's size changes.
MCIWnd windows can be created as child windows or overlapped windows. If created as an overlapped window, an MCIWnd window will have a title bar with contents specified with the appropriate style settings (MCIWNDF_SHOWMODE, MCIWNDF_SHOWNAME, or MCIWNDF_SHOWPOS).
MCIWnd windows can also be created via calls to CreateWindow or CreateWindowEx. Before you can do so, however, you must call MCIWndRegisterClass. This function registers the window class specified by the constant MCIWND_WINDOW_CLASS.
There are two additional functions that use windows of class MCIWnd. The functions GetOpenFileNamePreview and GetSaveFileNamePreview enhance the standard GetOpenFileName and GetSaveFileName functions by adding a multimedia preview window to the standard file open dialog (Figure 37.4).
Applications can communicate with an MCIWnd window by sending messages to it using the Windows SendMessage function. A large number of helper macros exist that simplify sending most of these messages.
Through these messages, applications can control the appearance and behavior of the MCIWnd window, start and stop playback and recording, close the MCI device or file and open a new file or device for recording or playback, seek specific playback and recording positions, retrieve information on device capabilities and current settings, specify MCI device attributes, and control the MCI device.
Table 37.1 summarizes MCIWnd messages and helper macros.
If enabled, MCIWnd windows can send notification messages to their parent windows. Specifically, five types of notification messages can be sent. All five can be enabled by specifying the MCIWNDF_NOTIFYALL window style when creating the window. Alternatively, notification messages can be enabled individually.
The MCIWNDM_NOTIFYERROR message is sent to the parent window to notify it of MCI errors. This notification can be enabled by specifying the MCIWNDF_NOTIFYERROR window style.
The MCIWNDM_NOTIFYMEDIA message notifies the parent window of any media changes that may have occurred. These messages are enabled by specifying the MCIWNDF_NOTIFYMEDIA window style.
The parent window is notified of changes in the MCIWnd window's position and size through MCIWNDM_NOTIFYPOS and MCIWNDM_NOTIFYSIZE messages. These messages are enabled by the MCIWNDF_NOTIFYPOS and MCIWNDF_NOTIFYSIZE window styles, respectively.
Finally, the parent window is notified of any operating mode changes (for example, changes from play to stop mode) by MCIWNDM_NOTIFYMODE messages. These messages are enabled by the MCIWNDF_NOTIFYMODE window style.
The Media Control Interface provides a set of device-independent command messages and command strings for controlling multimedia devices. Command messages and command strings can be used interchangeably.
The MCI recognizes a variety of different multimedia devices. These devices are listed in Table 37.2.
Table 37.2. Multimedia devices.
Audio CD player
Digital-audio tape player
Non GDI-based digital video in a window
Analog video in a window
Video cassette recorder or player
Video disc player
Waveform audio device
Both command messages and command strings can be used to control devices and to retrieve information from devices. Command messages retrieve information in the form of structures, which are easy to interpret in C programs. Command strings retrieve information in the form of strings that must be parsed and interpreted by the application.
All MCI devices support a core set of MCI commands and messages. Many devices support additional, device-specific commands.
Command strings are sent to devices using the mciSendString function. A simple use of this function is demonstrated in Listing 37.3; this application, which takes a single filename as its command-line argument, plays back a multimedia file. For example, if you specify the pathname for an AVI video file, this application will play back the video in full-screen mode. To compile this application, type cl mcistr.c user32.lib winmm.lib.
Command messages are sent to devices using the mciSendCommand function. This function takes several parameters, one of which is a command-specific structure. The structure may either be a general purpose one such as MCI_OPEN_PARMS or a device-specific extension to the general purpose version. Applications fill in this structure as appropriate prior to executing the call to mciSendCommand; commands that return information do so by modifying elements in this structure. The program in Listing 37.4 demonstrates simple playback using the MCI command message interface; this program can be compiled from the command line by typing cl mcimsg.c winmm.lib. Like its command string counterpart, this program also takes the name of a multimedia file on the command line and plays back that file.
Listing 37.4. MCI playback using command messages.
The generic syntax for MCI command strings is as follows:
command identifier [argument [, argument]]
The command portion specifies an MCI command such as PLAY, OPEN, or CLOSE. The identifier identifies an MCI device. This may be an MCI device name or an alias name. This identifier represents an instance of the appropriate MCI driver that was created when the device was opened.
Command arguments are used to specify flags and parameters specific to each MCI command.
For example, consider the following MCI command string:
play music from 0 to 100 wait
In this string, play is an MCI command; music is (presumably) an alias that was created when the device was opened; and from 0 to 100 wait is a series of arguments and flags applicable to the play command.
The wait flag in this command specifies that the call to mciSendString should not return before the command is finished. Normally, calls return immediately and the commands are processed in the background.
Another commonly used flag is the notify flag. Specifying this flag causes the MCI to post a multimedia notification (MM_MCINOTIFY) message to the application whenever a command is completed.
Some devices (for example, digital video devices) support the test flag. A command submitted with this flag is not executed; however, the MCI tests whether the command can be executed by the specified device and returns an error if that is not the case.
These flags can also be specified for commands submitted in the form of MCI command messages.
MCI commands fall into different categories. These include system commands, required commands, and optional commands.
The two system commands, break and sysinfo, are recognized and processed by the MCI itself. The break command is used to set a virtual key code that aborts other MCI commands; the sysinfo command returns information on MCI services and devices.
Required commands are those that every MCI device must implement. This set includes the following five commands: capability, close, info, open, and status. The capability, info, and status commands obtain information on the status and capabilities of the device. The open and close commands are used to open or close the device.
Optional commands can be further broken down into two categories; basic commands and extended commands. Basic commands include load and save, play, record, and stop, pause and resume, seek and set, and certain forms of the status command. For most devices, it is reasonable to assume that a subset of these commands applicable to the device is supported. For example, a playback device can reasonably be expected to support at least the play and stop commands; a recording device can be expected to support record and stop.
Extended commands include several additional configuration and editing commands. Your application should not expect that any of these commands are supported; instead, it should use the capability command to find out about the available set of commands.
We have already encountered two MCI functions: mciSendCommand is used to send an MCI command message, while mciSendString is used to send an MCI command string.
Another set of three functions can be used to retrieve information about an MCI device. The mciGetCreatorTask function returns the handle of the task that created a specific MCI device. The mciGetDeviceID function returns the MCI device identifier for a named device. The mciGetErrorString returns the error message string corresponding to a specific error code.
Two additional functions, mciGetYieldProc and mciSetYieldProc, can be used to yield procedure. The yield procedure is a function that the MCI calls regularly while waiting for the completion of a command that was issued with the wait flag.
The MCI also offers a series of macros that deal with time formats. Various time formats are used in MCI commands to set the recording or playback position, or to read back the current position. Positions can be expressed as time values, track positions, and so on. Time format macros are used to create position values and to retrieve individual elements of a position value. For example, MCI_HMS_HOUR retrieves the hour component of a position expressed in the form of hours, minutes, and seconds (HMS); MCI_MAKE_HMS creates a time value from parameters specifying hours, minutes, and seconds.
MCI devices can send two types of messages to the application. The MM_MCINOTIFY message is used to notify the application of the completion of a command. Parameters to this message identify the command and specify whether the command was successfully completed, or whether its execution was interrupted due to an error or some other condition.
The MM_MCISIGNAL message is used specifically in response to the extended MCI command signal. Through this command, applications can request that the MCI send an MM_MCISIGNAL message when a specific spot in the content is reached.
AVIFile functions and macros offer low-level access to files containing RIFF (Resource Information File Format) data; examples for such files include digital video and waveform audio files.
AVIFile functions are based on the OLE Component Object Model. AVIFile functions can be used to open and close files, place files to the Windows clipboard, and obtain and manipulate file properties. AVIStream functions can be used to read or write the actual audio or video data.
For video and audio data sources other than AVI video and waveform audio files, you can write custom file and stream handlers. Custom file and stream handlers are installable drivers that provide access to data in different sources using the OLE Component Object Model.
Custom handlers are dynamic link libraries ("inproc" OLE servers) that implement the IAVIFile and IAVIStream interfaces. These interfaces are used by the AVIFile and AVIStream family of functions.
DrawDib functions provide high-performance capabilities for drawing device-independent bitmaps (DIBs) in 8-bit, 16-bit, 24-bit, and 32-bit graphic modes.
DrawDib functions do not rely on the GDI but write directly to video memory. The provide a variety of services ranging from image stretching, dithering, to compression and decompression of many known formats.
Video capture can be accomplished using the AVICap window class and a series of related functions and macros.
In the simplest scenario, applications create an AVICap window through the capCreateCaptureWindow function and send messages to this window to control capturing. A large number of macros exist that simplify the sending of messages to AVICap windows and the processing of message results.
Windows offers a series of functions dealing with recording and playback of waveform audio. Simplest among these is the PlaySound function that enables you to play audio files that fit into available memory.
Other waveform audio functions can be used to control individual waveform input and output devices.
Windows also provides a programming interface to the audio compression manager, or ACM. The ACM provides for the transparent compression and decompression of waveform audio data during recording and playback.
The ACM is installed as a "mapper." This means that the ACM can intercept audio recording and playback requests and decode or encode data as necessary. The ACM can also search for a waveform device or an ACM compressor or decompressor that can handle a specific format.
Other multimedia services include a series of functions for low-level buffered file I/O optimized for media recording and playback; file services specific to RIFF (Resource Interchange File Format); functions and notification messages for handling joystick devices; and functions to create and manage high resolution multimedia timers.
With the rapid deployment of powerful personal computers that are equipped with CD-ROM drives, multimedia has been brought to the masses. The Windows programmer can no longer ignore this area of programming; multimedia, if only in the form of sound effects or tutorial video clips, is quickly becoming a part of most new applications.
Multimedia in windows consists of the capabilities to record and play video and audio files. Windows recognizes one video format (AVI files) and two audio formats (MIDI and waveform audio).
Windows offers multimedia interfaces on three distinct levels. At the highest level is the MCIWnd window class. Through this window class, it is possible to perform video playback with a single function call. The MCIWnd API also offers a large number of macros that utilize the SendMessage function to communicate with an MCIWnd window. These windows can also send notification messages to their parent window.
The mid-level multimedia programming interface is the Media Control Interface, or MCI. MCI offers command strings and command messages for controlling multimedia devices; command strings and command messages can be used interchangeably. All MCI devices recognize a set of core commands; many devices recognize a set of additional, device-specific commands. While using the command string interface is generally simpler, for commands that retrieve information from the device it is often advantageous to use command messages instead. Unlike command strings, which return responses in the form of strings that need to be parsed an interpreted by the application, command messages return information in the form of structures.
Low-level video services include the AVIFile family of functions, interfaces to video compression and video capture, and high performance functions for drawing device-independent bitmaps. For data in nonstandard sources, custom file and stream handler drivers can be developed.
The AVIFile function family as well as custom file and stream handlers are also applicable to waveform audio. Other low-level audio services include functions for recording and playback of waveform audio and MIDI data. Additional interfaces are provided to access the audio compression manager and audio mixer devices.
Other low-level multimedia interfaces include high performance buffered file I/O, joystick control, and multimedia timers.