![](/file/12652/www.mactech.com.tar/www.mactech.com/sites/all/themes/custom_front/images/you_are_here_red.gif)
![](/file/12652/www.mactech.com.tar/www.mactech.com/sites/default/files/beta-site.gif)
|
Volume Number: 19 (2003)
Issue Number: 3
Column Tag: QuickTime Toolkit
The Vision
Developing QuickTime Applications with Visual Basic
by Tim Monroe
Introduction
In the previous QuickTime Toolkit article ("Basic Instinct" in MacTech, February 2003), we took a look at using REALbasic to develop QuickTime applications. It seems fitting that we should follow that up with a look at the development environment that helped inspire REALbasic, Microsoft's Visual Basic. This is perhaps even more fitting when we learn that Visual Basic is the best selling software development tool in history, by a huge margin. Estimates of the number of its active users range from about 3 million to 15 million. Just over a year ago, Bill Gates himself claimed that there are about 8 million Visual Basic users worldwide. Even if the actual number of active developers is toward the low end of that range, and even if only a small percentage of those developers need to incorporate multimedia playback or editing capabilities in their applications, that's still a fairly substantial number of developers who could profit from using QuickTime.
Microsoft, however, does not have a history of promoting QuickTime as a multimedia creation or delivery technology. Unsurprisingly therefore, there is no built-in support within Visual Basic for working with QuickTime movies. (REALbasic, you'll recall, provides a MoviePlayer control that supports all the standard movie playback and editing operations, and it allows us to retrieve the Movie and MovieController identifiers associated with that player if we need to use QuickTime APIs that it does not support directly.) Nonetheless, Visual Basic is extensible in several ways, most importantly by allowing developers to incorporate within their applications objects that conform to the public Component Object Model (COM). One type of COM object is an ActiveX control, which can display a user interface and process events directed at that interface. We can support QuickTime in a Visual Basic application by using an appropriate ActiveX control.
In this article, we're going to work with three different ActiveX controls that can display and manage QuickTime movies within a Visual Basic application. Our goal, as in the previous few articles, is to replicate as much as possible the appearance and behavior of our sample application QTShell, which we've coded in C and built using Microsoft Developer Studio. On Windows, QTShell uses the multiple document interface (MDI) specification for creating and managing one or more movie windows within a general frame window. Figure 1 shows our Visual Basic application -- called VeeBeeMooVee -- with two open movies.
Figure 1: VeeBeeMooVee displaying two movies
I'll let the cat out of the bag right now: none of these three ActiveX controls will prove fully satisfactory for even this very minimal task of replicating the capabilities of QTShell. Two of them, however, could probably quite easily be upgraded to fit the bill, so our investigations here will not be totally for naught. And, in all fairness, it seems clear that none of them was intended to serve as a general-purpose control for handling QuickTime media within Visual Basic applications. More on this later.
One final note before we begin: the applications we develop here will be built using Visual Basic version 6.0. This version, which dates from 1998, has recently been superseded by Visual Basic .NET. I'm using the older version because it very likely still has a larger user base than the newer product, and because it's very likely easier to upgrade my code to the .NET version than to downgrade .NET code to the 6.0 version.
Visual Basic Overview
Visual Basic (or, more briefly, VB) is a rapid application development (RAD) environment introduced by Microsoft in 1991. Indeed, Visual Basic was one of the very first RAD tools on the market. What's appealing about RAD tools (as we saw in the previous article on REALbasic) is that it's phenomenally easy to construct an application's user interface. Drag some buttons into a window, add a list box or two, mix in some code, and voila: instant application.
A Visual Basic application consists of one or more forms, which are simply windows or dialog boxes. Inside each form we can place instances of user interface elements, which are collected on the Toolbox (Figure 2).
Figure 2: The Toolbox
The default Toolbox contains representations of pictures, text labels, editable text boxes, frames (that is, borders), buttons, check boxes, radio buttons, combo boxes, list boxes, scroll bars, timers, and list boxes for drives, folders, and files. We add a tool to a form by clicking on its representation in the Toolbox and then click-dragging inside the form.
We attach executable code to a form or to an instance of a tool in a form by double-clicking on the form or instance. A code window like the one in Figure 3 opens up. In this case, we double-clicked on the form itself, and Visual Basic has assumed that we want to add code that executes when the form is first loaded. There are several dozen events associated with a form in response to which we can execute code, including loading, unloading, redrawing, resizing, and so forth. (Look in the pull-down menu on the upper-right of the code window to see the pre-defined events that can trigger some code.)
Figure 3: A code window for a form
The code inserted into a code window is written in a dialect of BASIC called Visual Basic for Applications (VBA). Listing 1 shows a very simple function defined in VBA, which returns the base name of a file (that is, the portion of the full pathname that follows the rightmost path separator). Notice that we can use the underscore character ("_") as a line continuation marker.
Listing 1: Getting a file's base name
BaseName Function BaseName(fileNm As String) As String BaseName = Right$(fileNm, Len(fileNm) - _ InStrRev(fileNm, "\")) End Function
The InStrRev function returns the position within the first string of the second string, starting from the right side of the string.
We can also create collections of VBA code that are not attached to a specific object, called modules. Listing 2 shows a simple code module that defines some constants, variables, and function declarations that we can use in any code window in our project.
Listing 2: Defining global constants, variables, and functions
Module1.bas Global Const SM_CYCAPTION = 4 Global Const SM_CXFRAME = 32 Global Const SM_CYFRAME = 33 Global gIsQuitting As Boolean Declare Function GetSystemMetrics Lib "user32" (ByVal _ nIndex As Long) As Long
We'll use the gIsQuitting variable to keep track of whether our application is about to quit; the remaining items in this module will be used for determining the correct size of a movie window.
Visual Basic provides most of the standard user interface widgets that appear in Windows applications, and VBA provides a large number of built-in functions and procedures. Invariably, however, we'll want to do something that's not part of the standard Visual Basic repertoire. As I've mentioned, we can incorporate COM objects (such as ActiveX controls) in our VB applications, and we can access functions implemented in external DLLs by using the declare statement (as in Listing 2).
The Project
Let's begin by developing the central core of our VeeBeeMooVee application. Once we've done that, we can move on to take a look at each of the three ActiveX controls that provide QuickTime movie playback capabilities.
Creating a Project
First, we want to create a new project. Launch Visual Basic; it will display a dialog box (shown in Figure 4) listing the available project types. Select "Standard EXE", since we want to build a standalone executable application.
Figure 4: The New Project dialog box
Visual Basic then displays the initial form, named "Form1", as shown in Figure 5. As you can see, the default name for our new project is "Project1". Eventually we will change both of these names and also convert the initial form into an MDI child window. We'll also add a few more forms for our About box and other dialog boxes that we need to display.
Figure 5: The initial form
Adding the Parent and Child Windows
We can't configure the initial form into an MDI child window until we've got an MDI parent. To add an MDI parent window to the project, select "Add MDI Form" in the Project menu. Visual Basic displays a new form container window that holds the MDI parent window. In the Properties pane of the IDE, change the name of the MDI parent form to "MDIParent" and change the caption (that is, the window title) to "VeeBeeMooVee". To change the initial form into an MDI child window, select it and set its MDIChild property to True. While we're here, change its name to "MovieWindow".
Next, select the "Project1 Properties..." item in the Project menu. Change the project name to "VeeBeeMooVee" and set the startup object to be the MDIParent window, as shown in Figure 6. If you like, you can set the version and version information in the Make panel of this dialog box. (I just kept the default values.)
Figure 6: The project setting dialog box
When this is all done, the project explorer pane in Visual Basic's main window looks like Figure 7. Notice that the small icons for the MDI parent and child windows have changed to reflect their relationship.
Figure 7: The project explorer pane
Adding a Common Dialog Box
Now we want to add a new form to our project that will serve as the file-opening and file-saving dialog boxes. Add a new form (by selecting "Add Form" in the Project menu) and name the new form "DialogWindow". We can leave all other settings at their default values, since they are irrelevant. The reason they are irrelevant is that we are going to use this form as the container for a common dialog control, which can appear to the user as any one of the standard Windows dialog boxes for opening files, saving files, choosing colors, choosing fonts, and so forth. When we invoke one of these controls, it will automatically resize the form that contains it to the appropriate size.
To gain access to these common dialog controls, we need to add a new component to our project. In the Project menu, select the item "Components...". In the dialog box that is displayed, select the component named "Microsoft Common Dialog Control 6.0", as in Figure 8.
Figure 8: The component selection dialog box
Once we've done this, a new icon will appear in the Toolbox (Figure 9). Place an instance of this common dialog control anywhere in the new dialog window.
Figure 9: The updated Toolbox
Adding the About Box
Now we want to create our About box. Add a new form to the project and set its name to "AboutBox" and its caption to "About VeeBeeMooVee"; then add a picture, text labels, and command button so that it looks like the form in Figure 10.
Figure 10: The About box form
Double-click on the OK button and in the newly-opened code window add following line of code to the OKButton_Click procedure:
Unload OKButton.Parent
This just says to unload (that is, close) the parent of the OK button, which of course is the About box itself.
Adding Menus
We are now finished adding forms to the project. At this point, we need to set up the menu bar that is displayed in the MDI parent window. (See Figure 1 again.) We also need to add some code that is executed when the user selects the items in the menus. We won't be able to add code to all the menu items until we start working with the ActiveX controls, but we can do a few of them now.
Select the MDIParent form and then choose the "Menu Editor..." menu item in the Tools menu. Visual Basic displays the dialog box shown in Figure 11.
Figure 11: The Menu Editor tool
We add menus and menu items by filling in the Caption and Name fields and then clicking the Insert button. To place an item in a menu, add it to the list and then click the right arrow. The subordination of the item to the menu is indicated by preceding ellipses, as in Figure 12.
Figure 12: The New item in the File menu
The caption is the text of the menu or menu item, and the name is used to select the function or procedure that is executed when the user clicks on a menu or selects a menu item. For instance, when the user clicks on the File menu, Visual Basic executes our application's File_Click procedure. (This is typically a good place to enable or disable the items in the menu.) And when the user selects the New item in the File menu, Visual Basic executes our application's FileNew_Click procedure. In other words, the name of a menu or menu item is what links the menus to executable code in our application.
Add all the standard menus (File, Edit, Movie, Window, Help) and their standard items. (To add a menu separator line, add an item whose caption is the dash character, "-".) For the moment, let's add just one item to the Movie menu, "Hide Controller". Let's also add just one item to the Help menu, "About VeeBeeMooVee...". When we're done, the Menu Editor tool should look like Figure 13.
Figure 13: The Menu Editor tool (revised)
The Window menu is rather interesting. Remember that an MDI application uses this menu to display a list of the MDI child windows that are currently open. In Visual Basic, we can obtain this behavior simply by checking the WindowList check box for the Window menu. When VeeBeeMooVee is running, the Window menu will automatically be updated to list all open movie windows. Sweet.
Handling Menu Items
Most of VeeBeeMooVee's menu items operate on open movie windows and hence need to be enabled or disabled according to the state of the movies in those windows. For instance, we want to enable the Save menu item only if the movie in the frontmost movie window has been edited since it was opened or last saved. In most cases, we need to get this information from the ActiveX control that's handling our QuickTime movie playback and editing. So we'll have to wait a little bit to see how to enable and disable these menu items.
A few of our menu items are global to the application, however, so we can code them up now. In particular, when the user selects the "About VeeBeeMooVee..." menu item, our application's AboutVeeBeeMooVee_Click procedure is executed. Listing 3 shows the definition of AboutVeeBeeMooVee_Click. We just create a new instance of the AboutBox form and tell it to display itself modally. As we've seen already, the About box unloads itself when the user clicks its OK button.
Listing 3: Handling the "About VeeBeeMooVee" menu item
AboutVeeBeeMooVee_Click Private Sub AboutVeeBeeMooVee_Click() Dim aboutBx As New AboutBox aboutBx.Show vbModal End Sub
We can also handle the Quit menu item fairly easily, as shown in Listing 4. Here we just set the global variable gIsQuitting to True and unload the MDI parent window.
Listing 4: Handling the Quit menu item
FileQuit_Click Private Sub FileQuit_Click() gIsQuitting = True Unload MDIParent End Sub
Unloading the MDI parent window causes any open child windows to be unloaded as well. But before a child window is unloaded, its Form_Unload procedure is executed. This procedure is declared like this:
Private Sub Form_Unload(cancel As Integer)
Before exiting, the subroutine should set the cancel parameter to False if the window can be closed or to True if it should not be closed. Ideally, a movie window should check to see if it has been changed since last opened or saved; if it has been, it should display the usual dialog box allowing the user to save or discard any changes, or to cancel the close operation, as in Figure 14.
Figure 14: The Save Changes dialog box (application quitting)
It turns out that we don't need to add a new form to our project for this dialog box, as we can use the VBA function MsgBox to display it:
Response = MsgBox(Msg, Style, Title)
The Title parameter specifies the caption of the message box; we can set it like this:
Title = "VeeBeeMooVee"
The Style parameter specifies which buttons and icon should appear in the message box. We can get the three standard buttons and the caution icon like this:
Style = vbYesNoCancel + vbExclamation
Finally, the Msg parameter specifies the text message that should appear in the message box. Let's define the basic format as in the following line of code:
Msg = "Do you want to save changes to the document " _ + Chr(34) + "^0" + Chr(34) + " before ^1?"
34 is the ASCII code for the double-quote character, so Chr(34) is a sneaky way to insert that character into the message string. The strings "^0" and "^1" in the message text are placeholders that we'll replace with the movie's name and the operation that caused us to display the Save Changes dialog box.
MsgBox returns one of the built-in constants vbYes, vbNo, or vbCancel. We'll respond to these constants by saving the edited movie, discarding the changes, or canceling the close operation altogether. Listing 5 shows the complete definition of a movie window's Form_Unload procedure.
Listing 5: Preparing to close a movie window
Form_Unload Private Sub Form_Unload(cancel As Integer) ' called when the movie window is about to be closed Dim Msg, Style, Title, Response ' see whether the movie has changed If axCtrl.MovieChanged Then Msg = "Do you want to save changes to the document " _ + Chr(34) + "^0" + Chr(34) + " before ^1?" Msg = Replace(Msg, "^0", BaseName(axCtrl.FileName)) If gIsQuitting Then Msg = Replace(Msg, "^1", "quitting this application") Else Msg = Replace(Msg, "^1", "closing it") End If Style = vbYesNoCancel + vbExclamation Title = "VeeBeeMooVee" Response = MsgBox(Msg, Style, Title) Select Case Response Case vbNo axCtrl.FileName = "" cancel = False Case vbCancel gIsQuitting = False cancel = True Case vbYes ' save the file MDIParent.SaveFile (axCtrl.FileName) cancel = False End Select End If End Sub
You'll notice that we need to call a method implemented by the ActiveX control (here named axCtrl) to determine whether the movie has changed since opened or last saved and to determine the movie's name. Also, we call a method SaveFile, which we assume is implemented by the MDI parent.
We can handle the Open menu item in the File menu by displaying one of the common dialogs. Listing 6 shows how we do this. The key step is to invoke the ShowOpen method of the common dialog control that we put into in the dialog window.
Listing 6: Handling the Open menu item
FileOpen_Click Private Sub FileOpen_Click() Dim openDial As New DialogWindow On Error GoTo bail openDial.CommonDialog1.Filter = "All Files (*.*)|*.*|Movie Files (*.mov)|*.mov|Flash Files (*.swf)|*.swf" openDial.CommonDialog1.FilterIndex = 2 openDial.CommonDialog1.Flags = 4 ' hide the "Read Only" check box openDial.CommonDialog1.CancelError = True openDial.CommonDialog1.ShowOpen OpenFile (openDial.CommonDialog1.FileName) Unload openDial Exit Sub bail: ' the user pressed the Cancel button Unload openDial Exit Sub End Sub
As you can see, we pass the name of the file selected by the user to the OpenFile procedure. This procedure must contain code that is specific to each ActiveX control, so we'll look at implementing it later.
QuickTime Availability
We're actually getting a little bit ahead of ourselves here. Before we even begin to handle menu items and open any movie files, we first need to check the operating system to see whether QuickTime is installed. If it's not, there is no point in doing anything else in VeeBeeMooVee; in that case, we'll just tell the user that QuickTime isn't installed and exit. A good time to make this check is when our MDI parent window is loaded. Listing 7 shows our implementation of MDIForm_Load.
Listing 7: Loading the MDI parent window
MDIForm_Load Private Sub MDIForm_Load() ' check for the availability of QuickTime If Not IsQuickTimeInstalled() Then MsgBox "QuickTime is not installed! Exiting." Unload Me End If gIsQuitting = False End Sub
How can we determine whether QuickTime is installed on a particular computer? On the Macintosh, we can call the Gestalt function with the gestaltQuickTimeVersion selector and check to see whether the result code noErr is returned by Gestalt. On Windows, although the Gestalt function is available, it's available only as part of the QuickTime Media Layer. So we need some alternate strategy to see whether QuickTime is available.
QuickTime versions 4.1.1 and later install the file QuickTimeCheck.ocx, which contains an automation object that we can query to determine whether QuickTime is installed and, if so, what version is available. An automation object is a kind of COM object that provides some specific methods and properties. Unlike ActiveX controls, which are also COM objects, automation objects don't have any user interface. Rather, we can just create an instance of the object (using the CreateObject function) and execute its methods.
The QuickTimeCheck automation object supports two principal methods, IsQuickTimeAvailable and QuickTimeVersion, which return pretty much what you would expect. The IsQuickTimeAvailable method returns a Boolean value that indicates whether QuickTime is available; we use it in the IsQuickTimeInstalled function, defined in Listing 8.
Listing 8: Determining whether QuickTime is available
IsQuickTimeInstalled Function IsQuickTimeInstalled() As Boolean Dim theObject As Object Dim isQTAvail As Boolean isQTAvail = False On Error Resume Next ' do not report runtime error if object was not installed Set theObject = _ CreateObject("QuickTimeCheckObject.QuickTimeCheck") On Error GoTo 0 ' allow runtime errors If IsObject(theObject) Then If theObject.IsQuickTimeAvailable(1) Then isQTAvail = True End If End If IsQuickTimeInstalled = isQTAvail End Function
The argument passed to the IsQuickTimeAvailable method indicates whether we want QuickTime to be initialized (1) or whether we just want to check that the appropriate QuickTime files are installed on the user's machine (0). Passing 1 will of course take longer, but VeeBeeMooVee certainly needs QuickTime to be initialized at some point, so we may as well do it early on.
The QuickTimeVersion method returns a value of type Integer that indicates the version of QuickTime that is installed. QuickTimeVersion first initializes QuickTime (by calling IsQuickTimeAvailable with an argument of 1) and then calls Gestalt with the gestaltQuickTimeVersion selector. Accordingly, the value it returns is in the format used by Gestalt: the high-order word encodes the version number and the low-order word encodes the release state. For instance, the value 0x06018000 indicates that the release version (0x8000) of QuickTime version 6.0.1 (0x0601) is installed. Of course, the actual value returned will be in decimal form (100761600); you'll need to perform the decimal-to-hexadecimal conversion to extract the version information.
Remember that the QuickTimeCheck automation object is available only in QuickTime versions 4.1.1 and later. If we call our function IsQuickTimeInstalled on systems running QuickTime 3, for instance, it will return False. As QuickTime 3 is getting pretty long in the tooth, this may not be a significant problem.
The Apple QuickTime ActiveX Control
Finally we can turn our attention to using ActiveX controls to support QuickTime movie playback and editing within our Visual Basic application. I am aware of three readily available controls. The easiest to obtain, at least for QuickTime versions 5.0.3 and later, is the QuickTime ActiveX control, provided by Apple. (It's easy to obtain because it's installed as part of the QuickTime installation process; users running earlier versions of QuickTime can visit the QuickTime web site, where they will be automatically prompted to download the control.) I should say at the outset that this ActiveX control was designed to support QuickTime movie playback within web browsers running on Windows and does not provide a complete solution for displaying and editing QuickTime movies in our VeeBeeMooVee application. Nonetheless, it is instructive to play with this control, if only briefly.
To make the QuickTime ActiveX control available within Visual Basic, we need to explicitly load the control into the Toolbox, in somewhat the same fashion we earlier loaded the common dialog control. Select the "Components..." menu item in the Project menu and click the "Browse..." button. In the file-selection dialog box, navigate to the QuickTime folder within the "Program Files" folder. Select the file QTPlugin.ocx (as in Figure 15) and click the Open button.
Figure 15: The QuickTime ActiveX control
At this point, the Apple QuickTime Active X control is added to the list of available components under the name "Apple QuickTime Control" (Figure 16). Make sure it's checked and then click OK.
Figure 16: The component selection dialog box
An icon representing the QuickTime ActiveX control now appears in the Toolbox, as you can see in Figure 17.
Figure 17: The Toolbox with the QuickTime ActiveX control
Now we can click that new icon and then click-drag within a movie window to place an instance of the ActiveX control in the window. Size the control to fill the content area of the movie window.
As we saw earlier, VeeBeeMooVee needs to define the OpenFile procedure, which is called by the FileOpen_Click method. Listing 9 shows how we can define that procedure for the QuickTime ActiveX control.
Listing 9: Opening a file (QuickTime ActiveX control)
OpenFile Sub OpenFile(fileNm As String) Dim movieWind As New MovieWindow If Len(fileNm) = 0 Then movieWind.Caption = "Untitled" Else movieWind.Caption = BaseName(fileNm) movieWind.QTActiveXPlugin1.SetURL (fileNm) ' set the movie window size here End If movieWind.Show End Sub
We create a new movie window and, if passed a non-empty filename, execute the control's SetURL method to assign the selected file to the ActiveX control.
We also need to set the size of the movie window to exactly hold the movie in its content area. With the QuickTime ActiveX control, it'll take a little bit of work to determine the appropriate window size. This control supports the GetRectangle method, which returns the location and dimensions of the movie in the form of a string. Here's a sample call to GetRectangle:
Dim mvRect As String mvRect = movieWind.QTActiveXPlugin1.GetRectangle
This call returns a string of the form: "61,-20,251,220". We need to parse this string and set the size of the movie window accordingly. Perhaps the easiest course is to split the string into an array, using the Split function:
Dim numArray As Variant numArray = Split(mvRect, ",")
Then we can get the height and width of the movie by subtracting the appropriate array elements:
mvWid = numArray(2) - numArray(0) mvHgt = numArray(3) - numArray(1)
If the controller bar is visible, we need to increase the desired height of the movie window:
If movieWind.QTActiveXPlugin1.GetControllerVisible Then mvHgt = mvHgt + 16 End If
Now we can set the height and width of the movie window:
With movieWind .Width = (mvWid + (2 * GetSystemMetrics(SM_CXFRAME))) _ * Screen.TwipsPerPixelX .Height = (mvHgt + (2 * GetSystemMetrics(SM_CYFRAME) _ + GetSystemMetrics(SM_CYCAPTION))) _ * Screen.TwipsPerPixelY End With
Notice that we need to take into account the caption bar at the top of a movie window and the frame borders at the left, right, top, and bottom edges. That's because a window's Width and Height properties are the size of the entire window, not the size of its content area. Also, these properties are not expressed in pixels but in twips. We can access the global Screen object to determine the number of twips per pixel in the horizontal and vertical directions.
Finally, we need to reset the movie rectangle. The QuickTime ActiveX control keeps the movie centered within the object it's embedded in, even if the size of that object changes. So let's recalculate the rectangle; first, subtract out the height of the controller bar (if it's visible):
If movieWind.QTActiveXPlugin1.GetControllerVisible Then mvHgt = mvHgt - 16 End If
And then construct a new string whose top-left corner is at 0,0:
mvRect = "0,0," + Str(mvWid) + "," + Str(mvHgt) movieWind.QTActiveXPlugin1.SetRectangle (mvRect)
If all goes well, the movie will exactly contain the movie and the movie controller bar within its content area.
The QuickTime ActiveX control provides a fairly large number of methods in addition to the three we've used so far. Figure 18 shows the Object Browser window for this control, which reveals a smattering of the methods for setting properties.
Figure 18: The Object Browser window for the QuickTime ActiveX control
Unfortunately, Apple's QuickTime ActiveX control does not provide any methods for editing the movie it's displaying, and this alone makes it less than ideal for use in VeeBeeMooVee. Nor does it provide access to either the Movie or MovieController identifiers of that movie (which we could use in calling external functions).
The SkyLight ActiveX Control
A better choice for VeeBeeMooVee is the SkyLight ActiveX control, written by John Cromie of Skylark Associates Ltd. This is a commercial product that is available in two versions, a "Lite" version and a "Pro" version. The Lite version supports basic movie playback, but it does not support movie editing. The Pro version include more properties and methods than are available in the Lite version, including editing; it also provides extensive support for manipulating QuickTime VR movies. Happily, we can download and use demo versions of these controls; they provide the full functionality of the commercial versions but display a nag dialog box when the control is first instantiated (Figure 19).
Figure 19: The nag dialog box for the SkyLight ActiveX control
Installation is a snap; we just need to launch the downloaded installer, which puts the control into the proper location and registers it with the operating system. We still need to load the control into our Visual Basic project, just as we did with the QuickTime ActiveX control. When we've done this, the SkyLight icon appears in the Toolbox (Figure 20).
Figure 20: The Toolbox with the SkyLight ActiveX control
Add an instance of this control to the MDI child form.
Setting the Size of a Movie Window
It's easier to get the natural height and width of a movie with the SkyLight ActiveX control, as it provides two methods, NaturalHeight and NaturalWidth, which return values as numbers, not as strings. We can use these methods in our OpenFile routine, as shown in Listing 10.
Listing 10: Opening a file (SkyLight control)
OpenFile Sub OpenFile(fileNm As String) Dim movieWind As New MovieWindow movieWind.SkyLight1.MovieControllerVisible = True movieWind.SkyLight1.BorderVisible = False movieWind.Visible = False If Len(fileNm) = 0 Then movieWind.SkyLight1.MovieNew movieWind.Caption = "Untitled" Else movieWind.SkyLight1.FileName = fileNm movieWind.Caption = BaseName(fileNm) End If With movieWind .Width = (.SkyLight1.NaturalWidth + _ (2 * GetSystemMetrics(SM_CXFRAME))) * _ Screen.TwipsPerPixelX .Height = (.SkyLight1.NaturalHeight + _ (2 * GetSystemMetrics(SM_CYFRAME) + _ GetSystemMetrics(SM_CYCAPTION))) * _ Screen.TwipsPerPixelY If .SkyLight1.MovieControllerVisible Then .Height = .Height + (16 * Screen.TwipsPerPixelY) End If End With movieWind.Show End Sub
Editing Movies
The SkyLight Pro control provides methods that we can use to edit movies and to determine whether a movie has been edited. Listing 11 defines our Edit_Click function, which is called when the user clicks on the Edit menu. As you can see, we take this opportunity to adjust the state of the Edit menu items.
Listing 11: Adjusting the Edit menu
Edit_Click Private Sub Edit_Click() ' adjust the Edit menu items Dim isEditable As Boolean isEditable = True If MDIParent.ActiveForm Is Nothing Then isEditable = False Else If MDIParent.ActiveForm.SkyLight1.Interactive Then isEditable = False End If End If If isEditable Then If MDIParent.ActiveForm.SkyLight1.MovieChanged Then EditUndo.Enabled = True Else EditUndo.Enabled = False End If EditCut.Enabled = True EditCopy.Enabled = True EditPaste.Enabled = True EditClear.Enabled = False ' no Clear method in current SkyLight EditSelectAll.Enabled = True EditSelectNone.Enabled = True Else EditUndo.Enabled = False EditCut.Enabled = False EditCopy.Enabled = False EditPaste.Enabled = False EditClear.Enabled = False EditSelectAll.Enabled = False EditSelectNone.Enabled = False End If End Sub
There are a couple of things to notice here. First, we can find the frontmost movie window by using the ActiveForm method of the MDI parent window. If there is no such active form, or if it holds an interactive movie, we disable all the items in the Edit menu. Otherwise, we enable the items in the Edit menu. SkyLight does not support a Clear method, so we've disabled the Clear item even if editing is enabled for the movie.
Second, and like the Apple QuickTime ActiveX control, SkyLight does not provide any way to retrieve the Movie or MovieController identifiers associated with the movie displayed in a window. If we did have this information, we could call the MCGetControllerInfo function to get a better indication of the edit state of a movie.
Now we need to handle items in the Edit menu. SkyLight provides a handful of methods for performing the standard editing operations (except, as already noted, the Clear operation). For instance, we can handle the Cut menu item as shown in Listing 12. The Copy, Paste, Undo, and "Select All" items can be handled similarly.
Listing 12: Handling the Cut menu item
EditCut_Click Private Sub EditCut_Click() MDIParent.ActiveForm.SkyLight1.Cut End Sub
There is no built-in support for the "Select None" menu item, but we can easily achieve the desired end by setting the start and stop times of the current selection (Listing 13).
Listing 13: Handling the "Select None" menu item
EditSelectNone_Click Private Sub EditSelectNone_Click() MDIParent.ActiveForm.SkyLight1.StartTime = 0 MDIParent.ActiveForm.SkyLight1.StopTime = 0 End Sub
Manipulating Files
Now that we can edit movies, we need to adjust the state of the items in the File menu according to the edit state of the frontmost movie window, and of course we need to handle those items. The key method here is the MovieChanged method, which we encountered just above. Listing 14 shows our definition of the File_Click method.
Listing 14: Adjusting the File menu
File_Click Private Sub File_Click() ' adjust the File menu items FileNew.Enabled = True FileOpen.Enabled = True FileQuit.Enabled = True If MDIParent.ActiveForm Is Nothing Then FileClose.Enabled = False FileSave.Enabled = False FileSaveAs.Enabled = False Else FileClose.Enabled = True If MDIParent.ActiveForm.SkyLight1.MovieChanged Then FileSave.Enabled = True Else FileSave.Enabled = False End If FileSaveAs.Enabled = True End If End Sub
Actually, SkyLight does not allow us to save an edited movie into the same file we opened it from. (In other words, SkyLight appears to open movie files with read-only access.) So the enabling and disabling of the Save menu item here is more or less wishful thinking on our part. This is the way we'd like to handle things, if SkyLight actually allowed us to save a movie. I hope that a future version of SkyLight will allow movie files to be opened with read/write access.
SkyLight does allow us to save an edited movie into a different file, using its MovieSaveAs method. Listing 15 shows how we handle the "Save As" menu item.
Listing 15: Handling the "Save As" menu item
FileSaveAs_Click Private Sub FileSaveAs_Click() Dim saveDial As New DialogWindow saveDial.CommonDialog1.FileName = _ MDIParent.ActiveForm.SkyLight1.FileName saveDial.CommonDialog1.ShowSave If saveDial.CommonDialog1.FileName <> "" Then MDIParent.ActiveForm.SkyLight1.MovieSaveAs _ (saveDial.CommonDialog1.FileName) ' close the current file and open the new file MDIParent.ActiveForm.SkyLight1.FileName = "" DoEvents MDIParent.ActiveForm.SkyLight1.FileName = _ saveDial.CommonDialog1.FileName MDIParent.ActiveForm.Caption = BaseName(saveDial.CommonDialog1.FileName) End If Unload saveDial End Sub
Here we invoke the ShowSave method of the common dialog control to display the standard file-saving dialog box. If the user actually selects a file (that is, if the FileName property is not empty), we call SkyLight's MovieSaveAs method to save the movie into the specified location. Then we need to close the current movie and open the new movie in the same window. Notice that we call the built-in Visual Basic method DoEvents to give SkyLight a chance to fully unload the current movie.
(It might be possible to implement the Save menu item by saving an edited movie under a new name -- that is, using SaveMovieAs -- and then using Windows APIs to rename the new file under the existing file name. I played around with this idea briefly but could not get it to work quite right. A more talented or persistent reader might have better success than I did.)
Operating on Movies
Let's take a quick look at how we can use the SkyLight ActiveX control to handle the "Hide Controller" item in the Movie menu. First we need to enable or disable that item according to the current state of the application, and we need to adjust the item text. The Movie_Click function defined in Listing 16 handles these operations.
Listing 16: Adjusting the Movie menu
Movie_Click Private Sub Movie_Click() If MDIParent.ActiveForm Is Nothing Then MovieHideController.Enabled = False MovieHideController.Caption = "Hide Controller" Else MovieHideController.Enabled = True If MDIParent.ActiveForm.SkyLight1._ MovieControllerVisible Then MovieHideController.Caption = "Hide Controller" Else MovieHideController.Caption = "Show Controller" End If End If End Sub
Nothing too exciting here, or in the MovieHideController_Click function (Listing 17), which handles user selections of the "Hide Controller" menu item.
Listing 17: Handling the "Hide Controller" menu item
MovieHideController_Click Private Sub MovieHideController_Click() MDIParent.ActiveForm.SkyLight1.MovieControllerVisible = _ Not MDIParent.ActiveForm.SkyLight1.MovieControllerVisible End Sub
The QTVRControlX ActiveX Control
The final QuickTime-savvy ActiveX control that I am aware of is the QTVRControlX control written by George Birbilis. This control was built with Borland's Delphi, a version of the Object Pascal language that is coupled with a RAD environment quite reminiscent of Visual Basic. This too is a commercial product, but once again it's available for free demos -- at the slight cost of the user having to endure a nag dialog box when the control is used (Figure 21).
Figure 21: The nag dialog box for the QTVRControlX ActiveX control
The QTVRControlX control provides a number of methods that are specific to QuickTime VR movies (hence the name), but it works quite well with other kinds of QuickTime movies.
The installation is straightforward. As with the two ActiveX controls that we've already considered, we need to install this control into our Visual Basic project. When we do that, we'll see three new icons in the Toolbox (Figure 22).
Figure 22: The Toolbox with the QTVRControlX ActiveX controls
The latest version of this control is the one whose icon is the clipped "QTVR" icon and whose type is QTVRControlX2. That's the one I've worked with most.
Much of the code that we've already developed can be adapted for use with the QTVRControlX control simply by changing a few method names. For instance, the SkyLight method MovieControllerVisible is just ControllerVisible in QTVRControlX. So we would rewrite Listing 17 above like this:
Listing 18: Handling the "Hide Controller" menu item
MovieHideController_Click Private Sub MovieHideController_Click() MDIParent.ActiveForm.QTVRControlX21.ControllerVisible = _ Not MDIParent.ActiveForm.QTVRControlX21.ControllerVisible End Sub
The QTVRControlX control does not support any editing operations, but it does provide methods that return the Movie and MovieController identifiers of the movie displayed in a movie window. For instance, we can get MovieController identifier like this:
Dim mC As Long mC = QTVRControlX21.MovieControllerHandle
It should therefore be straightforward to implement basic movie editing operations ourselves -- or indeed any other operations involving QuickTime APIs. However, I was never able to successfully use the value returned by the MovieControllerHandle method to call external QuickTime functions. It's quite possible that my declare statements were flawed in some way, or that some other minor confusion was tripping me up. Perhaps at some later date I'll be able to report back with more success.
Conclusion
Where do we stand in terms of building a multi-window movie playback and editing application using Visual Basic? How complete can we make VeeBeeMooVee using off-the-shelf ActiveX controls? All three of the controls we've considered here are quite sufficient for displaying QuickTime movies in windows inside an MDI frame window, and each of them provides a fairly broad assortment of methods for controlling movie playback. Only the SkyLight control offers built-in support for editing linear QuickTime movies (though it lacks the all-important ability to save an edited movie into the same file it was opened from). The QTVRControlX control is potentially the most powerful ActiveX control of the three, as it provides access to the Movie and MovieController identifiers of a movie. However, as I just noted, I've had no success in actually using those identifiers to call external QuickTime functions.
So, for the moment, VeeBeeMooVee must remain fairly well developed but annoyingly unfinished. It would be nice if the SkyLight control would support saving an edited movie and provide us with the Movie and MovieController identifiers. Alternatively, it would be nice if the QTVRControlX control would add methods for simple editing and some more complete documentation on using the Movie and MovieController identifiers in a Visual Basic application. Of course, some altogether new ActiveX control might appear that would provide all these capabilities and more. Or maybe I'm just having visions.
Credits
Thanks are due to Eric Carlson, John Cromie, and George Birbilis for helping me understand the various QuickTime-savvy ActiveX controls.
Tim Monroe in a member of the QuickTime engineering team. You can contact him at monroe@apple.com. The views expressed here are not necessarily shared by his employer.
![](/file/12652/www.mactech.com.tar/www.mactech.com/sites/all/themes/custom_front/img/search_text.gif)
- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine