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
$111.78
Apple Inc.
-0.87
MSFT
$47.66
Microsoft Corpora
+0.14
GOOG
$516.35
Google Inc.
+5.25

MacTech Search:
Community Search:

Software Updates via MacUpdate

calibre 2.13 - Complete e-library manage...
Calibre is a complete e-book library manager. Organize your collection, convert your books to multiple formats, and sync with all of your devices. Let Calibre be your multi-tasking digital librarian... Read more
Mellel 3.3.7 - Powerful word processor w...
Mellel is the leading word processor for OS X and has been widely considered the industry standard since its inception. Mellel focuses on writers and scholars for technical writing and multilingual... Read more
ScreenFlow 5.0.1 - Create screen recordi...
Save 10% with the exclusive MacUpdate coupon code: AFMacUpdate10 Buy now! ScreenFlow is powerful, easy-to-use screencasting software for the Mac. With ScreenFlow you can record the contents of your... Read more
Simon 4.0 - Monitor changes and crashes...
Simon monitors websites and alerts you of crashes and changes. Select pages to monitor, choose your alert options, and customize your settings. Simon does the rest. Keep a watchful eye on your... Read more
BBEdit 11.0.2 - Powerful text and HTML e...
BBEdit is the leading professional HTML and text editor for the Mac. Specifically crafted in response to the needs of Web authors and software developers, this award-winning product provides a... Read more
ExpanDrive 4.2.1 - Access cloud storage...
ExpanDrive builds cloud storage in every application, acts just like a USB drive plugged into your Mac. With ExpanDrive, you can securely access any remote file server directly from the Finder or... Read more
Adobe After Effects CC 2014 13.2 - Creat...
After Effects CC 2014 is available as part of Adobe Creative Cloud for as little as $19.99/month (or $9.99/month if you're a previous After Effects customer). After Effects CS6 is still available... Read more
Evernote 6.0.5 - Create searchable notes...
Evernote allows you to easily capture information in any environment using whatever device or platform you find most convenient, and makes this information accessible and searchable at anytime, from... Read more
Command-C 1.1.7 - Clipboard sharing tool...
Command-C is a revolutionary app which makes easy to share your clipboard between iOS and OS X using your local WiFi network, even if the app is not currently opened. Copy anything (text, pictures,... Read more
Tidy Up 4.0.2 - Find duplicate files and...
Tidy Up is a complete duplicate finder and disk-tidiness utility. With Tidy Up you can search for duplicate files and packages by the owner application, content, type, creator, extension, time... Read more

Latest Forum Discussions

See All

Make your own Tribez Figures (and More)...
Make your own Tribez Figures (and More) with Toyze Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
So Many Holiday iOS Sales Oh My Goodness...
The holiday season is in full-swing, which means a whole lot of iOS apps and games are going on sale. A bunch already have, in fact. Naturally this means we’re putting together a hand-picked list of the best discounts and sales we can find in order... | Read more »
It’s Bird vs. Bird in the New PvP Mode f...
It’s Bird vs. Bird in the New PvP Mode for Angry Birds Epic Posted by Jessica Fisher on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Telltale Games and Mojang Announce Minec...
Telltale Games and Mojang Announce Minecraft: Story Mode – A Telltale Games Series Posted by Jessica Fisher on December 19th, 2014 [ permalink ] | Read more »
WarChest and Splash Damage Annouce Their...
WarChest and Splash Damage Annouce Their New Game: Tempo Posted by Jessica Fisher on December 19th, 2014 [ permalink ] WarChest Ltd and Splash Damage Ltd are teaming up again to work | Read more »
BulkyPix Celebrates its 6th Anniversary...
BulkyPix Celebrates its 6th Anniversary with a Bunch of Free Games Posted by Jessica Fisher on December 19th, 2014 [ permalink ] BulkyPix has | Read more »
Indulge in Japanese cuisine in Cooking F...
Indulge in Japanese cuisine in Cooking Fever’s new sushi-themed update Posted by Simon Reed on December 19th, 2014 [ permalink ] Lithuanian developer Nordcurrent has yet again updated its restaurant simulat | Read more »
Badland Daydream Level Pack Arrives to C...
Badland Daydream Level Pack Arrives to Celebrate 20 Million Downloads Posted by Ellis Spice on December 19th, 2014 [ permalink ] | Read more »
Far Cry 4, Assassin’s Creed Unity, Desti...
Far Cry 4, Assassin’s Creed Unity, Destiny, and Beyond – AppSpy Takes a Look at AAA Companion Apps Posted by Rob Rich on December 19th, 2014 [ permalink ] These day | Read more »
A Bunch of Halfbrick Games Are Going Fre...
A Bunch of Halfbrick Games Are Going Free for the Holidays Posted by Ellis Spice on December 19th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

The Apple Store offering free next-day shippi...
The Apple Store is now offering free next-day shipping on all in stock items if ordered before 12/23/14 at 10:00am PT. Local store pickup is also available within an hour of ordering for any in stock... Read more
It’s 1992 Again At Sony Pictures, Except For...
Techcrunch’s John Biggs interviewed a Sony Pictures Entertainment (SPE) employee, who quite understandably wished to remain anonymous, regarding post-hack conditions in SPE’s L.A office, explaining “... Read more
Holiday sales this weekend: MacBook Pros for...
 B&H Photo has new MacBook Pros on sale for up to $300 off MSRP as part of their Holiday pricing. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina MacBook Pro: $1699... Read more
Holiday sales this weekend: MacBook Airs for...
B&H Photo has 2014 MacBook Airs on sale for up to $120 off MSRP, for a limited time, for the Thanksgiving/Christmas Holiday shopping season. Shipping is free, and B&H charges NY sales tax... Read more
Holiday sales this weekend: iMacs for up to $...
B&H Photo has 21″ and 27″ iMacs on sale for up to $200 off MSRP including free shipping plus NY sales tax only. B&H will also include a free copy of Parallels Desktop software: - 21″ 1.4GHz... Read more
Holiday sales this weekend: Mac minis availab...
B&H Photo has new 2014 Mac minis on sale for up to $80 off MSRP. Shipping is free, and B&H charges NY sales tax only: - 1.4GHz Mac mini: $459 $40 off MSRP - 2.6GHz Mac mini: $629 $70 off MSRP... Read more
Holiday sales this weekend: Mac Pros for up t...
B&H Photo has Mac Pros on sale for up to $500 off MSRP. Shipping is free, and B&H charges sales tax in NY only: - 3.7GHz 4-core Mac Pro: $2599, $400 off MSRP - 3.5GHz 6-core Mac Pro: $3499, $... Read more
Save up to $400 on MacBooks with Apple Certif...
The Apple Store has Apple Certified Refurbished 2014 MacBook Pros and MacBook Airs available for up to $400 off the cost of new models. An Apple one-year warranty is included with each model, and... Read more
Save up to $300 on Macs, $30 on iPads with Ap...
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
iOS and Android OS Targeted by Man-in-the-Mid...
Cloud services security provider Akamai Technologies, Inc. has released, through the company’s Prolexic Security Engineering & Research Team (PLXsert), a new cybersecurity threat advisory. The... Read more

Jobs Board

*Apple* Store Leader Program (US) - Apple, I...
…Summary Learn and grow as you explore the art of leadership at the Apple Store. You'll master our retail business inside and out through training, hands-on experience, 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
*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...
Job Description: Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.