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
$117.60
Apple Inc.
-1.03
MSFT
$47.47
Microsoft Corpora
-0.12
GOOG
$541.08
Google Inc.
+1.81

MacTech Search:
Community Search:

Software Updates via MacUpdate

MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
Carbon Copy Cloner 4.0.3 - Easy-to-use b...
Carbon Copy Cloner backups are better than ordinary backups. Suppose the unthinkable happens while you're under deadline to finish a project: your Mac is unresponsive and all you hear is an ominous,... Read more
ForeverSave 2.1.3 - Universal auto-save...
ForeverSave auto-saves all documents you're working on while simultaneously doing backup versioning in the background. Lost data can be quickly restored at any time. Losing data, caused by... Read more
Voila 3.8.1 - Capture, annotate, organiz...
Voila is a screen-capture, recording, and annotation tool that is a full-featured replacement for Mac's screen-capture and screen-recording capabilities. It has a large and robust set of editing,... Read more
SyncTwoFolders 2.0.6 - Syncs two user-sp...
SyncTwoFolders simply synchronizes two folders. It supports synchronization across mounted network drives and it is a possibility to run a simulation showing in a log what will be done. Please visit... Read more
Duplicate Annihilator 5.1.1 - Find and d...
Duplicate Annihilator takes on the time-consuming task of comparing the images in your iPhoto library using effective algorithms to make sure that no duplicate escapes. Duplicate Annihilator detects... Read more
HandBrake 0.10.0 - Versatile video encod...
HandBrake is a tool for converting video from nearly any format to a selection of modern, widely supported codecs. Supported Sources: VIDEO_TS folder, DVD image or real DVD (unencrypted -- CSS is... Read more

Latest Forum Discussions

See All

Tilt to Live Bundle Set to Arrive This T...
Tilt to Live Bundle Set to Arrive This Thanksgiving Posted by Ellis Spice on November 25th, 2014 [ permalink ] One Man Left has unveiled an upcoming Tilt to Live bundle, allowing players to get the series for a di | Read more »
BattleLore: Command (Entertainment)
BattleLore: Command 1.0 Device: iOS Universal Category: Entertainment Price: $9.99, Version: 1.0 (iTunes) Description: ***NOTE: Compatible with iPad 2/iPad mini, iPod touch 5 and up and iPhone 4S and up – WILL NOT RUN ON EARLIER... | Read more »
Weather Or Not Review
Weather Or Not Review By Jennifer Allen on November 25th, 2014 Our Rating: :: STYLISH WEATHER REPORTINGiPhone App - Designed for the iPhone, compatible with the iPad Check the weather quickly and conveniently with Weather or Not... | Read more »
The All-New Football Manager Handheld 20...
The All-New Football Manager Handheld 2015 is Available Now Posted by Jessica Fisher on November 25th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Six iOS Games to Get You Ready for Thank...
Image Source: Friends Wiki At this point in the month, you or at least a few people you know are probably getting ready to scramble around (or are already scrambling around) for Thanksgiving Dinner. It’s a hectic day of precise oven utilization, but... | Read more »
Call of Duty: Heroes: Tips, Tricks, and...
Hello Heroes: What’d we think of Call of Duty‘s take on Clash of Clans? Check out our Call of Duty: Heroes review to find out! Just downloaded Call of Duty: Heroes and need some handy tips and tricks on how to get ahead of the rest? As we often do,... | Read more »
Call of Duty: Heroes Review
Call of Duty: Heroes Review By Jennifer Allen on November 25th, 2014 Our Rating: :: CLASH OF FRANCHISESUniversal App - Designed for iPhone and iPad Mix Clash of Clans with Call of Duty, and this is what you get.   | Read more »
Slider Review
Slider Review By Jordan Minor on November 25th, 2014 Our Rating: :: SLIDE TO PLAYUniversal App - Designed for iPhone and iPad Slider has all the excitement of unlocking your phone screen.   | Read more »
oh my giraffe (Games)
oh my giraffe 1.0.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0.0 (iTunes) Description: Eat fruits while being chased by lions. Cut the vines to send fruit plummeting onto the lions. Don't worry, your flexible... | Read more »
One of 2000’s Most Loves Adventure Games...
One of 2000’s Most Loves Adventure Games, The Longest Journey, has Come to iOS Posted by Jessica Fisher on November 25th, 2014 [ permalink ] | Read more »

Price Scanner via MacPrices.net

Early Black Friday MacBook Pro sale: 15-inch...
 Best Buy has posted early Black Friday prices on 15″ Retina MacBook Pros, with models on sale for $300 off MSRP on their online store for a limited time. Choose free local store pickup (if available... Read more
A9 Chips Already?
It’s barely more than a couple of months since Apple got the first A8 systems-on-chip into consumer hands, but rumor and news focus is already turning to the next-generation A9 SoC. Apple Daily... Read more
NewerTech Announces NuGuard KXs Impact X-Orbi...
NewerTech has announced updates to its family of Impact X-Orbing Screen Armor bringing military grade, triple layer protection to Apple’s new iPhone 6 and 6 Plus. Like all models in the NuGuard KXs... Read more
13-inch 1.4GHz MacBook Air on sale for $889,...
 B&H Photo has the 13″ 1.4GHz/128GB MacBook Air on sale for $889 including free shipping plus NY tax only. Their price is $110 off MSRP. B&H will also include free copies of Parallels Desktop... Read more
Save up to $300 on Macs and iPads with your A...
Purchase a new Mac or iPad at The Apple Store for Education and take up to $300 off MSRP. All teachers, students, and staff of any educational institution qualify for the discount. Shipping is free,... Read more
Apple refurbished Mac Pros available for up t...
The Apple Store is offering Apple Certified Refurbished Mac Pros for up to $600 off the cost of new models. An Apple one-year warranty is included with each Mac Pro, and shipping is free. The... Read more
Jumptuit Launches One-Tap Windows 8.1 iTunes...
Jumptuit has launched Windows 8.1 support for One-Tap iTunes Sync. with which Windows 8.1 users can now easily sync their iTunes libraries with Microsoft OneDrive. Jumptuit provides easy access from... Read more
Apple restocks refurbished 13-inch 2014 Retin...
The Apple Store has restocked Apple Certified Refurbished 2014 13″ 2.6GHz Retina MacBook Pros for up to $230 off the cost of new models. An Apple one-year warranty is included with each model, and... Read more
CEA Study Finds More People Recycling Electro...
A new study by the Consumer Electronics Association (CEA) finds that electronics recycling receives the continued and growing support of consumers. According to the CEA,s Recycling and Reuse Study,... Read more
15″ 2.2GHz Retina MacBook Pro on sale for $17...
 B&H Photo has the 2014 15″ 2.2GHz Retina MacBook Pro on sale today for $1749. Shipping is free, and B&H charges NY sales tax only. B&H will also include free copies of Parallels Desktop... 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* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC)- Retail S...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Project Manager, *Apple* Financial Services...
**Job Summary** Apple Financial Services (AFS) offers consumers, businesses and educational institutions ways to finance Apple purchases. We work with national and Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.