A Multimedia Class Library for Windows

John enhances the Windows Media Control Interface to multimedia devices by designing and implementing a comprehensive C++ class library that provides object support for multimedia. The result is a set of objects that make programming multimedia easier and more robust, concise, and maintainable.


July 01, 1993
URL:http://www.drdobbs.com/windows/a-multimedia-class-library-for-windows/184409038

Figure 1


Figure 1


Copyright © 1993, Dr. Dobb's Journal

JUL93: A Multimedia Class Library for Windows

A Multimedia Class Library for Windows

Encapsulating the Media Control Interface

John Musser

John is a senior developer at Personal Media International, a startup specializing in multimedia entertainment software. He can be reached via CompuServe at 70621,1460.


Microsoft Windows' Media Control Interface (MCI) is a standard command language for communicating with multimedia devices--CD, Waveform and MIDI audio, AVI, videodiscs, video overlay devices, audio mixers, and the like. However, even though both Microsoft and Borland provide with their compilers large, comprehensive class libraries--the Microsoft Foundation Class library and ObjectWindows Library, respectively--neither provide object support for multimedia or Windows API multimedia extensions.

This article addresses this gap by showing how to design and implement a comprehensive C++ class library that enhances the MCI interface to multimedia devices. This hierarchy employs encapsulation, inheritance, and polymorphism to create a flexible and extensible framework for controlling multimedia devices under Windows. The result is a set of objects that make programming multimedia easier, more robust, concise, and maintainable. A simple client program, MciMan, demonstrates the use of the Waveform audio and AVI video classes. The class library and client program are compiler independent and can be used with any Windows 3.1 compatible C++ compiler, including Borland's C/C++ and Microsoft's Visual C++. The AVI portions of this code require the digitalv.h #include file, Video for Windows drivers, and runtime DLLs that come with the Video for Windows package and its SDK. They're available free of charge from the Windows Extensions forum on CompuServe (GO WINEXT). Vfwrun.zip contains the runtime DLLs and vfwdk.zip is the Video for Windows Development Kit.

MCI Overview

The MCI specification, released in 1991 by Microsoft and IBM, defines a set of base commands that can be applied to any general device, and extended commands for specific device types. The specification is designed to be extensible so that other devices may be added. For example, the AVI specification was added in 1992. Another example is MediaVision, which supplies an MCI driver that provides audio-mixing capabilities using extended commands specific to this device type. The specification as documented in the Windows SDK identifies 11 device types. Some drivers are supplied with either Windows or the SDK, while others are provided by the device supplier. Table 1 describes most of the currently available MCI drivers. As of this writing, however, not all the device types listed actually had MCI drivers available.

MCI divides all multimedia devices into one of two device types: simple or compound. The basic difference is that simple devices do not use data files, whereas compound devices usually do. CD audio and videodisc players are simple devices; waveform audio, MIDI sequencers, and AVI are all compound devices.

MCI provides two basic programming interfaces: a command-string interface that allows the use of ordinary text strings such as play cdaudio to control multimedia devices (this very open approach is well suited to scripting and authoring applications); and a command-message interface that uses C-language structures and a Windows-style message-passing model for device control. The MCI classes I describe here use the command-message interface because it's more efficient and better suited to general programming.

A single function, mciSendCommand(), is used along with a set of "polymorphic" arguments to access the command-message interface. The mciSendCommand() function takes:

Nearly all the member functions in this library make at least one call to mciSendCommand(). But don't be fooled by its simplicity: It has many options, constants, flags, messages, structures, and return values (and our goal is to hide these).

MCI Class Library

The MCI class hierarchy is designed to encapsulate and enhance the MCI interface and to get the most benefit from the least code. It is part of a C++ library that will be used as part of the basis for the commercial multimedia products we are currently developing. As such, it was designed for a specific immediate purpose, but also had to be capable of handling unknown future requirements. This influenced both the design and the implementation. The design had to be flexible, extensible, and (most of all) practical. Because our needs are for specific devices only, not all MCI devices are directly implemented although adding support for additional devices is a short process. Figure 1 diagrams the MCI class hierarchy, giving an overview of this single-inheritance tree.

All MCI commands are classified into one of four categories: system, required, basic, and extended. Commands specified for the first two types (such as open, close, and status) require support by all device types and are good candidates for member-function definitions in the base class(es) of the MCI class hierarchy. The basic commands, including play, stop, and seek, are supported by most device types and can also be placed at or near the top of the tree. Extended commands that support specific devices can be implemented farther down the hierarchy.

The root node for the MCI class hierarchy, MCIDevice, serves as the base class for both the CompoundDevice class and all simple device classes. It provides the basic open/close/play type commands, status commands for querying a device's state, set commands to configure devices, and a number of other miscellaneous functions. The data items needed (all private) are:

The constructor (see Listing One, page 102) does not automatically open the device--doing so would mean that the device would be opened and potentially unavailable to other applications from the time this object is constructed until it's destroyed. This is also true for all the derived classes: A device is not opened until explicitly told to do so with an Open() call. It is closed either through a call to the Close() member function or when the object is destroyed and the destructor is invoked. (Constructing a CompoundDevice with a filename is treated as if it were an Open() call and that file is immediately opened.) Keeping devices open only as long as necessary is generally polite behavior in a multitasking environment such as Windows.

The constructor for MCIDevice and any derived simple device (such as CDAudio) takes a single argument, a string value specifying the appropriate MCI device name. A default value is provided for each of the simple device types so that an application will rarely have to explicitly provide this value. The only exception might be on a system using nonstandard or additional device names that do not match the usual MCI device-name strings defined in the [mci] section of the SYSTEM.INI file. MCIDevice is not an abstract class and can therefore be constructed and used directly. It is designed, however, to serve as the basis for derived classes supporting unique device types.

Most of the member functions have a direct mapping to a corresponding MCI command. These functions almost always require fewer arguments than either the command-message or the command-string interfaces because some of the necessary information has been encapsulated in the object. Each member function first initializes any necessary data structures, sets the appropriate flags and arguments, and calls mciSendCommand(), occasionally more than once.

The function's return code is passed back to the object's client and is also saved in order to maintain an error state for later reference. The ErrorMessage() member function calls mciGetErrorString() to get the error description, and displays a standard Windows error-message dialog box to the user. This class and all derived classes highlight one of the benefits of using C++: the ability to use destructors to perform automatic cleanup. It is important that applications properly close all MCI devices and files they have opened. By utilizing the C++ destructor mechanism we can automatically trigger the closing of any open devices before an application exits and thereby improve reliability and robustness (not to mention avoiding any extra general-protection-fault-type messages).

A sampling of functions built upon the MCI set, status, and capability commands are implemented to show how these access operations can be implemented. MCIDevice provides the virtual functions Set(), Status(), and GetDevCaps() for this purpose, but makes these protected because they are not intended to be directly called. Making these public forces the user to be familiar with the numerous MCI_SET and MCI_STATUS constant values needed for each specific option. Instead, these are used as the mechanism within publicly defined inline functions defined for each of the set and status options. For example, the Length() function, which returns the length of the current device, uses the Status() function with the MCI_STATUS_LENGTH option to get its value. (It should be pointed out that a type of simple runtime type identification can be achieved by using information functions such as DeviceType() and CompoundDevice(). The DeviceType() function returns a unique constant identifier for each device type, and therefore each class. CompoundDevice() returns a Boolean value of True if the given object is a compound device.)

One other notable method, SetParent(), is used to assign the handle of a window designated as the parent of this MCI object. Play() checks to see if a parent window has been assigned to it and if so, stores this handle in the parameter block given to mciSendCommand() and sets the MCI_NOTIFY bit of the corresponding flags value. This causes MCI to post the MM_MCINOTIFY message to the specified window when the operation is completed. When this occurs, the window procedure's lParam is set to the device id that initiated the callback, and the wParam gives the status of the operation, which can be success, failure, superseded, or aborted. MCI allows the Notify option to be used with all commands, and this library uses it specifically as an option to the Play() function. If needed, this option can be added to any or all of the other functions for these classes.

A Simple MCIDevice

CDAudio is a simple class for this "simple" device. In fact, all the basic functionality for simple devices is provided in the MCIDevice base class. Therefore, CDAudio doesn't need to provide additional functions of its own. It can instead rely on the operations inherited from its parent. The MCI command set for cdaudio defines a few additional options for some of the base commands, three of which are implemented here. These are Eject() to eject the disc, and SetTimeFormatMSF() and SetTimeFormatTMSF() for setting the time format (T=tracks, M=minutes, S=seconds, F=frames). Each is implemented as an inline function that passes the appropriate flags to the Set() function. By allowing just the time format to be set through these functions, we can avoid inadvertently trying to set a device to a time format that it will not accept. This lets the programmer know which formats are acceptable for each device by looking at its class definition, which cannot be determined without the documentation using the C interface.

This CDAudio device can be opened, closed, played, stopped, queried for its status, and so on. All common operations are inherited from the MCIDevice base class. Other simple devices such as Videodisc and VCR can be implemented similarly by deriving from MCIDevice, specifying the appropriate device-type string for the constructor and adding any device-specific operations.

CompoundDevice

A compound device is an MCI device that uses files. Therefore, the CompoundDevice class and its descendants must be able to handle a filename on open. Keep in mind that the device id is more like an element id, one of which is allocated for each open compound-device file. (A filename for a compound device is known as the "device element.") The constructor for CompoundDevice takes two optional arguments: the name of the file to open, and the name of the device type to be opened. The second argument is immediately passed to the base-class constructor. The first argument, if given, is stored internally and then used to open the file. This approach follows the iostream model, in which a filename passed to the constructor automatically opens the file. The implementation could easily be changed to not open the file and require a subsequent call to Open(). Not requiring a filename allows the object to be constructed first and later supply a filename on the call to Open().

The Open() and Close() virtual functions are redefined in order to deal with filenames. Open() saves the filename as part of the object's data and then passes this along in the lpstrElementName field of the mciOpenParms structure given to mciSendCommand(). The Close() command uses MCIDevice::Close to close the device and then frees any memory associated with the filename.

An example of a straightforward derivation from CompoundDevice is the Wave class. Windows supports digital audio through waveform audio files that typically use the .WAV file extension. The MCI support for waveform audio is fairly complete, allowing recording and playback of formats ranging from 8-bit mono at 11 KHz to 16-bit stereo at 44 KHz, depending on the audio card installed. The Wave class does not need to override most of the virtual functions inherited from CompoundDevice. Functions such as Play, Stop, Pause, Resume, and Seek can all be used as is. Three additional status functions have been added to allow the user to query the format of the current file: Channels(), which returns 1 if the file is mono and 2 if it is stereo; BitsPerSample(), which returns either 8 or 16; and SamplesPerSecond(), which returns 11025, 22050, or 44100 to reflect the sampling rate. These functions use extended MCI commands added as part of the waveform audio-command set.

Video as a Data Type

Audio Video Interleaved (AVI) provides scalable, software-only (with optional hardware support), full-motion digital video. The video sequences can contain images, audio, and palettes. The audio can be synchronized with the video within 1/30th of a second. Currently, application support for AVI playback is only available through an MCI interface--a low-level playback is provided in the Video for Windows Development Kit. Microsoft provides an MCI driver, MCIAVI.DRV, which implements a subset of the MCI digital-video command set for AVI.

To support this new (and large) command set, our AVI class implements a number of new functions. A Window() function was added, which takes a handle to a parent window to allow playback as the child of a specific window rather than the default behavior which is to create its own frame window. PutDestination() was added to allow the playback window to be positioned at a specific rectangle within the parent window. Other added functions include: Step(), which moves forward or backward a specified amount; Seek(), which moves to a given position; Update(), which displays the current frame; and Signal(), which notifies the parent when a particular position has been reached. This implementation could be further enhanced to redefine some of the base-class virtual functions for extensions that cannot be handled by the inherited versions, such as having a Play() that handles options to play back in a window or full screen. (Alternatively, these can be set using the Configure() function, which pops up a dialog box to set these and other options.)

This class could be modified to use three different window handles: one for the recipient of the MM_MCINOTIFY notify message after Play(), one to receive the MM_MCISIGNAL message when a Signal() position is reached, and another to be used as the parent window for display purposes using the Window() function. The present design uses up to two concurrent window handles: One is used for the Window() parent--this value is not stored as class data; the other, the hWndParent data member, is used for receiving both types of notification messages.

MciMan

I've included a sample client application, MciMan, which is available electronically (see "Availability," page 5). MciMan shows how an application might use the MCI class library. Specifically, MciMan uses the AVI and Wave classes to allow the user to work with either type of device. A simple menu interface allows you to select, play back, pause, resume, and stop the device type. A Notify toggle under the Command menu causes the user to be notified by a modal message box when the next Play command is completed. (This uses the MCI_NOTIFY option.) One file of each type can be open simultaneously.

MciMan works by creating one instance each of the Wave and AVI classes as global objects. When the user selects which type to make currently active from the MCI Device menu, a pointer to that global object is assigned to a CompoundDevice* variable. This pointer is then used for all subsequent operations and relies on the virtual-function mechanism to invoke the appropriate class function based upon the current selected type. Therefore, when the Play menu item is selected, the Play() function is invoked using this pointer and either the Wave::Play() or the AVI::Play() function will be executed. When the program is exited, the destructors for these two objects are invoked, and all devices and files are automatically closed--no special cleanup is needed in the WM_DESTROY or other similar code block.

Future Directions

Although the current implementation of the MCI class hierarchy is fairly comprehensive, it does not cover all devices nor does it implement all of the commands and their variants. In addition to MCI's large command set, the number of MCI devices is also increasing. You can, in fact, write your own MCI device drivers with the Microsoft Device Driver Kit (DDK). And as with any software library, there's always room for one more feature.

A good example of an additional device is the MCI driver that Apple is expected to supply for playing QuickTime for Windows video clips. Depending on the command set this driver supports, it may make sense to create a DigitalVideo class which could serve as the parent to both AVI and QuickTime subclasses. This class could be used to provide a set of functions common to these two specific devices, thus sharing code and giving a standard API for both types. (Client programs could then use these interchangeably.)

In any case, you now have a class library that uses C++ and the MCI command set to build a solid framework of multimedia objects for Windows. The MCI class library hides the arcane syntax of the MCI command-message interface (the messages, ids, structures, double casts, and flags) and instead provides a set of objects that give applications a cleaner, more flexible, and robust interface for controlling a variety of multimedia devices.

Table 1: Several Windows MCI devices.

===========================================================================
Device Type  Description                   Driver Files  Driver Source
===========================================================================
animation    Plays Autodesk Animator       MCIAAP.DRV    Autodesk
             (.flc/.fli) files
             Plays MacroMind Director      MCIMMP.DRV    MDK, Visual Basic
             (.mmm) files

cdaudio      Controls compact disc audio   MCICDA.DRV    Windows 3.1 SDK,MDK

dat          Controls Digital Audio Tape
             deck

digitalvideo Plays AVI video files         MCIAVI.DRV    Video for Windows

other        An undefined MCI device

overlay      Controls a video overlay      MCIVBLST.DRV  Creative Labs
             device--analog video in a
             window

scanner      Controls an image scanner

sequencer    Plays MIDI audio files        MCISEQ.DRV    Retail Windows

vcr          Controls a video cassette
             recorder

videodisc    Controls the Pioneer          MCIPIONR.DRV  Windows 3.1 SDK,MDK
             LD-V4200 videodisc player

waveaudio    Plays and records waveform    MCIWAVE.DRV   Retail Windows
             audio files
===========================================================================

Figure 1: MCI class hierarchy.

Products Mentioned
Video For Windows
Microsoft Corp.
One Microsoft Way
Redmond, WA 98052-6399
$199.00; Runtime DLL and SDK available free of charge from CompuServe (GOWINEXT)
System Requirements:
Playback: 386 SX, Windows 3.1, sound card, mouse, VGA
Capture: 50 Mbytes free hard-disk space, video-capture board

[LISTING ONE]


/*------------------------------------------------------------------------*\
 * Mci.cpp:      C++ class hierarchy for Windows multimedia objects using MCI.
 * $Author:      John Musser  $
 * $Date:        24 Feb 1993 19:37:02  $
 * $Copyright:   Personal Media International Inc., 1993 $
 * Description:  A set of classes to encapsulate the command-message interface
 *               to MCI. Currently implemented: CDAudio, Waveform audio, AVI.
 *               Two base classes MCIDevice and CompoundDevice provide most of
 *               the basic functionality needed for derived MCI types.
\*------------------------------------------------------------------------*/

#include "MCI.h"
#include <memory.h>
#include <string.h>

#define   MCI_BUFSIZE  128           // used for char array sizing

/*------ * MCIDevice functions * ------*/
MCIDevice::MCIDevice(LPSTR lpszDevice)
{
   wDeviceID      = NULL;
   hWndParent     = NULL;
   dwErrState     = NULL;
   lpszDeviceType = NULL;
   if (lpszDevice)
      SetDeviceType(lpszDevice);
}
MCIDevice::~MCIDevice()
{
   if (wDeviceID)
      Close();
   if (lpszDeviceType)
      delete [] lpszDeviceType;
}
LPSTR
MCIDevice::Info(DWORD dwFlags)
{
   MCI_INFO_PARMS mciInfoParms;
   static char cBuf[MCI_BUFSIZE];
   if (!wDeviceID)
      return (LPSTR)NULL;
   mciInfoParms.lpstrReturn = cBuf;
   mciInfoParms.dwRetSize   = MCI_BUFSIZE;
   dwErrState = mciSendCommand(wDeviceID, MCI_INFO, dwFlags,
                                     (DWORD)(LPMCI_INFO_PARMS) &mciInfoParms);
   return mciInfoParms.lpstrReturn;
}
DWORD
MCIDevice::Set(DWORD dwFlags, DWORD dwExtra)
{
   MCI_SET_PARMS mciSetParms;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   if (dwFlags & MCI_SET_TIME_FORMAT)
      mciSetParms.dwTimeFormat = dwExtra;             // dwExtra is format
   dwErrState = mciSendCommand(wDeviceID, MCI_SET, dwFlags,
                                      (DWORD)(LPMCI_SET_PARMS) &mciSetParms);
   return dwErrState;
}
DWORD
MCIDevice::Status(DWORD dwFlags, DWORD dwItem, DWORD dwExtra)
{
   MCI_STATUS_PARMS mciStatusParms;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciStatusParms.dwItem = dwItem;
   // Determine which extra struct fields to set based on flags.
   if (dwFlags & MCI_TRACK)
      mciStatusParms.dwTrack = dwExtra;
   dwErrState = mciSendCommand(wDeviceID, MCI_STATUS, dwFlags,
                                (DWORD)(LPMCI_STATUS_PARMS) &mciStatusParms);
   return mciStatusParms.dwReturn;
}
DWORD

MCIDevice::GetDevCaps(DWORD dwItem)
{
   MCI_GETDEVCAPS_PARMS mciGetDevCapsParms;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciGetDevCapsParms.dwItem = dwItem;
   dwErrState = mciSendCommand(wDeviceID, MCI_GETDEVCAPS, MCI_GETDEVCAPS_ITEM,
                       (DWORD)(LPMCI_GETDEVCAPS_PARMS) &mciGetDevCapsParms);
   return mciGetDevCapsParms.dwReturn;
}
void
MCIDevice::SetDeviceType(LPSTR lpszDevice)
{
   if (lpszDeviceType)
      delete [] lpszDeviceType;
   lpszDeviceType = new __far char[lstrlen(lpszDevice)+1];
   lstrcpy( lpszDeviceType, lpszDevice);
}
void
MCIDevice::ErrorMessageBox()
{
   char szErrorBuf[MAXERRORLENGTH];
   if (mciGetErrorString(dwErrState, (LPSTR)szErrorBuf, MAXERRORLENGTH))
      MessageBox(hWndParent, szErrorBuf, "MCI Error", MB_ICONEXCLAMATION);
   else
      MessageBox(hWndParent, "Unknown MCI Error", "MCI Error",
                 MB_ICONEXCLAMATION);
}
DWORD
MCIDevice::Open(LPSTR lpszDevice /* = NULL */)
{
   MCI_OPEN_PARMS  mciOpenParms;
   if (wDeviceID)                        // Already open don't do it again
      return MCI_WARN_ALREADY_OPEN;
   if (lpszDevice)                       // Type given, save it in private data
      SetDeviceType(lpszDevice);
   else
      if (!lpszDeviceType)               // Make sure we have a type to open,
         return MCI_ERR_NO_DEVICENAME;   // had to given here or in ctor.
   mciOpenParms.lpstrDeviceType = lpszDeviceType;
   dwErrState = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE,
                                     (DWORD)(LPMCI_OPEN_PARMS) &mciOpenParms);
   if (!dwErrState)
      wDeviceID = mciOpenParms.wDeviceID;
   return dwErrState;
}
DWORD
MCIDevice::Close()
{
   MCI_GENERIC_PARMS mciGenericParms;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   if (!(dwErrState = mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT,
                             (DWORD)(LPMCI_GENERIC_PARMS) &mciGenericParms)))
      wDeviceID = NULL;
   return dwErrState;
}
DWORD
MCIDevice::Stop()
{
   MCI_GENERIC_PARMS mciGenericParms;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   return dwErrState = mciSendCommand(wDeviceID, MCI_STOP, MCI_WAIT,
                              (DWORD)(LPMCI_GENERIC_PARMS) &mciGenericParms);
}
DWORD
MCIDevice::Pause()
{
   MCI_GENERIC_PARMS mciGenericParms;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   return dwErrState = mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT,
                              (DWORD)(LPMCI_GENERIC_PARMS) &mciGenericParms);
}
DWORD
MCIDevice::Resume()
{
   MCI_GENERIC_PARMS mciGenericParms;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   return dwErrState = mciSendCommand(wDeviceID, MCI_RESUME, MCI_WAIT,
                              (DWORD)(LPMCI_GENERIC_PARMS) &mciGenericParms);
}
DWORD
MCIDevice::Play(LONG lFrom, LONG lTo)
{
   MCI_PLAY_PARMS mciPlayParms;
   DWORD dwFlags = 0L;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   if (hWndParent) {
      mciPlayParms.dwCallback = (DWORD)(LPVOID) hWndParent;
      dwFlags |= MCI_NOTIFY;
   }
   if (lFrom != current) {
      mciPlayParms.dwFrom = lFrom;
      dwFlags |= MCI_FROM;
   }
   if (lTo != end ) {
      mciPlayParms.dwTo = lTo;
      dwFlags |= MCI_TO;
   }
   dwErrState = mciSendCommand(wDeviceID, MCI_PLAY, dwFlags,
                               (DWORD)(LPMCI_PLAY_PARMS) &mciPlayParms);
   return(dwErrState);
}
DWORD
MCIDevice::Seek(LONG lTo)
{
   MCI_SEEK_PARMS mciSeekParms;
   DWORD dwFlags = 0L;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   switch(lTo) {
      case start:
         dwFlags = MCI_SEEK_TO_START;
         break;
      case end:
         dwFlags = MCI_SEEK_TO_END;
         break;
      default:
         mciSeekParms.dwTo = (DWORD)lTo;
         dwFlags = MCI_TO;
   }
   dwErrState = mciSendCommand(wDeviceID, MCI_SEEK, dwFlags,
                                      (LONG)(LPMCI_SEEK_PARMS) &mciSeekParms);
   return dwErrState;
}
DWORD
MCIDevice::StopAll()
{
   return mciSendCommand(MCI_ALL_DEVICE_ID, MCI_STOP, 0, NULL);
}
/*-------  * CompoundDevice functions * -------*/
CompoundDevice::CompoundDevice(LPSTR lpszFile, LPSTR lpszDevice)
   : MCIDevice(lpszDevice)
{
   lpszFileName = NULL;
   if (lpszFile)
      Open(lpszFile);
}
CompoundDevice::~CompoundDevice()
{
   if (wDeviceID)
      Close();
   if (lpszFileName)
      delete [] lpszFileName;
}
DWORD
CompoundDevice::Open(LPSTR lpszFile)
{
   MCI_OPEN_PARMS mciOpenParms;
   DWORD dwFlags = 0L;
   if (wDeviceID)                   // If we're already open don't do it again
      return MCI_WARN_ALREADY_OPEN;
   lpszFileName = new __far char[lstrlen(lpszFile)+1];
   lstrcpy( lpszFileName, lpszFile);
   mciOpenParms.lpstrElementName = lpszFileName;
   mciOpenParms.lpstrDeviceType = lpszDeviceType;
   dwFlags = MCI_OPEN_ELEMENT | MCI_OPEN_TYPE;

   dwErrState = mciSendCommand(NULL, MCI_OPEN, dwFlags,
                                 (DWORD)(LPMCI_OPEN_PARMS) &mciOpenParms);
   if (!dwErrState)
      wDeviceID = mciOpenParms.wDeviceID;
   return dwErrState;
}
DWORD
CompoundDevice::Close()
{
   MCIDevice::Close();
   if (lpszFileName) {
      delete [] lpszFileName;
      lpszFileName = NULL;
   }
   return dwErrState;
}
/*------ * AVI functions * ------*/
DWORD
AVI::Update(HDC hdc)
{
   MCI_DGV_UPDATE_PARMS mciUpdateParms;
   DWORD dwFlags;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciUpdateParms.hDC = hdc;
   dwFlags = MCI_DGV_UPDATE_HDC | MCI_DGV_UPDATE_PAINT;
   dwErrState = mciSendCommand(wDeviceID, MCI_UPDATE, dwFlags,
                              (LONG)(LPMCI_DGV_UPDATE_PARMS) &mciUpdateParms);
   return dwErrState;
}
DWORD
AVI::PutDestination(RECT &rect)
{
   MCI_DGV_PUT_PARMS mciPutParms;
   DWORD dwFlags;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciPutParms.rc = rect;
   dwFlags = MCI_DGV_PUT_DESTINATION | MCI_DGV_RECT;
   dwErrState = mciSendCommand(wDeviceID, MCI_PUT, dwFlags,
                                 (LONG)(LPMCI_DGV_PUT_PARMS) &mciPutParms);
   return dwErrState;
}
DWORD
AVI::Signal(DWORD dwPosition)
{
   MCI_DGV_SIGNAL_PARMS mciSignalParms;
   DWORD dwFlags;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   if (!hWndParent)                  // we need a parent to get the message
      return MCI_ERR_NO_PARENT;
   mciSignalParms.dwCallback =(DWORD)(LPVOID)hWndParent; // MCI_SIGNAL recipient
   mciSignalParms.dwPosition =dwPosition;                // when to send
   mciSignalParms.dwUserParm =(DWORD)(LPVOID)this;       // self will be lParam
   dwFlags = MCI_DGV_SIGNAL_AT | MCI_DGV_SIGNAL_USERVAL;
   dwErrState = mciSendCommand(wDeviceID, MCI_SIGNAL, dwFlags,
                         (LONG)(LPMCI_DGV_SIGNAL_PARMS) &mciSignalParms);
   return dwErrState;
}
DWORD
AVI::Configure()
{
   MCI_GENERIC_PARMS mciGenericParms;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   return dwErrState = mciSendCommand(wDeviceID, MCI_CONFIGURE, 0,
                          (DWORD)(LPMCI_GENERIC_PARMS) &mciGenericParms);
}
DWORD
AVI::Cue(DWORD dwTo)
{
   MCI_DGV_CUE_PARMS mciCueParms;
   DWORD dwFlags;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciCueParms.dwTo = dwTo;
   dwFlags = MCI_DGV_CUE_OUTPUT | MCI_TO;
   dwErrState = mciSendCommand(wDeviceID, MCI_CUE, dwFlags,
                         (LONG)(LPMCI_DGV_CUE_PARMS) &mciCueParms);
   return dwErrState;
}
DWORD
AVI::Step(DWORD dwFrames, BOOL bReverse)
{
   MCI_DGV_STEP_PARMS mciStepParms;
   DWORD dwFlags;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciStepParms.dwFrames = dwFrames;
   dwFlags = MCI_DGV_STEP_FRAMES;
   if (bReverse)
      dwFlags |= MCI_DGV_STEP_REVERSE;
   dwErrState = mciSendCommand(wDeviceID, MCI_STEP, dwFlags,
                         (LONG)(LPMCI_DGV_STEP_PARMS) &mciStepParms);
   return dwErrState;
}
DWORD
AVI::SetAudioVolume(DWORD dwVolume)
{
   MCI_DGV_SETAUDIO_PARMS mciSetAudioParms;
   DWORD dwFlags;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciSetAudioParms.dwValue = dwVolume;
   mciSetAudioParms.dwItem  = MCI_DGV_SETAUDIO_VOLUME;
   dwFlags = MCI_DGV_SETAUDIO_ITEM | MCI_DGV_SETAUDIO_VALUE;
   dwErrState = mciSendCommand(wDeviceID, MCI_SETAUDIO, dwFlags,
                         (LONG)(LPMCI_DGV_SETAUDIO_PARMS) &mciSetAudioParms);
   return dwErrState;
}
DWORD
AVI::SetSpeed(DWORD dwSpeed)
{
   MCI_DGV_SET_PARMS mciSetParms;
   DWORD dwFlags;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciSetParms.dwSpeed = dwSpeed;
   dwFlags  = MCI_DGV_SET_SPEED;
   dwErrState = mciSendCommand(wDeviceID, MCI_SET, dwFlags,
                                  (LONG)(LPMCI_DGV_SET_PARMS) &mciSetParms);
   return dwErrState;
}
DWORD
AVI::Window(HWND hWnd)
{
   MCI_DGV_WINDOW_PARMS  mciWindowParms;
   DWORD dwFlags;
   if (!wDeviceID)
      return MCI_ERR_NOT_OPEN;
   mciWindowParms.hWnd = hWnd;
   dwFlags = MCI_DGV_WINDOW_HWND;
   dwErrState = mciSendCommand(wDeviceID, MCI_WINDOW, dwFlags,
                             (LONG)(LPMCI_DGV_WINDOW_PARMS) &mciWindowParms);
   return dwErrState;
}
End Listing


Copyright © 1993, Dr. Dobb's Journal

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.