Apple Developer Connection
Advanced Search
Member Login Log In | Not a Member? Contact ADC

Using the QTKit Framework

The heart of QuickTime is its comprehensive underlying framework which is powerful, extensible, and flexible. This framework provides developers with the ability to display, import, export, modify, and capture more than 200 different media types. Until recently, however, using this power from Cocoa applications hasn't always been straightforward. Before the release of Mac OS X Tiger, anything more than display and playback of QuickTime files required using a set of Carbon APIs that were distinctly foreign to most Cocoa developers.

That has all changed. With Mac OS X Tiger comes QuickTime 7, which features a redesigned core architecture and provides a modernized Cocoa interface called QTKit. QTKit allows Cocoa developers to leverage a useful subset of QuickTime's capabilities. It not only allows you to play QuickTime movies, but to edit, splice, combine, and transcode them. And, if you need to go beyond its abilities, QTKit makes it easy to get to the underlying QuickTime primitives and directly use the more than 2500 functions in the QuickTime procedural API.

In essence, QTKit brings a deeper level of integration with QuickTime to the premier programming environment on Mac OS X.

This article shows how the QTKit framework is structured and introduces you to the essential concepts you'll need to understand to get up and running. First, let's review why QuickTIme is so important and what it brings to developers.

The Benefits of QuickTime

QuickTime is Apple's state-of-the-art multimedia platform that enables everything from high-definition video and audio playback to the creation of immersive environments. QuickTime is the most powerful and comprehensive digital media framework available. It powers iTunes, iMovie and Final Cut Pro as well as thousands of programs from third-party developers. It has stood the test of time by supporting files created by the original QuickTime release 15 years ago as well as the modern H.264, MPEG4, and 3G files. And, it's available to you to integrate into your own applications.

There are many benefits to using QuickTime, but here is a short list of the most powerful advantages for developers:

  • Support for the latest H.264 video and AAC audio specifications
  • Creation and playback of ISO-compliant MPEG-4 files
  • Creation and playback of 3GPP and 3GPP2 files for mobile multimedia
  • Support for dozens of other industry formats, including still images, streaming protocols, SMPTE effects, and more
  • End-to-end solution for digital media
  • Leverages Core Image and Core Video in Tiger for increased performance

With those features, you have plenty of reasons to want to include QuickTime in your application.

Now, let's take a high-level look at the QTKit API.

QTKit API Overview

The QTKit framework provides straightforward access to QuickTime's core functionality. It contains five classes, two data structures, and a number of useful methods, functions, and protocols. The primary structure of the API is shown in the following figure:

Api

The two fundamental classes in QTKit are QTMovie and QTMovieView. As you would expect from the name of the class, QTMovie serves as the Cocoa object representation of a QuickTime movie. QTMovie instances can reference data contained in files, URLs, or in memory. The QTMovieView, a subclass of NSView, is used to display on-screen a QTMovie instance. It also provides an optional controller bar that allows users to directly control the playback of a movie. If you want to provide your own custom controls instead, the QTMovieView class provides a full complement of methods to control playback, such as play, pause, stop, and more.

The QuickTime File Format. What makes QuickTime unique is the fact that its file format doesn't describe how to encode one kind of media. Instead, the QuickTime file format is a scalable container that can hold a variety of media types, including audio, video, Macromedia Flash, still images, and more.

The flexibility of the QuickTime file format has proven so valuable that it has been made a worldwide digital medial standard. It is currently used as the basis of the MPEG-4, 3GPP, and 3GPP2 specifications and gives the Apple and the standards community the ability to easily add new media technologies as they emerge.

The QTMovie and QTMovieView classes completely replace the older Cocoa NSMovie and NSMovieView classes. These new classes give a deeper level of control over movie display and playback as well as the ability to access and set a wide variety of movie attributes. As well, they provide solid support for modern Cocoa programming practices, such as using Cocoa Bindings and error handling using the NSError class.

The other three classes, QTTrack, QTMedia, and QTDataReference, as well as the QTTime and QTTimeRange data structures, give you access to the various components of a QuickTime movie. A QTMovie contains a set of QTTrack objects, each of which in turn references a QTMedia object. There's a lot of functionality encapsulated in these classes—enough to fill several articles. But let's not get ahead of ourselves. Instead, now that you have a high-level view of how QTKit is structured, let's turn our attention to see how easy it is to start using QTKit in a Cocoa application.

Displaying Movies: Step-by-Step

To display a movie in an application, you'll need to set up a QTMovieView. Like other NSView subclasses, the easiest way to work with a QTMovieView is to use InterfaceBuilder to lay out a user interface and define how it connects with your application's logic. These connections can be made with either traditional Cocoa outlets and actions or they can use the Cocoa Bindings technology.

To give you a feel for how to use a QTMovieView to display QuickTime movies, we're going to present a quick step-by-step guide that will result in a functional movie player. You can follow along if you'd like on your computer in Xcode, or just read along.

Step One: Set up the Project

To get started, launch Xcode and create a new Cocoa Document-based application. The application needs to be able to open QuickTime movies, so open up the Info window for the application's target, and set mov as a document type extension and the role as "Viewer", as shown in the following figure:

Documentprops

Next, you'll need to add the QTKit framework to the project so that Xcode will link the built application with it. To do this, use the Project > Add to Project menu and navigate to /System/Library/Frameworks/QTKit.framework, then click the Add button. Once added, it will show up in your project's window:

Addframework

Step Two: Add a QTMovieView to the User Interface

Now that the housekeeping details are taken care of, let's lay out the user interface. Double click on the MyDocument.nib file in your project to open it in Interface Builder. Once Interface Builder is open, you'll probably need to load up the QTKit palette. You can find this palette in the /Developer/Extras/Palettes folder, as shown in the following figure.

Palette

You can install this palette by double-clicking on it in the Finder. Or, if you can install it using the Palettes tab in Interface Builder's Preferences Window. You can even drag it into the /Developer/Palettes folder. After you install the QuickTime palette into Interface Builder using any of these methods, working with QTMovieView is as easy as working with any other view. You can place, resize, and set the view's attributes all within Interface Builder. Drag out a movie view onto the window:

Dragqtmovieview

Then, resize the QTMovieView as needed:

Resize

You'll probably also want to set up the view autosizing in the Size panel of the inspector. We'll leave that up to you.

Step Three: Set up a Binding

To connect the QTMovieView to the QuickTime movie it will display, we to define a Cocoa binding from the QTMovieView's movie parameter to a model key named movie in the File's Owner object. To create this binding:

  1. In the the QTMovieView Inspector (select the QTMovieView and open with Command-Shift-I, if needed) use the top pull down menu to navigate to the Bindings panel.
  2. In the Parameters section of the Bindings panel, open up the movie parameter by clicking on the disclosure triangle next to it.
  3. Select "File's Owner" in the Bind to: pull down menu.
  4. Type movie into the Model Key Path: field.
  5. Make sure the Bind checkbox is clicked.
Bind

If you're not familiar with Cocoa Bindings, or are still getting the hang of Cocoa, you may not have caught the full implications of what just happened. By defining the above binding, we've instructed Cocoa to create a connection at run time between the object that loads the user interface, the File's Owner object, and the QTMovieView object. This connection will provide the QTMovieView with a reference to the QuickTime movie it is to display. Next, we'll finish up this connection. But first, remember to save the user interface in Interface Builder.

Bindings Make it Easy. You may be reading along wondering what the catch is. After all, there's not a lot of code here. Rest assured, we're not omitting anything for the sake of space. This is really all it takes to hook up a movie to a movie view and create a working sample application. Cocoa bindings makes it just this easy.

What are Cocoa Bindings? They are a fairly new technology that implements much of the needed infrastructure needed to glue the various bits of a Model-View-Controller (MVC) application together. Their abilities go far beyond what you see here and include being able to synchronize multiple values in different parts of your application with little or no coding on your part. And, as you know, writing less "grunt" code leaves you with more time to write the code that really matters in your application. You can read more about Cocoa Bindings in the ADC resources listed at the end of this article.

Step Four: Finish the Binding

We turn our connection back to Xcode. To finish up the binding, we need to add a movie instance variable to the NSDocument subclass MyDocument. This will serve as the movie model key that we bound the QTMovieView's movie parameter to. Open up the MyDocument.h file (found in the classes folder)  and add the instance variable as well as an import of the QTKit framework.

#import <Cocoa/Cocoa.h>
#import <QTKit/QTKit.h>

@interface MyDocument : NSDocument
{
    QTMovie *movie;
}
@end

As simple as it seems, that's all that's needed to finish the binding with the QTMovieView in the user interface. Next, we need to make sure that there's some data to be displayed.

Step Five: Load the QuickTime Data

To load up a QuickTime movie when the document is opened, first open the MyDocument.m file, found in the Implementation Files folder. Now edit the loadDataRepresentation:ofType: method as follows:

- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType
{
    movie = [QTMovie movieWithData:data error:nil];
    return YES;
}

Build and Run

At this point, you're ready to build and run the application. When you do so, you'll be able to load and play movies from your filesytem.

Just locate a .mov file on your hard drive, and open it with the File>Open in your application.

Runingmovie

If you've followed along and built this simple application on your system and opened a sample movie, you'll probably notice one detail that isn't perfect. The QuickTime movie's aspect ratio will be scaled to that of the QTMovieView. With a bit more logic, we could add the functionality to our viewer to make it resize itself to respect the aspect ratio of the movie being displayed. But to make this logic work, we need to look at how to work with movie attributes.

Working With Movie Attributes

Most of the interesting data in a QTMovie object is stored as attributes and accessible using an attribute key. To give you an idea of what kind of data is encapsulated as attributes, the following table offers a few example attribute keys:

AttributeDescription
QTMovieCopyrightAttributeContains the copyright string for a movie.
QTMovieEditableAttributeIndicates whether the movie can be edited or not
QTMovieHasAudioAttributeIndicates whether the movie has an audio track or not
QTMovieIsSteppableAttributeIndicates whether the movie can be stepped from frame to frame.
QTMovieNaturalSizeAttributeGives the size of the movie when displayed at full resolution
QTMovieSelectionAttributeThe selected range of a movie
QTMovieTimeScaleAttributeThe time scale of a movie

Full documentation of all the various attribute keys is located in the QTMovie API documentation. The following code shows an example of accessing the copyright attribute of a movie:

[movie attributeForKey:QTMovieCopyrightAttribute];

When executed, the above method will provide something like the following:

©2006 Apple, Inc.  All Rights Reserved

More applicable to our sample viewer above, you can use the QTMovieNaturalSizeAttribute key to get the size of the movie being played:

NSValue *value = [movie attributeForKey:QTMovieNaturalSizeAttribute];
NSSize size = [value sizeValue];

With this bit of code, you can set up window resizing to respect the aspect ratio of the QuickTime movie being displayed. Instead of wandering off into a window management topic, however, let's continue on with our overview of QTKit.

Making an Edit

So far, we've just shown how to play back a movie in a QTMovie view and how to get data from a QTMovie. Let's go a bit further and look at how we can actually edit a QuickTime movie. As a reasonable example, let's look at a bit of code which will make a 10 second preview from a longer QuickTime movie and save it out as a new movie.

The first thing our code needs to do is create a QTMovie object that references the original movie:

NSError *error;
NSString *path = @"/path/to/movie.mov"
QTMovie *movie = [QTMovie movieWithFile:path error:&error];

Now, in order to operate on the movie, we need to know the time scale the movie uses. This is accessed as an attribute:

NSNumber *scale = [movie attributeForKey:QTMovieTimeScaleAttribute];

How QuickTime Measures Time. Different media formats keep track of time in different ways. For example, theatrical cinema runs at 24 frames per second. Television runs at 30 frames per second. To keep things flexible, QuickTime lets each underlying media format define time however is appropriate along with a time scale so that it can accurately play back the media data. In our example above, we use the QTMovieTimeScaleAttribute to determine how many units 10 seconds occupies in the movie's time scale.

The next thing we need to do is determine the range of the first 10 seconds of the movie. To do this, we're going to use the QTTime and QTTimeRange data types which simply encapsulate a time value or a range of time values in the QTKit API:

QTTime start = QTMakeTime(0, [scale longValue]);
QTTime cut = QTMakeTime(10 * [scale longValue], [scale longValue]);
QTTimeRange range = QTMakeTimeRange(start, cut);

Now, using our 10 second QTTimeRange, we can create a new QTMovie object from the original movie:

QTMovie *newMovie = [[QTMovie alloc] initWithMovie:movie 
    timeRange:range error:&error];

All that's left is to save out our new small preview movie.

NSMutableDictionary *savedMovieAttributes = [NSDictionary 
    dictionaryWithObject:[NSNumber numberWithBool:YES] 
    forKey:QTMovieFlatten];
[movie writeToFile:@"/small/files/movie-preview.mov" 
    withAttributes:savedMovieAttributes];

You don't even need to write a GUI application to use this code to edit movies. With a bit more code that processes command-line arguments and iterates over a number of original movie files, you can make a command-line tool that can automatically make previews for a large collection of QuickTime movies.

Dropping Down to the Procedural API

As stated earlier, QTKit doesn't replicate the entire procedural QuickTime API. To perform some QuickTime operations, you'll need to shift gears and directly use the underlying QuickTime API. However, you won't have to scrap your codebase and start over to do so. QTKit gives you the ability to easily access the QuickTime primitive counterparts of its classes where needed. You can then use any QuickTime function on that primitive.

The following method call provides access to a Movie primitive from a QTMovie instance:

Movie moviePrimitive = [movie quickTimeMovie];

Once you have the primitive, you can pass it to any QuickTime function that takes a Movie data type as a parameter. And, even better, you don't need to change anything about the way you display the movie with a QTMovieView or, for that matter, any other part of your code that you've written using QTKit. You can use the modern Cocoa-based QTKit for integrating QuickTime with your Cocoa-based application and drop down to the procedural API only where needed.

What Can You Do With QTKit?

With just the overview of QTKit that we've given here, you can see that QTKit lets you access an enormous amount of power from QuickTime and build it into your application in a straightforward fashion. What can you do with this knowledge now? You could create a media browser that would let you keep tabs on all the various kinda of meta-data about a large collection of QuickTime movies. You could build a command-line tool that would grab several frames from a set of movies and make a web page that allowed users to browse a collection remotely and choose the movie they wanted to download. Or, you could even build the next video editing tool customized for your own internal workflow.

The sky is the limit. QTKit brings an unprecedented ease of development to more than 15 years of multimedia experience in the QuickTime platform. It brings the premier multimedia platform to the premier application creation platform.

For More Information on QTKit

Using this article as a springboard, you should be able to dive into code and, with the help of the documentation, create your own QuickTime based Cocoa application. Along the way, you'll want to take advantage of the following resources:

For More Information on Cocoa Bindings

Created: 2006-06-19