TweetFollow Us on Twitter

Jan 00 QTToolkit

Volume Number: 16 (2000)
Issue Number: 1
Column Tag: QuickTime Toolkit

QuickTime Toolkit

by Tim Monroe

Using Movie Controllers for QuickTime Movie Playback and Editing

The State of the Art

It's been almost ten years since Apple introduced QuickTime, its software architecture for creating and playing back multimedia content. In that time, QuickTime has progressed from a Mac-only tool for playing movies roughly the size of postage stamps into the de facto standard on Macintosh and Windows computers for the creation, delivery, and playback of digital media, including video, sound, music, 3D graphics, virtual reality, sprite animation, and more. The range of abilities that QuickTime has gained over the years is truly staggering. It now supports real-time streaming of audio and video over the Internet and LANs, a full-featured video effects and transitions architecture, vector drawing capabilities, and the ability to associate actions to sprites, text, hot spots in QuickTime VR movies, and 3D objects.

It's high time, then, that MacTech should begin to devote some regular space to discussing QuickTime from a programmer's point of view, and this is the first of a series of articles that will focus on using the QuickTime application programming interfaces to create and play back digital media. Given the wide range of capabilities provided by the QuickTime APIs, you can guess that there will be no shortage of topics for us to investigate over the coming months and years. Admittedly, QuickTime can perhaps be a bit overwhelming if you try to understand it all at once. So we'll adopt a fairly leisurely approach and try to dissect the QuickTime architecture a little bit at a time, starting with its high-level interfaces and then gradually working down to lower-level capabilities to add to our knowledge of the entire architecture. Eventually, we'll be creating QuickTime files and writing custom QuickTime components just like the pros.

In this article, we'll learn how to open and display QuickTime movies, and how to manage the user's interactions with those movies (such as starting and pausing movies, zooming in and out in QuickTime VR movies, and similar operations). This is a relatively simple task, and it's one that involves adding a fairly small amount of code to a basic working application. So, to really earn our money today, we're going to complicate things. In addition to showing how to support basic movie playback and editing in a Macintosh application, we're also going to show how to do it in an application that runs on Microsoft Windows. In other words, we want the code we write here to compile and execute both on the Macintosh operating system and on the major flavors of the Windows operating system (to wit: Windows 95, Windows 98, and Windows NT).

Now this might sound like heresy, especially for a publication like MacTech whose mission is to report on and facilitate software development for Macintosh computers. But it's really just as consistent with MacTech's mission as its regular coverage of the Internet or of Java and other cross-platform languages and tools. Moreover, it's fair to say that the cross-platform parity exhibited by the QuickTime application programming interfaces since version 3.0 is largely responsible for QuickTime's recent explosion in popularity and is crucial for its continued success. So, everything we're going to do in these articles will be completely cross-platform.

In point of fact, however, once you learn to pay attention to a few recurring issues like the endianness of multi-byte data, writing QuickTime code that is compatible with multiple platforms really isn't so hard. Indeed, part of the reason that QuickTime runs so well on Windows is that a good bit of the Macintosh programming concepts (including handles, resources, and file system specifications) were implemented on Windows as part of the QuickTime Media Layer. The hardest part of getting your QuickTime code to run on Windows may simply be creating a basic application shell or framework to hold that code. Accordingly, we'll spend a good bit of time in this article discussing just that issue.

Movie Controllers

Before we start looking at our source code, however, let's take a minute to make clear what it is that we want to achieve. For the moment, we'll be content to build an application that can open and display QuickTime movies in windows on the screen. The Windows version of our basic application will have a "frame window" that contains a menu bar and within which we can open and display one or more movie windows. Figure 1 shows the appearance of the application's frame window before the user has opened any QuickTime movies.


Figure 1.The frame window of the Windows application

A movie window will contain all the standard window parts (in particular, a title bar and close box), the movie itself, and a special set of controls called the movie controller bar. Figure 2 shows a typical Macintosh movie window, and Figure 3 shows a Windows version of the same movie window. Both of these windows show the standard movie controller bar along the bottom edge of the window.


Figure 2.A movie window on the Macintosh.


Figure 3.A movie window on Windows.

The movie controller bar allows the user to control the playback of the movie and to navigate within it. For instance, the user can use the fast-forward button to play the movie forward at an accelerated rate. Or, the user can drag the position thumb to set the current location in the movie.

Some kinds of QuickTime movies use a different movie controller bar. For instance, QuickTime VR movies are not typically played frame-by-frame in a linear fashion. For these movies, you'll see the movie controller bar shown in Figure 4, which contains controls that allow the user to zoom in and out and to perform other operations on the movie.


Figure 4.The movie controller bar for a QuickTime VR movie.

The movie controller bar is created and managed by a software component called a movie controller component (or, more briefly, a movie controller). Now here's the really fun part: once you've opened a QuickTime movie (using a few simple QuickTime functions), you can call a couple more functions to create and attach a movie controller to your movie. Thereafter, the movie controller (and not your application) draws the movie controller bar and manages all events associated with the movie. Your application doesn't need to know how to jump to a new location in the movie or how to start and stop the movie playback. It simply needs to pass any events it receives to the movie controller component before acting on them itself. The movie controller intercepts any events that apply to it and reacts to them appropriately.

So, the first lesson we need to take to heart is this: we can get all the basic movie playback capabilities simply by creating a movie controller, attaching it to our movie, and then giving it the first shot at handling any events we receive. And as if that weren't enough, the movie controller also provides an extremely easy way for us to perform basic editing operations on movies that support them. (Not all movies support cutting, copying, or pasting of movie segments; for instance, QuickTime VR movies do not.)

The Application Framework

Now it's time for a bit of a detour. As mentioned earlier, QuickTime provides an extensive set of services for handling digital media like sound, video, sprite animation, and the like. But of course we'll need to use other services to handle the basic graphical user interface for our QuickTime-savvy application (windows, menus, dialog boxes, and so forth). Since you're an experienced Macintosh programmer, you're already familiar with the ideas underlying event-driven programming on the Macintosh. Remember, though, that we want to support QuickTime programming on both Macintosh and Windows systems. So we'll need to address separately the issues specific to each operating system, while trying to factor out as much code as possible to share between the two systems.

Our general approach will go like this: we'll create two files, MacFramework.c and WinFramework.c, that handle the basic application services that are specific to the Macintosh and Windows operating systems, respectively. These services include starting up and shutting down the application, handling events and messages, creating windows, opening files dropped onto the application icon, and so forth. We won't delve very much into these framework files in this article, since there isn't very much in them of interest for QuickTime programmers. Suffice it to say that the Macintosh framework would look very familiar to anyone who cut their Mac programming eyeteeth on MacTech's "Getting Started" series (or on some similarly good source); it uses standard event-driven programming techniques to handle the user's actions. And the Windows framework is a very straightforward implementation of the multiple document interface (MDI) specification defined by Microsoft for creating and managing one or more document windows within a general frame window.

What's distinctive about MacFramework.c and WinFramework.c is that they have been carefully designed to call functions defined in a third file, ComFramework.c, for most QuickTime services or other services that are not system-specific. ComFramework.c also defines a number of functions that are substantially the same on both platforms but which may require several short platform-dependent blocks (introduced by the compiler flags TARGET_OS_MAC and TARGET_OS_WIN32).

Keep in mind that (in this article, at least) we want to support only the most basic playback and editing of QuickTime movies, which is exactly what is provided by the basic framework. In future articles, however, we'll want to add other capabilities to our applications. For instance, we'll want to handle some new menus, in addition to the standard File and Edit menus; and we'll want to perform some application-specific tasks at idle time (perhaps change the pan angle of a QuickTime VR movie). To make it easy to add such capabilities, we create yet another file, called ComApplication.c, which defines a number of functions that are called at particular times by the basic framework. For instance, after the framework does any necessary menu adjusting for the File and Edit menus (enabling certain menu items and disabling others), it calls the function QTApp_AdjustMenus, defined in ComApplication.c, to allow us to adjust any application-specific menus. Since we don't have any application-specific tasks to perform, for the moment at least, we can ignore ComApplication.c and instead turn our attention to the file ComFramework.c.

Handling Movie Windows

To get a taste for how our basic framework works, let's begin by considering how we want to manage our application's movie windows. On the Macintosh, a movie window is of type WindowPtr; on Windows, a movie window is of type HWND. To simplify the code that handles movie windows, we define a custom type that refers to either a Macintosh movie window or a Windows movie window, like this:

#if TARGET_OS_MAC
typedef WindowPtr				WindowReference;
#endif
#if TARGET_OS_WIN32
typedef HWND						WindowReference;
#endif

We need to maintain some information for each movie window displayed by our application. We'll use the standard technique of defining a structure to hold this information and allocating an instance of that structure for each open movie window. Let's call this instance a "window object record".

typedef struct {
	WindowReference				fWindow;
	Movie								fMovie;
	MovieController 			fController;
	FSSpec								fFileFSSpec;
	short								fFileResID;
	short								fFileRefNum;
	Boolean							fCanResizeWindow;
	Boolean							fIsDirty;
	Boolean							fIsQTVRMovie;
	QTVRInstance					fInstance;
	OSType								fObjectType;
	Handle								fAppData;
} WindowObjectRecord,		*WindowObjectPtr, **WindowObject;

Notice that the first field of this structure, fWindow, is of type WindowReference, which (as you've just seen) is a WindowPtr on the Mac and an HWND on Windows. The fMovie and fController fields identify the movie and movie controller. The next three fields maintain information about the location of the movie file on disk and in memory. The three fields after that indicate whether the movie window can be resized (which is almost always true), whether the movie data has changed since it was opened or last saved, and whether the movie is a QuickTime VR movie. If the movie is a QuickTime VR movie, the fInstance field holds the QTVR instance associated with the movie. The fObjectType field holds an arbitrary identifier that is unique to our application; we use this field just to make sure that we've got a valid window object. Finally, the fAppData field holds a handle to any application-specific data. For now, we won't need to use this field.

When the user selects a movie file to open, we need to allocate a window object record and attach it to the window in which the movie is opened. The standard Macintosh way to do this is to use the SetWRefCon function to set the window's reference constant, an application-specific 32-bit value, to the handle to the window object record. Windows provides a similar capability to attach an application-specific 32-bit value to a window, with the SetWindowLong function. Listing 1 shows the code we use for creating a window object.

Listing 1: Creating a window object

QTFrame_CreateWindowObject

void QTFrame_CreateWindowObject (WindowReference theWindow)
{
	WindowObject			myWindowObject = NULL;

	if (theWindow == NULL)
		return;
		
	// allocate space for a window object record and fill in some of its fields
	myWindowObject = (WindowObject)NewHandleClear(sizeof(WindowObjectRecord));
	if (myWindowObject != NULL) {
		(**myWindowObject).fWindow = theWindow;
		(**myWindowObject).fController = NULL;
		(**myWindowObject).fObjectType = kMovieControllerObject;
		(**myWindowObject).fInstance = NULL;
		(**myWindowObject).fCanResizeWindow = true;
		(**myWindowObject).fIsDirty = false;
		(**myWindowObject).fAppData = NULL;
	}
	
	// associate myWindowObject (which may be NULL) with the window
#if TARGET_OS_MAC
	SetWRefCon(theWindow, (long)myWindowObject);
#endif
#if TARGET_OS_WIN32
	SetWindowLong(theWindow, GWL_USERDATA, (LPARAM)myWindowObject);
	
	// associate a GrafPort with this window 
	CreatePortAssociation(theWindow, NULL, 0L);
#endif
	
	// set the current port to the new window
	MacSetPort(QTFrame_GetPortFromWindowReference(theWindow));
}

Internally, QuickTime does some of its drawing using QuickDraw, the collection of system software routines that perform graphic operations on the user's screen (and elsewhere). And, as you know, QuickDraw does all of its drawing within the current graphics port. On the Macintosh, there is a very close connection between a WindowPtr and a graphics port, but there is no such connection between Windows HWNDs and graphics ports. So, when our application is running on Windows, we need to call the CreatePortAssociation function to create a connection between the HWND and a graphics port (of type GrafPtr).

Once we've called CreatePortAssociation to associate a graphics port with an HWND, we can subsequently call the GetNativeWindowPort function to get a pointer to the graphics port that was associated with that window. Listing 2 defines a function that we can call from either Macintosh or Windows code to get a window's graphics port. (Now you can understand the last line in Listing 1, which sets the current graphics port to the port associated with the specified window.)

Listing 2: Getting the graphics port associated with a window

QTFrame_GetPortFromWindowReference

GrafPtr QTFrame_GetPortFromWindowReference (WindowReference theWindow)
{
#if TARGET_OS_MAC
	return((GrafPtr)GetWindowPort(theWindow));
#endif
#if TARGET_OS_WIN32
	return(GetNativeWindowPort(theWindow));
#endif
}

Let's look briefly at a few other small utilities that we'll use extensively in our framework code. Keep in mind that our general goal here is to provide utilities that insulate us from the specific details of any particular operating system. One thing we'll need to do fairly often is retrieve the window-specific data that we've stored in the window object record associated with a given movie window. We can use the QTFrame_GetWindowObjectFromWindow function, defined in Listing 3 to do this. Aside from a few sanity checks (namely the calls to QTFrame_IsAppWindow and QTFrame_IsWindowObjectOurs), this function is essentially the reverse of attaching the window object record to a window.

Listing 3: Getting the window-specific data associated with a window

QTFrame_GetWindowObjectFromWindow

WindowObject QTFrame_GetWindowObjectFromWindow (WindowReference theWindow)
{
	WindowObject		myWindowObject = NULL;

	if (!QTFrame_IsAppWindow(theWindow))
		return(NULL);
			
#if TARGET_OS_MAC
	myWindowObject = (WindowObject)GetWRefCon(theWindow);
#endif
#if TARGET_OS_WIN32
	myWindowObject = (WindowObject)GetWindowLong(theWindow, GWL_USERDATA);
#endif

	// make sure this is a window object
	if (!QTFrame_IsWindowObjectOurs(myWindowObject))
		return(NULL);
		
	return(myWindowObject);
}

Finally, we'll often need to iterate through all open movie windows. On Windows, this is fairly simple, since the operating system provides an easy way for us to ask just for the first (or next) child of the MDI frame window, which we know to be a movie window. On the Macintosh, it's a bit harder, since we need to skip over any dialog windows or other types of windows that might be in our window list. We can use the functions QTFrame_GetFrontAppWindow and QTFrame_GetNextAppWindow, defined in Listings 4 and 5, to step through all open windows that belong to our application.

Listing 4: Getting the first application window

QTFrame_GetFrontAppWindow

WindowReference QTFrame_GetFrontAppWindow (void)
{
#if TARGET_OS_MAC
	return(FrontWindow());
#endif
#if TARGET_OS_WIN32
	return(GetWindow(ghWnd, GW_HWNDFIRST));
#endif
}

One thing to notice in Listing 5 is that we did not find the next window by reading the nextWindow field of the window record, as used to be standard practice. Instead, we've used the accessor function GetNextWindow defined in the header file MacWindows.h. Here we're treating the window record as an opaque data structure and thereby facilitating our eventual move to Carbon-compatible APIs.

Listing 5: Getting the next application window

QTFrame_GetNextAppWindow

WindowReference QTFrame_GetNextAppWindow (WindowReference theWindow)
{
#if TARGET_OS_MAC
	return(theWindow == NULL ? NULL : GetNextWindow(theWindow));
#endif
#if TARGET_OS_WIN32
	return(GetWindow(theWindow, GW_HWNDNEXT));
#endif
}

To find the front movie window, on the Macintosh, we'll just walk through the window list until we find the first window with a non-NULL window object attached to it, as shown in Listing 6.

Listing 6: Getting the first movie window

QTFrame_GetFrontMovieWindow

WindowReference QTFrame_GetFrontMovieWindow (void)
{
	WindowReference			myWindow;

#if TARGET_OS_MAC
	myWindow = QTFrame_GetFrontAppWindow();
	while ((myWindow != NULL) && (QTFrame_GetWindowObjectFromWindow(myWindow) == NULL))
		myWindow = QTFrame_GetNextAppWindow(myWindow);
#endif

#if TARGET_OS_WIN32
	myWindow = (HWND)SendMessage(ghWndMDIClient, WM_MDIGETACTIVE, 0, 0L);
#endif

	return(myWindow);
}

And to get the movie window that follows a specific movie window, we'll continue walking through the window list until we find the next window with a non-NULL window object, as shown in Listing 7.

Listing 7: Getting the next movie window

QTFrame_GetNextMovieWindow

WindowReference QTFrame_GetNextMovieWindow (WindowReference theWindow)
{
	WindowReference			myWindow;

#if TARGET_OS_MAC
	myWindow = QTFrame_GetNextAppWindow(theWindow);
	while ((myWindow != NULL) && (QTFrame_GetWindowObjectFromWindow(myWindow) == NULL))
		myWindow = QTFrame_GetNextAppWindow(myWindow);
#endif

#if TARGET_OS_WIN32
	myWindow = GetWindow(theWindow, GW_HWNDNEXT);
#endif

	return(myWindow);
}

Handling Menus

Now we want to do the same thing for menus that we've done for windows, namely develop a unified way to refer to menus and menu items, so that we can (for instance) enable and disable menu items, or process the user's selection of menu items, in a platform-neutral manner. Happily, this is a simpler task than the one we just solved.

On the Macintosh, a particular menu item is specified using two pieces of information, the menu ID and the index of the item in the specified menu. On Windows, a menu item is specified by a single 16-bit "menu item identifier", which is an arbitrary value that we associate with the menu item. Because the value on Windows is arbitrary, we'll construct it by setting the high-order 8 bits to the Macintosh menu ID and the low-order 8-bits to the index of the menu item in the menu.

Suppose we use the following values for our resource and menu IDs in our Macintosh resource file:

#define kMenuBarResID					128
#define kAppleMenuResID				128
#define kFileMenuResID					129
#define kEditMenuResID					130

Then we can define our menu item identifiers like this:

#define IDS_FILEMENU					33024	// (kFileMenuResID<<8)+0
#define IDM_FILENEW						33025	// (kFileMenuResID<<8)+1
#define IDM_FILEOPEN					33026
#define IDM_FILECLOSE					33027
#define IDM_FILESAVE					33028
#define IDM_FILESAVEAS					33029
#define IDM_EXIT							33031

#define IDS_EDITMENU					33280	// (kEditMenuResID<<8)+0
#define IDM_EDITUNDO					33281	// (kEditMenuResID<<8)+1
#define IDM_EDITCUT						33283
#define IDM_EDITCOPY					33284
#define IDM_EDITPASTE					33285
#define IDM_EDITCLEAR					33286
#define IDM_EDITSELECTALL			33288

You might be wondering why we didn't just define, for instance, IDS_FILEMENU as (kFileMenuResID<<8)+0. In fact this works fine when developing either Mac or Windows applications using CodeWarrior, but it doesn't seem to work when using Microsoft Developer Studio on Windows machines. Go figure (literally!). Also, for those of you with calculators that don't have a "<<" key, bit-shifting left by 8 is the same as multiplying by 256.

In effect, we're simply adopting the Windows method of specifying menu items, but doing so in a manner that allows us to retrieve the Macintosh menu ID and menu item index from the arbitrary menu item identifier, using these macros:

#define MENU_IDENTIFIER(menuID,menuItem)	((menuID<<8)+(menuItem))
#define MENU_ID(menuIdentifier)				((menuIdentifier&0xff00)>>8)
#define MENU_ITEM(menuIdentifier)			((menuIdentifier&0x00ff))

Now we need to devise a way of referring to menus themselves in a cross-platform way. On the Macintosh, we access menus using variables and parameters of type MenuHandle, while on Windows, we use variables and parameters of type HMENU. Once again, we'll define a custom type that refers to either a Macintosh menu or a Windows menu, like this:

#if TARGET_OS_MAC
typedef MenuHandle				MenuReference;
#endif
#if TARGET_OS_WIN32
typedef HMENU						MenuReference;
#endif

Let's see how this all fits together in practice. Suppose we want to enable or disable a particular menu item. For instance, the user may have edited a movie window, in which case we want to make sure that the Undo menu item in the Edit menu is enabled. We can use the function QTFrame_SetMenuItemState defined in Listing 8.

Listing 8: Enabling or disabling a menu item

QTFrame_SetMenuItemState

void QTFrame_SetMenuItemState (MenuReference theMenu, UInt16 theMenuItem, short theState)
{
#if TARGET_OS_MAC
	if (theState == kEnableMenuItem)
		EnableMenuItem(theMenu, MENU_ITEM(theMenuItem));
	else
		DisableMenuItem(theMenu, MENU_ITEM(theMenuItem));
#endif
#if TARGET_OS_WIN32
	EnableMenuItem(theMenu, (UINT)theMenuItem, (UINT)theState);
#endif
}

On Windows, QTFrame_SetMenuItemState uses the Windows function EnableMenuItem to set the specified menu item to the desired state. On the Macintosh, it calls either of the two Macintosh functions EnableMenuItem or DisableMenuItem, depending on the value of theState.

QuickTime Support

Now let's get back to the task at hand, which is showing how to open and display QuickTime movies, and how to handle basic editing operations on those movies.

Opening a Movie File

Let's suppose that you've already got a file system specification record that indicates which movie file the user wants to open. You might have called the Standard File Package or (on Macintosh) the newer Navigation Services to elicit a file from the user, or you might have gotten the file specification from an Open Document Apple Event. At this point, you can call the function OpenMovieFile to open the movie file:

myErr = OpenMovieFile(&myFSSpec, &myRefNum, fsRdWrPerm);

Next you need to load the movie data from the file. You can do this by calling the NewMovieFromFile function, as follows:

myResID = 0;
myErr = NewMovieFromFile(&myMovie, myRefNum, &myResID, NULL, newMovieActive, NULL);

Now you need to create a window in which to display the movie. This operation is platform-specific: on the Macintosh, you can call the NewCWindow function, or on Windows you can call the CreateWindowEx function. Once you've successfully created a window, you just need to pass that window to the QTFrame_CreateWindowObject function defined in Listing 1, which (as you saw) allocates some memory to hold information needed by our frameworks and attaches the window object record to the window.

Attaching a Movie Controller

So, at this point, we've opened the movie file and read the movie data from it. We've also created a window (which is not yet visible), created a window object, and attached the window object to the window. Before we can make the movie window visible, we need to create a movie controller and attach it to the window. We do this by calling the framework function QTFrame_SetupController:

myMC = QTFrame_SetupController(myMovie, myWindow, true);

The QTFrame_SetupController function is a tad lengthy, so we won't show it all here. But it does only four things of any real interest: it creates a new movie controller for the specified movie, it enables movie controller editing, it adds a grow box to the movie controller bar, and it installs a movie controller action filter function. We'll discuss movie editing a little later; for now, let's see how to do the other three tasks.

Before you can create a movie controller, you need to determine the rectangle within which the movie is going to be displayed. Our basic application draws the movie at its natural size, which we can obtain by calling the GetMovieBox function (making sure that the top-left corner of the movie is at 0,0):

GetMovieBox(myMovie, &myRect);
MacOffsetRect(&myRect, -myRect.left, -myRect.top);
SetMovieBox(myMovie, &myRect);

Then we can create a movie controller with this single line of code:

myMC = NewMovieController(myMovie, &myRect, mcTopLeftMovie);

Even though we've opened the move at its natural size, we'd like to allow the user to resize the movie window. By default, the movie controller does not provide this capability, so we need to do a little work to enable movie resizing. The first thing we need to do is find the upper bounds for the movie window size. In other words, we need to find the largest rectangle that can contain a movie window. We could set an arbitrary upper bounds for our movie window size, but instead we'll allow the user to resize a movie to fit as much of the available screen space as possible. On the Macintosh, we can get this area like this:

gMCResizeBounds = (**GetGrayRgn()).rgnBBox;

(This line of code isn't Carbon-compliant, of course. We still have a little bit of work to do before our Macintosh framework is fully Carbonized.) On Windows, we can call the GetDesktopWindow and GetWindowRect functions to get the size of the desktop. Note that GetWindowRect returns a structure of type RECT, which on Windows consists of four long integers. So we need to convert the RECT into a Rect, like this:

RECT				myRect;

GetWindowRect(GetDesktopWindow(), &myRect);
OffsetRect(&myRect, -myRect.left, -myRect.top);
gMCResizeBounds.top = (short)myRect.top;
gMCResizeBounds.left = (short)myRect.left;
gMCResizeBounds.right = (short)myRect.right;
gMCResizeBounds.bottom = (short)myRect.bottom;

Then, we can enable window resizing by calling the MCDoAction function with the mcActionSetGrowBoxBounds parameter:

MCDoAction(myMC, mcActionSetGrowBoxBounds, &gMCResizeBounds);

MCDoAction is a general-purpose tool for getting a movie controller to perform various actions. (If you take a look in the header file Movies.h, you'll see well over 60 defined movie controller actions.) We'll use MCDoAction extensively throughout this series of articles. For the moment, though, it's important to know that before the movie controller performs any action initiated by MCDoAction, it informs your application of the pending action and indeed allows your application to cancel that action. To get these notifications of pending actions, you need to install a movie controller action filter function, like this:

MCSetActionFilterWithRefCon(myMC, NewMCActionFilterWithRefConProc(QTApp_MCActionFilterProc), (long)myWindowObject);

The first parameter is the movie controller to which you want to attach a filter function. The second parameter is a universal procedure pointer for the filter function itself. The third parameter is an application-specific reference constant that is passed to the filter function whenever it is called. As you can see, we're passing the window object to the filter function so that we can gain access to the movie window's data inside that function.

Listing 9 shows a typical movie controller action filter function. This function handles only one action, mcActionControllerSizeChanged, which the movie controller sends to our filter function whenever the user has dragged the resize box in the controller bar. Our job is then to resize the associated movie window.

Listing 9: Intercepting movie controller actions

QTFrame_SetMenuItemState

PASCAL_RTN Boolean QTApp_MCActionFilterProc (MovieController 		theMC, short theAction, void *theParams, long theRefCon)
{
#pragma unused(theMC, theParams)

	Boolean				isHandled = false;
	WindowObject		myWindowObject = NULL;
	
	myWindowObject = (WindowObject)theRefCon;
	if (myWindowObject == NULL)
		return(isHandled);
		
	switch (theAction) {
		// handle window resizing
		case mcActionControllerSizeChanged:
			QTFrame_SizeWindowToMovie(myWindowObject);
			break;

		default:
			break;
			
	} // switch (theAction)
	
	return(isHandled);	
}

A movie controller action filter function should return false as its function result if it wants the movie controller to handle the action specified by the theAction parameter. Conversely, it should return true if it doesn't want the movie controller to handle the action. In the case of the mcActionControllerSizeChanged action, we return false to let the controller do any processing it needs to.

Handling Events

Once we've got a movie file opened in a window and have attached a movie controller to it, we need to let the movie controller handle any events that apply to it. (For instance, if the user hits the space bar, the movie controller should start or stop the movie, depending on its current playing state.) The main thing we need to do is pass the event to the MCIsPlayerEvent function, but how we do this differs between the Mac and Windows platforms.

On Macintosh, we get events in our main event loop by calling WaitNextEvent. When we retrieve an event in this way, we don't know which, if any, movie controller it might apply to. So, we'll just pass the event to all open movie controllers until we find one that accepts it. Listing 10 shows how we do this.

Listing 10: Handling events on the Macintosh

QTFrame_CheckMovieControllers

static Boolean QTFrame_CheckMovieControllers (EventRecord *theEvent)
{	
	WindowPtr					myWindow;
	WindowObject				myWindowObject;
	MovieController			myMC;
	
	myWindow = QTFrame_GetFrontMovieWindow();
	while (myWindow != NULL) {
	 	myWindowObject = QTFrame_GetWindowObjectFromWindow(myWindow);
		if (myWindowObject != NULL) {
			myMC = (**myWindowObject).fController;
			if (myMC != NULL)
				if (MCIsPlayerEvent(myMC, theEvent))
					return(true);
		}
		
		myWindow = QTFrame_GetNextMovieWindow(myWindow);
	}

	return(false);
}

On Windows, however, system and user actions are handled somewhat differently. Windows sends messages describing those actions directly to the window procedure of the target window, so we know in advance which movie controller the action might apply to. The only "gotcha" is that MCIsPlayerEvent wants to get Macintosh-style events, not Windows-style messages. So, we need to call the function WinEventToMacEvent to translate the Windows message into a Macintosh event, as shown in Listing 11.

Listing 11: Handling events on Windows

QTFrame_MovieWndProc

WinEventToMacEvent(&myMsg, &myMacEvent);
// pass the Mac event to the movie controller if the movie window isn't minimized
if (!IsIconic(theWnd))
	MCIsPlayerEvent(myMC, (EventRecord *)&myMacEvent);

Editing a Movie

Some QuickTime movie controllers support the typical cut, copy, paste, and clear editing operations through a handful of high-level functions that are very easy to use. By default, a newly-created movie controller has editing turned off, so we need to explicitly turn it on, like this:

MCEnableEditing(myMC, true);

If you're interested in finding out whether a particular movie controller supports editing, you can inspect the value returned by MCEnableEditing, which is non-zero if the specified movie controller does not support editing. We'll ignore that return value here, since we'll dynamically determine whether a movie controller supports editing whenever we adjust our application's menus.

When the user selects an item in our Edit menu (or performs some equivalent keyboard operation), we simply need to call the corresponding movie controller function. For instance, if the user selects the Cut menu item, we'll call the MCCut function and also set a flag to indicate that the movie's data has changed:

myMovie = MCCut(myMC);
(**myWindowObject).fIsDirty = true;

MCCut removes the current movie selection from the movie associated with the specified movie controller. Moreover, MCCut returns the cut movie selection to us; this is useful if we want to allow the user to paste that segment back into the same movie (or indeed into some other movie). We need to call the PutMovieOnScrap function if we want to allow that segment to be pasted. Listing 12 shows our entire function for handling the Edit menu.

Listing 12: Handling items in the Edit menu

QTFrame_HandleEditMenuItem

void QTFrame_HandleEditMenuItem (WindowReference theWindow, 		UInt16 theMenuItem)
{
	WindowObject			myWindowObject = NULL;
	MovieController		myMC = NULL;
	Movie						myMovie = NULL;
	
	myWindowObject = QTFrame_GetWindowObjectFromWindow(theWindow);
	myMC = QTFrame_GetMCFromWindow(theWindow);
	
	// make sure we have a valid movie controller and a valid window object
	if ((myMC == NULL) || (myWindowObject == NULL))
		return;

	switch (theMenuItem) {
		case IDM_EDITUNDO:
			MCUndo(myMC);
			(**myWindowObject).fIsDirty = true;	break;
		case IDM_EDITCUT:
			myMovie = MCCut(myMC);
			(**myWindowObject).fIsDirty = true;	break;
		case IDM_EDITCOPY:
			myMovie = MCCopy(myMC);				break;
		case IDM_EDITPASTE:
			MCPaste(myMC, NULL);
			(**myWindowObject).fIsDirty = true;	break;
		case IDM_EDITCLEAR:
			MCClear(myMC);
			(**myWindowObject).fIsDirty = true;	break;
		case IDM_EDITSELECTALL:
			QTUtils_SelectAllMovie(myMC);		break;
		default:								break;
	} // switch (theMenuItem)
	
	// place any cut or copied movie segment onto the global scrap
	if (myMovie != NULL) {
		PutMovieOnScrap(myMovie, 0L);
		DisposeMovie(myMovie);
	}
}

The movie controller also provides an easy way for us to determine which Edit menu items need to be enabled or disabled at any time. The function MCGetControllerInfo returns a 32-bit set of flags that we can inspect to see the current status of the movie controller, such as whether the user has edited the movie. So, to enable or disable the Undo menu command, we can use code like this:

MCGetControllerInfo(myMC, &myFlags);
if (myFlags & mcInfoUndoAvailable)
	QTFrame_SetMenuItemState(myMenu, IDM_EDITUNDO,
			kEnableMenuItem);
else
	QTFrame_SetMenuItemState(myMenu, IDM_EDITUNDO, kDisableMenuItem);

See the function QTFrame_AdjustMenus in ComFramework.c for the complete story on adjusting all the Edit (and File) menu items.

Conclusion

Well, we've accomplished what we set out to do, namely show how to open QuickTime movie files in movie windows and handle user interactions with those movies. Along the way, we also took a fairly long look at some of the techniques we can use, here and in the future, to make sure that our code operates identically on both Macintosh and Windows operating systems. By spending the time now getting to understand how to use the platform-independent utilities defined in ComFramework.c, we have (I hope) made it easier for us to spend our time later focussing on the QuickTime-specific tasks we want to accomplish.

Before we close up shop, let's take a minute to discuss the source code that accompanies this month's article. I have included three project files created with Metrowerk's CodeWarrrior IDE version 4.0.2. These projects build PowerPC, 68K, and Windows versions of an application called QTShell, which is our bare-bones QuickTime viewing and editing application. I have also included the file QTShell.mak, which you can use with Microsoft Developer Studio to build a Windows application, if you prefer programming on a Windows machine. (The CodeWarrior project files should also work when using CodeWarrior on Windows, but I have not tested this.)

No matter which platform or development environment you develop on, you'll need to obtain the QuickTime-specific header files, libraries, and (on Windows) resource-building tools. These are available on the QuickTime Software Development Kit (SDK) CD, which I highly recommend if you really want to do some serious QuickTime programming. The same header files, libraries, and tools are found on the CD included in the excellent book Discovering QuickTime by George Towner (available through the DevDepot at http://www.devdepot.com). Finally, you can download the necessary files from the QuickTime web site at http://www.apple.com/quicktime/developers.

Credits

The basic Windows framework for the QTShell source code (as contained in the file WinFramework.c) is based on an earlier sample code package by Brian Friedkin, called MDIPlayer. The basic Macintosh framework (as contained in the file MacFramework.c) and the factoring of any application-specific code into the file ComAppliction.c is based on an earlier sample code package by Kent Sandvik, called MovieShell.


Tim Monroe (monroe@apple.com) is a software engineer on Apple's QuickTime team. He is currently developing sample code and utilities for the QuickTime software development kit.

 
AAPL
$102.50
Apple Inc.
+0.25
MSFT
$45.43
Microsoft Corpora
+0.55
GOOG
$571.60
Google Inc.
+2.40

MacTech Search:
Community Search:

Software Updates via MacUpdate

Path Finder 6.5.5 - Powerful, award-winn...
Path Finder is a file browser that combines the familiar Finder interface with the powerful utilities and innovative features. Just a small selection of the Path Finder 6 feature set: Dual pane... Read more
QuarkXPress 10.2.1 - Desktop publishing...
With QuarkXPress, you can communicate in all the ways you need to -- and always look professional -- in print and digital media, all in a single tool. Features include: Easy to Use -- QuarkXPress is... Read more
Skype 6.19.0.450 - Voice-over-internet p...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
VueScan 9.4.41 - Scanner software with a...
VueScan is a scanning program that works with most high-quality flatbed and film scanners to produce scans that have excellent color fidelity and color balance. VueScan is easy to use, and has... Read more
Cloud 3.0.0 - File sharing from your men...
Cloud is simple file sharing for the Mac. Drag a file from your Mac to the CloudApp icon in the menubar and we take care of the rest. A link to the file will automatically be copied to your clipboard... Read more
LibreOffice 4.3.1.2 - Free Open Source o...
LibreOffice is an office suite (word processor, spreadsheet, presentations, drawing tool) compatible with other major office suites. The Document Foundation is coordinating development and... Read more
SlingPlayer Plugin 3.3.20.505 - Browser...
SlingPlayer is the screen interface software that works hand-in-hand with the hardware inside the Slingbox to make your TV viewing experience just like that at home. It features an array of... Read more
Get Lyrical 3.8 - Auto-magically adds ly...
Get Lyrical auto-magically add lyrics to songs in iTunes. You can choose either a selection of tracks, or the current track. Or turn on "Active Tagging" to get lyrics for songs as you play them.... Read more
Viber 4.2.2 - Send messages and make cal...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device,... Read more
Cocktail 7.6 - General maintenance and o...
Cocktail is a general purpose utility for OS X that lets you clean, repair and optimize your Mac. It is a powerful digital toolset that helps hundreds of thousands of Mac users around the world get... Read more

Latest Forum Discussions

See All

Rhonna Designs Magic (Photography)
Rhonna Designs Magic 1.0 Device: iOS Universal Category: Photography Price: $1.99, Version: 1.0 (iTunes) Description: Want to sprinkle *magic* on your photos? With RD Magic, you can add colors, filters, light leaks, bokeh, edges,... | Read more »
This Week at 148Apps: August 25-29, 2014
Shiny Happy App Reviews   | Read more »
Qube Kingdom – Tips, Tricks, Strategies,...
Qube Kingdom is a tower defense game from DeNA. You rally your troops – magicians, archers, knights, barbarians, and others – and fight against an evil menace looking to dominate your kingdom of tiny squares. Planning a war isn’t easy, so here are a... | Read more »
Qube Kingdom Review
Qube Kingdom Review By Nadia Oxford on August 29th, 2014 Our Rating: :: KIND OF A SQUARE KINGDOMUniversal App - Designed for iPhone and iPad Qube Kingdom has cute visuals, but it’s a pretty basic tower defense game at heart.   | Read more »
Fire in the Hole Review
Fire in the Hole Review By Rob Thomas on August 29th, 2014 Our Rating: :: WALK THE PLANKUniversal App - Designed for iPhone and iPad Seafoam’s Fire in the Hole looks like a bright, 8-bit throwback, but there’s not enough booty to... | Read more »
Alien Creeps TD is Now Available Worldwi...
Alien Creeps TD is Now Available Worldwide Posted by Ellis Spice on August 29th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Dodo Master Review
Dodo Master Review By Jordan Minor on August 29th, 2014 Our Rating: :: NEST EGGiPad Only App - Designed for the iPad Dodo Master is tough but fair, and that’s what makes it a joy to play.   | Read more »
Motorsport Manager Review
Motorsport Manager Review By Lee Hamlet on August 29th, 2014 Our Rating: :: MARVELOUS MANAGEMENTUniversal App - Designed for iPhone and iPad Despite its depth and sense of tactical freedom, Motorsport Manager is one of the most... | Read more »
Motorsport Manager – Beginner Tips, Tric...
The world of Motorsport management can be an unforgiving and merciless one, so to help with some of the stress that comes with running a successful race team, here are a few hints and tips to leave your opponents in the dust. | Read more »
CalPal Update Brings the App to 2.0, Add...
CalPal Update Brings the App to 2.0, Adds Lots of New Stuff Posted by Ellis Spice on August 29th, 2014 [ permalink ] | Read more »

Price Scanner via MacPrices.net

Apple now offering refurbished 21-inch 1.4GHz...
The Apple Store is now offering Apple Certified Refurbished 21″ 1.4GHz iMacs for $929 including free shipping plus Apple’s standard one-year warranty. Their price is $170 off the cost of new models,... Read more
Save $50 on the 2.5GHz Mac mini, on sale for...
B&H Photo has the 2.5GHz Mac mini on sale for $549.99 including free shipping. That’s $50 off MSRP, and B&H will also include a free copy of Parallels Desktop software. NY sales tax only. Read more
Save up to $300 on an iMac with Apple refurbi...
The Apple Store has Apple Certified Refurbished iMacs available for up to $300 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. These are the best prices on... Read more
The Rise of Phablets
Carlisle & Gallagher Consulting Group, a businesses and technology consulting firm focused solely on the financial services industry, has released an infographic depicting the convergence of... Read more
Bad Driver Database App Allows Good Drivers t...
Bad Driver Database 1.4 by Facile Group is a new iOS and Android app that lets users instantly input and see how many times a careless, reckless or just plain stupid driver has been added to the... Read more
Eddy – Cloud Music Player for iPhone/iPad Fre...
Ukraine based CapableBits announces the release of Eddy, its tiny, but smart and powerful cloud music player for iPhone and iPad that allows users to stream or download music directly from cloud... Read more
A&D Medical Launches Its WellnessConnecte...
For consumers and the healthcare providers and loved ones who care for them, A&D Medical, a leader in connected health and biometric measurement devices and services, has launched its... Read more
Anand Lal Shimpi Retires From AnandTech
Anand Lal Shimpi, whose AnandTech Website is famous for its meticulously detailed and thoroughgoing reviews and analysis, is packing it in. Lal Shimpi, who founded the tech site at age 14 in 1997,... Read more
2.5GHz Mac mini, Apple refurbished, in stock...
The Apple Store has Apple Certified Refurbished 2.5GHz Mac minis available for $509, $90 off MSRP. Apple’s one-year warranty is included, and shipping is free. Read more
13-inch 2.5GHz MacBook Pro on sale for $999,...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $999.99 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more

Jobs Board

*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.