David A. Feinleib
Created: August 16, 1993
Click to open or copy the files in the AVI sample application.
The AVI Hotspot Editor (AVIHED.EXE) and its accompanying dynamic-link libraries (DLLs) AVIHVWR.DLL and AVIHAPP.DLL provide you with the ability to specify hot spots for audio-video interleaved (AVI) files, much as you can specify hot spots for device-independent bitmap (DIB) files with the segmented hypergraphics hot-spot editor (SHED.EXE). The AVI hot-spot kit can be used with Microsoft® Multimedia Viewer or with a stand-alone application by using the correct DLL.
The AVI Hotspot Editor allows you to draw hot spots easily on your AVI file and save them in a hot-spot information file, which you specify when you call the hspPlayAVI function in the AVI hot-spot DLL.
The AVI hot-spot kit allows you to:
You use the AVI Hotspot Editor to create and edit the hot spots for an audio-video interleaved (AVI) file. Hot spots can cover overlapping areas in an AVI file as long as their Begin and End frames do not overlap. For each hot spot, you specify a command string, a hot-spot ID, and (optionally) a beginning and ending frame.
After loading the Hotspot Editor, choose File Open to open an AVI file. You will be prompted for a hot-spot (.INI) file. If you are creating hot spots for the first time for the specified AVI file, choose Cancel because you do not need to open an .INI file.
Once the AVI file is loaded, you can start drawing hot spots on this file. To do so, click the left mouse button, hold it down, and draw the hot spot that you want. To specify hot-spot attributes, double-click inside the rectangle you have created. To adjust the position of a hot spot, put the mouse cursor inside the rectangle, click the mouse button, and move the rectangle while holding the button down. To change the size of the hot spot, click and drag one of the hot spot's edges. You can delete a hot spot by clicking inside its rectangle and pressing the Delete key or choosing Delete Specified Hotspot from the Hotspots menu.
When a hot spot is selected, its rectangle coordinates will appear in the Selected Hotspot Info window.
In the Hotspot Attributes dialog box, you can specify the following information:
Once you have drawn hot spots for the specified AVI file, make sure you have specified a hot-spot ID and, if applicable, a command string for each hot spot. Then choose Save from the File menu and enter a filename. If you have not specified an ID for each hot spot, you will receive an error message.
The kit allows you to specify different active frames for each hot spot, so you can choose to see only the hot spots that are active for the frame currently displayed in the editor (choose the Only Show Hotspots in Current Frame option from the Hotspots menu) or all hot spots in the AVI file, independent of the frame displayed.
The AVI Hotspot Editor writes the hot-spot information file using WritePrivateProfileString, so all entries in the file have the regular .INI file format. The .INI file must contain a [Configuration] section, a [Hotspots] section, and information for at least one hot spot. Each hot spot is saved under its hot-spot ID.
The [Configuration] section has the format:
[Configuration]
Version=1.00
Editor=AVIHED
The current version number is 1.00. The Editor entry will normally be AVIHED.
The [Hotspots] section is in the form: HotspotID=1. Hot-spot IDs can be anything as long as =1 is specified, for example:
[Hotspots]
Ears=1
Eyes=1
Door=1
Window=1
For each entry in the [Hotspots] section, there must be a corresponding hot-spot entry, for example:
[Door]
Rect=247,17,281,71
Command=sndPlaySound(`n.wav', 1)
BeginFrame=0
EndFrame=101
OnClick=1018
ToFrame=0
where:
The most effective way I have found for drawing on top of an AVI file is to have Video for Windows™ paint the AVI file to a window that you have created. When you receive a WM_PAINT message in your window procedure, use the MCI_UPDATE command to update the window, given the handle of the device context returned by BeginPaint. After the command returns (using the MCI_WAIT flag), draw the hot-spot rectangles (or whatever you want to draw).
The editor uses the MCI_STOP command to stop playing the AVI file instead of the MCI_PAUSE command, because using MCI_PAUSE will result in incorrect painting of the hot-spot rectangles. Even if you use MCI_PAUSE with the MCI_WAIT flag, Video for Windows finishes painting after the mciSendCommand when MCI_PAUSE returns, which results in the rectangles being hidden. You could also use MCI_STEP to step to the current frame, although MCI_STOP does the job in one step.
If you want to use a window that Video for Windows creates instead of your own, you will need to subclass Video's window. Do this by getting the AVI window from the return information provided by the MCI_OPEN command. In the subclass procedure, intercept the WM_PAINT message and then call mciSendCommand with the MCI_UPDATE command there. Do not call the old window procedure because this will result in the output you have drawn being hidden beneath the AVI file.
Unfortunately, there is still no good way to keep drawn objects on top of an AVI file while it is playing. Doing so on a frame-by-frame basis would slow down the display of the AVI file and cause flicker.
Originally, I used the rectangle painting function in the Microsoft® Windows Software Development Kit (SDK) SHOWDIB sample. However, when I started dealing with AVI files that used very light colors or a variety of colors, inverting the hot-spot rectangles did not show them clearly enough. Using PatBlt after selecting a multicolored brush into the device context (DC) made inverting much clearer. Another possibility would be to store an off-screen DC of the current frame, draw colored lines onto the window DC, and then BitBlt from the off-screen DC onto the window when a line’s position changes. This would be possible by calling mciSendCommand to have Video for Windows paint the current frame to an off-screen DC, but this process could be time-consuming, resulting in slow rectangle movement.
There are two AVI hot-spot dynamic-link libraries (DLLs): AVIHVWR.DLL, which can be used with Viewer applications by specifying it in the Viewer application's MVP project file, and AVIHAPP.DLL, which you can link with your stand-alone application. Visual Basic® support has not yet been implemented, but suggestions on how to do so are described in the “Suggested Improvements” section later in this article.
Because there can be only one instance of each DLL but multiple applications or instances of an application calling each DLL, the hot-spot DLL associates a structure with each application (instance) that calls it. The structure it uses is called a MOVIEINFO structure, defined in HOTSPOT.H. There are two ways to associate information with a window: Either write your own functions to match up a global handle to data with a window handle and have a list of these associations, or use the SetProp/GetProp/RemoveProp functions, which allow you to specify 16-bit values (such as handles to globally allocated memory) for a window. The DLLs’ exported function hspPlayAVI calls SetProp; the subclass procedure calls GetProp every time it is called so that it can obtain information about the movie, such as the address of the old procedure to call. Finally, when the subclass procedure receives a WM_DESTROY message, it calls RemoveProp to remove the movie information from the window.
Although the DLL can be used with Visual Basic already, unfortunately, it has no way to send information back to a Visual Basic application when a hot spot is selected. To send a message to the Visual Basic application using SendMessage or to use a callback function from the DLL to the application (as done with non-Visual Basic stand-alone applications), you must either add basic VBX functionality to the DLL or you must write a separate VBX that can export callbacks from a Visual Basic application or intercept new messages (messages that are not already implemented in Visual Basic) by subclassing the form on which it is placed.
The hot spots for an AVI file are read in from an .INI file and stored in a doubly-linked list. Each time the user clicks in the movie window (causing a WM_LBUTTONDOWN message to be sent), the subclass procedure calls a function that calls the PtInRect function for each hot spot in the list to determine which hot-spot rectangle, if any, the mouse cursor was positioned in when the user pressed the mouse button. While going through the hot-spot list, before determining whether the point was in a hot-spot rectangle, the function checks to see if the hot spot is valid for the currently displayed frame, that is, if the current frame is between the beginning and ending frames for the hot spot.
For the Viewer DLL (AVIHVWR.DLL), the function calls VwrCommand as follows:
VwrCommand (VwrFromHinst (GetWindowWord (hwnd, GWW_HINSTANCE) ),
NULL, pHotspot->pszCommand, cmdoptNONE)
Because Viewer requires a VWR structure to be specified as the first parameter to VwrCommand, the function obtains the instance handle by using GetWindowWord; the hwnd parameter is obtained from the following calls:
hwnd = GetParent(pMovieInfo->hwndParent);
if (!hwnd) hwnd = pMovieInfo->hwndParent;
where pMovieInfo is a pointer to a MOVIEINFO structure passed to the function from the subclass procedure.
For the stand-alone application DLL (AVIHAPP.DLL), the DLL executes the callback function passed as the last parameter to the hspPlayAVI DLL function. The callback function is defined in the application by exporting a function in the .DEF file and then calling MakeProcInstance and passing its return value to the hspPlayAVI function. When the application receives the WM_DESTROY message, it should call FreeProcInstance. Using SendMessage with a WM_USER+xxxx message or (as a hack) WM_COMMAND with a certain value for wParam is another possibility, but a callback appears to be the most straightforward and efficient solution.
There are separate DLLs for Viewer (AVIHVWR.DLL) and for stand-alone applications (AVIHAPP.DLL).
To use the hot-spot DLL with Viewer, specify the following command in the [CONFIG] section of the your Viewer application's .MVP project file:
RegisterRoutine("AVIHVWR","hspPlayAVI","I=USS")
In your Viewer .RTF file, specify some text or bitmap; then specify the following in hidden text:
!hspPlayAVI(hwndContext,'AVIFILE.AVI','HSPFILE.INI')
where AVIFILE.AVI is the name of the AVI file to play and HSPFILE.INI is the name of the INI file containing hot-spot information for the AVI file. hwndContext is a Viewer-defined variable; see the Viewer documentation for further details.
To use the hot-spot DLL with stand-alone applications, add AVIHAPP.LIB to your link statement. Include AVIHAPP.H, which defines the prototype for the hspPlayAVI function. To call the hspPlayAVI function, you will need to export a callback function that the DLL will call to notify you that a hot spot has been selected. To play an AVI file with hot spots, call the hspPlayAVI function with the name of the AVI file as the first parameter, the name of the .INI file containing hot-spot information as the second parameter, and the address returned from MakeProcAddress called with the name of your exported callback function. When your application's window receives a WM_DESTROY message, it should call FreeProcAddress.
The callback function must have the following syntax:
BOOL __export hspAVICallback (HWND hwndParent, HWND hwndMovie, WORD wMessage,
FAR * lParam1, LONG FAR *lParam2)
The callback should handle the WM_SIZE and and WM_LBUTTONDOWN messages. (Note that these are not used as messages but as command identifiers, defining what action should be taken on the given parameters, if any.) If the callback processes a message successfully, it should return TRUE; if it does not, it should return FALSE.
If wParam is WM_SIZE: The callback is being given an opportunity to adjust the size of the window in which the movie will be played before the movie is displayed. lParam1 is a far pointer to a RECT structure that gives the dimensions of the movie.
If wParam is WM_LBUTTONDOWN: The user has selected a hot spot. lParam1 is an LPSTR containing the hot-spot ID; lParam2 is an LPSTR containing the command for the hot spot.
The AVIHRUN sample program demonstrates how to call hspPlayAVI, how to implement a callback function, and how to process the messages in the callback function. In addition, it implements a simple command language, which supports the following two commands:
Both commands have the same syntax as their Viewer counterparts.
The AVI Hotspot Editor and DLLs provide the framework and basic but usable functionality for an AVI hot-spot interface in Viewer and stand-alone applications. Suggested improvements to the current code include: