TweetFollow Us on Twitter

The Vision

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.

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.