This is the first in a series of articles about integrating QuickTime technology into your Cocoa applications. My intent with this series is to provide the introductory steps for those of you who are new to QuickTime, to cover many of the common tasks and issues that may crop up during your development cycle, and to share my experience so that, hopefully, you can avoid some of the mistakes I've made along the way.
I'll start out by providing an overview of QuickTime, then some insight into the interfaces between Cocoa and QuickTime, and finish up with an example of a "simple" QuickTime Movie Player that you can include in your own applications.
In the early 1990s, Apple introduced QuickTime, a collection of application programmer interfaces (APIs) and components that support the creation, manipulation, and playback of multimedia and interactive content. Using the QuickTime API, programmers can work with media types such as video, images, and audio, as well as vector-based graphics and animation, 3D modeling data, panoramic objects, MIDI-formatted music, and user-defined media types. QuickTime components provide access to variety of services, such as image compression/decompression, video digitizing, and pre-defined effects that can be performed on one or more media types.
|
Related Reading
|
The movie is used as a metaphor to describe any time-based sequence of data that can be manipulated by QuickTime. The data can be a sequence of images (like a cinematic movie), or audio samples, or stock quotes, or any type of data that varies over time. A typical movie might contain several streams of data, called tracks. For example, a .mov file might contain a movie with two streams, a video track and a sound track.
The QuickTime API is quite extensive and beyond the scope of this article. So I would suggest that before you begin a project of your own that you spend some time with the QuickTime documentation available at the Apple developer web site.
During the 1990s, QuickTime had helped Apple become successfully positioned as a leader in multimedia and interactive technology. So, when Apple bought NeXT in the late 1990s, the challenge facing Apple was how to integrate one of its homegrown key technologies with Cocoa, the new development environment acquired from NeXT. QuickTime did not have an object-oriented interface and was based on QuickDraw, Apple's pre-Mac-OS-X imaging architecture. Cocoa, on the other hand, was defined by the objects in Foundation and AppKit. Cocoa's image architecture was also based on Quartz, the PDF-based graphical system that Apple had chosen as its go-forward technology.
The solution was to introduce two new objects into Cocoa's AppKit. The
NSMovie object was provided to encapsulate the QuickTime
movie metaphor, and NSMovieView provided insulation from
the differences in the underlying image architectures.
Let's take a look at the NSMovie object. This object fits
my notion of a shallow encapsulation. The NSMovie class
contains only a few methods, which provide you with access to the QuickTime
Movie data type, but does not allow you to directly modify
the movie's data. In other words, using NSMovie, you can
get a pointer to the movie for display purposes, but you can't use NSMovie
itself to change a movie's playback attributes or contents.
The NSMovie class contains methods that allow you to do
three basic things:
NSMovie object.Movie
pointer being encapsulated.First, NSMovie objects can be initialized from a URL, a
pasteboard, or an existing QuickTime Movie pointer. NSMovie
can be initialized with any type of data that QuickTime will accept
using the methods -initWithURL:byReference, -initWithPasteboard,
or -initWithMovie. This includes video, audio, and still
images. Also, you may need to use a URL for initialization from HTTP
and RSTP resources.
The following example shows how to initialize an instance of NSMovie
using a sample movie contained in an application's bundle:
NSString *Example_Path;
NSURL *Example_URL;
NSMovie *Example_Movie;
//+
// Load the movie from within the current Bundle
//-
Example_Path = [[NSBundle mainBundle] pathForResource: @"Sample_Movie"
ofType: @"mov"];
Example_URL = [NSURL fileURLWithPath: Example_Path];
Example_Movie = [[NSMovie alloc] initWithURL: Example_URL byReference: YES];
Second, NSMovie provides methods to help you filter file
types and pasteboards that can be used to initialize an object instance.
Using -canInitWithPasteboard and -movieUnfilteredPasteboardTypes,
you can test the suitability for initialization using a particular pasteboard
or retrieve a list of pasteboard types that will be accepted. Additionally,
you may also find it useful to use -movieUnfilteredFileTypes
to restrict the choices a user has when selecting files.
The following is an example of how you might use NSMovie
to filter the file types that will be selectable from an NSOpenPanel.
//+
// Create and display an open panel with only movie types selectable
//-
Add_Video_Panel = [NSOpenPanel openPanel];
Button_Clicked = [Add_Video_Panel runModalForDirectory: NSHomeDirectory()
file: nil types: [NSMovie movieUnfilteredFileTypes]];
Finally, NSMovie can be used to return information about
the QuickTime movie that it has encapsulated. The method -QTMovie
returns the QuickTime Movie pointer that is the source
for the NSMovie object. This is the key ingredient for
extending the value of NSMovie when working with QuickTime.
With a Movie pointer in hand, you can use functions from
the QuickTime API to access, manipulate, and change any QuickTime attributes
that are unavailable from NSMovie.
The following example illustrates how to access several QuickTime movie
attributes that are not available from NSMovie:
- (void) Get_QuickTime_Attributtes: (NSMovie *)Example_Movie
{
Movie QuickTime_Movie;
TimeScale Time_Scale;
TimeValue Current_Time, Movie_Length;
QuickTime_Movie = [Example_Movie QTMovie];
Current_Time = GetMovieTime( QuickTime_Movie, nil );
Time_Scale = GetMovieTimeScale( QuickTime_Movie );
Movie_Length = GetMovieDuration( QuickTime_Movie );
}
Okay, so you have an NSMovie object ... now what? Most of
the time, the answer to that question will be that you need to play
it. That's where NSMovieView comes in.
|
|
Figure 1 illustrates a typical use of NSMovieView. In this
case, the view is displayed with a movie controller, which allows the
user to play the movie, set the volume, reposition the play head, and
make selections.
Before you get started working with NSMovieView, I recommend
that you get thoroughly familiar with the features and behavior of Apple's
QuickTime Player. Much of the functionality of NSMovieView
is demonstrated directly in the features of the QuickTime Player.
|
In most cases, you will use the Interface Builder to create and set up
your NSMovieView. The Cocoa Graphic Views palette contains
an NSMovieView that you can drag onto the windows of your
application.
|
|
To create the window in Figure 1, I dragged the NSMovieView
from the palette onto my application's window.
|
|
The Interface Builder Inspector (the right-hand window in Figure 3) is
used to set the attributes for your NSMovieView. I wired
the necessary outlets and actions and added a small piece of code to
initialize the movie, and voila!
Instead of going through the various methods for NSMovieView
one at a time, I have put together a small example that illustrates
a wide variety of NSMovieView's functionality.
Figure 4 illustrates what the final version of the example application will look like.
|
|
So, how do we get there?
I'll assume that you have done some (even a small amount) Cocoa programming and that you are familiar with Apple's Developer Tools — Project Builder (PB) and Interface Builder (IB).
The design of our example will be simple and have the following attributes:
Here's the step-by-step recipe that I used to make this example. You may wish to download the source code and follow along as we go through the process.
Start up Project Builder and select New Project from the File menu. When the Project Builder Assistance displays a list of project types, select "Cocoa Application," then enter the name "NSMovie_Example."
Double-click the MainMenu.nib file in the Project Builder "Groups
& Files" tab and Interface Builder will be started for you.
To build my user interface, I have dragged five elements from the IB palette
onto my application window: an NSMovieView, an NSSlider
(horizontal with tick marks), an NSTextField, an NSButton
(borderless), and another NSSlider (horizontal without
tick marks).
Next, I used the IB inspector to set the attributes for each element:
NSMovieView size to 320 by 240
and un-check the "Show Controller" attribute.Control_Button to the
"Play" image, and the alternate icon to the "Pause"
image.Volume_Slider
to match with the minimum and maximum volumes allowed by QuickTime (0.0
and 1.0, respectively).The result can be seen in Figure 5.
|
|
Step Three: Outlets, Actions, and Controllers
The NSMovie_Example application will need a controller to handle the
actions initiate by the user interface. From the Interface Builder Classes
menu, I created and instantiated an NSObject subclass named
NSMovie_Example_Controller.
|
|
Figure 6 shows the IB inspector after I have created outlets for each
of the interface elements. Additionally, I have created four Actions (-Scrub_Movie,
-Set_Volume, -Start_Movie, and -Stop_Movie)
that will respond to users' actions.
To complete the process, I used the "Create Files..." item on the IB Classes menu to create Objective C source files for my controller and add them to my project.
|
Returning to Project Builder, the project setup is completed by organizing the source code and resource files.
In addition to the template files that are provided when NSMovie_Example
was created, I have already added files for the button images (Play.tiff
and Pause.tiff) and the controller object (NSMovie_Example_Controller.m
and NSMovie_Example_controller.h). To complete the project, I added
three more files:
NSString
that represents the movie's time in minutes and seconds.NSMovie_Example will play.Once these were added, I created a couple of new groups within the project, and the result is a "Groups & Files" tab that looks like the following:
|
|
NSMovie_Example_ControllersTo finish the coding, I needed to add the logic for the Action methods
in the NSMovie_Example_Controller. In addition to the four Action methods
I had specified in the Interface Builder, I needed to add two more methods
to the controller: -awakeFromNib and -Update_Player_Feedback.
Here's the pseudo-code description for each method:
- (void) awakeFromNib
This method completes the initialization of the controller object. The main tasks are to load the movie einsteins_legacy.mov from the application bundle, make the resulting
NSMovieobject the current movie inMovie_View, initializeTime_Display, and set the parameters ofScrub_Sliderto match the duration of the newly loaded movie.
(IBAction) Scrub_Movie: (id)senderThis method is invoked when someone moves the scrub slider. The main task is to adjust the movie's current time (using
SetMovieTimeValue()) to mirror the value represented by the slider's current position. Additionally,Time_Displaymust be updated to reflect the current movie time.
(IBAction) Set_Volume: (id)senderThis method is invoked when the user moves the volume slider. The main task is to adjust the
Movie_View's volume to reflect the current position ofVolume_Slider.
(IBAction) Start_Movie: (id)senderThis method is invoked after a user clicks the
Control_Button(in its normal state). The main tasks are to start playback in theMovie_Viewand adjust the action of theControl_Buttonso that the next click will stop the movie. This method also creates a repeatingNSTimer(Scrub_Timer) that fires every tenth of a second, sending a message to-Update_Player_Feedback, which in turn animatesScrub_SliderandTime_Display.
(IBAction) Stop_Movie: (id)senderThis method is invoked after a user clicks the
Control_Button(in its alternate state). The main tasks are to stop playback in theMovie_Viewand adjust the action of theControl_Buttonso that the next click will start the movie. This method also stops the repeating time (Scrub_Timer) and resetsScrub_Sliderto its initial position.
(void) Update_Player_Feedback: (NSTimer *) Incoming_TimerThis method is invoked every tenth of second by
Scrub_Timer. The main task is to animateScrub_SliderandTime_Displayto reflect the current playback time ofMovie_View. IfMovie_Viewis done playing, then movie playback is stopped andScrub_SliderandTime_Displayare reset to their initial values.
Special thanks to my friend Wes Vasher. He's responsible for whipping up a short movie for this project.
Hopefully, I've given you enough to get you going on integrating QuickTime into your next Cocoa project.
This QuickTime overview is just the tip of the iceberg. Before you take on your own project, I suggest that you dig deeply into the QuickTime reference materials available at Apple's Developer site. Most notably, check out the QuickTime sample code that uses Cocoa.
The NSMovie_Example project is a place to start, but by no means does
it encompass all the functionality that QuickTime makes available to
you. I would suggest that you take the source code, play with it and
modify it to your own purposes (try adding a movie to your About Box).
That's all for now!
Douglas Welton is the president of Einstein's Legacy that created Cinematics, an innovative new media player that helps you to build and manage playlists of digital movies on your Macintosh.
Return to MacDevCenter.com
Copyright © 2007 O'Reilly Media, Inc.