
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:
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:
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:
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.
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:
Then, resize the QTMovieView as needed:
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:
- 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.
- In the Parameters section of the Bindings panel, open up the movie parameter by clicking on the disclosure triangle next to it.
- Select "File's Owner" in the Bind to: pull down menu.
- Type movie into the Model Key Path: field.
- Make sure the Bind checkbox is clicked.
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.
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:
| Attribute | Description |
| QTMovieCopyrightAttribute | Contains the copyright string for a movie. |
| QTMovieEditableAttribute | Indicates whether the movie can be edited or not |
| QTMovieHasAudioAttribute | Indicates whether the movie has an audio track or not |
| QTMovieIsSteppableAttribute | Indicates whether the movie can be stepped from frame to frame. |
| QTMovieNaturalSizeAttribute | Gives the size of the movie when displayed at full resolution |
| QTMovieSelectionAttribute | The selected range of a movie |
| QTMovieTimeScaleAttribute | The 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];
[newMovie 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
Updated: 2008-09-18
|