< Previous PageNext Page > Hide TOC

Building a Simple QTKit Capture Application

In this chapter, you’ll build a QTKit capture player, a simple yet powerful application that demonstrates how you can take advantage of some of the new capture classes and methods available in the next iteration of the QuickTime Kit framework. When completed, your QTKit capture player application will allow you to capture a video stream and record the media to a QuickTime movie. You won’t have to write more than 20 or 30 lines of Objective-C code to implement this capture player.

Using Xcode 3 as your integrated development environment (IDE), along with Interface Builder 3, you’ll see how easy it is to work with the QuickTime Kit framework. In this example, you’ll use the new QTKit capture control provided in the library of controls available in Interface Builder 3. The QTKit capture control will perform much of the work for you in implementing the design of the user interface for this application.

Following the steps in this guide, you’ll be able to build a functioning capture player application that controls the capture of QuickTime movies, adding simple start and stop buttons, and allowing you to output and display your captured files in QuickTime Player. For this project, you’ll need an iSight camera, either built-in or plugged into your Macintosh. You’ll also need Mac OS X v10.5, the latest release of Mac OS X, installed in your system.

In building your QTKit capture application, you’ll work with the following three classes:

For purposes of this tutorial, you won’t need to have a complete understanding of the methods that belong to these capture classes. As you extend your knowledge of the QTKit framework, you should refer to the QTKit Framework Reference, which describes in detail the methods, notifications, attributes, constants, and types that comprise the collection of classes in the QTKit API.

In this section:

First Steps
Create the Project Using Xcode 3
Create the User Interface Using Interface Builder 3
Set Up a Preview of the Captured Video Output
Wire the Start and Stop Buttons
Complete the Project Nib File in Xcode
Implement and Build Your Capture Application


First Steps

If you’ve worked with Cocoa and Xcode before, you know that every Cocoa application starts out as a project. A project is simply a repository for all the elements that go into the application, such as source code files, frameworks, libraries, the application’s user interface, sounds, and images. You use Xcode to create and manage your project.

The QTKit capture player application should serve as a good learning example for developers who may be new to Cocoa and QuickTime. If you already know Cocoa, you probably won’t be surprised at how quickly and effortlessly you can build this capture player application.

What You Need

Before you get started with your QTKit capture player project, be sure that you are running Mac OS X v10.5 and have the following items installed on your system:

Prototype the Capture Player

Interface Builder lets you specify the windows, menus, and views of your application, while Xcode enables you to define the behavior behind them. Interface Builder provides the basic support you need for configuring the items in your user interface. Beyond that, most of the work you do in constructing your application takes place in Xcode.

When designing your application, start by defining your application’s data model in Xcode. Once you’ve constructed a workable data model, you can use Interface Builder to create a set of basic windows, menus, and views for presenting that data. Depending on the complexity of your design, you may also need to create custom views and controls, and then integrate them into Interface Builder and add them to your nib files.

Creating the controller objects and tying them to your data model in your user interface is the final step in the design process.

Of course, you can just jump right in and start assembling windows and menus in Interface Builder. However, using Interface Builder 3, which is the latest iteration, it’s important to have a good understanding of your application’s desired behavior first. Knowing your application’s data model, and knowing what operations will occur on that data, will help you piece together the design elements you need to show in order to convey that information to the end user.

You may want to start by creating a rough sketch of your QTKit capture application. Think of what design elements you want to incorporate into the application. Rather than simply jumping into Interface Builder and doing your prototype there, you may want to visualize the elements first in your rough sketch, as shown in Figure 2-1.


Figure 2-1  Prototype sketch of QTKit capture application

Prototype sketch of QTKit capture application

In this design prototype, you can start with three simple objects: a capture view and two control buttons. These will be the building blocks for your application. After you’ve sketched them out, you can begin to think of how you’ll be able to hook them up in Interface Builder and what code you need in your Xcode project to make this happen.

Create the Project Using Xcode 3

To create the project, follow these steps:

  1. Launch Xcode 3 (shown in Figure 2-2) and choose File > New Project.

    Figure 2-2  The Xcode 3 icon

    The Xcode 3 icon
  2. When the new project window appears, select Cocoa Application.

  3. Name the project MyRecorder and navigate to the location where you want the Xcode application to create the project folder. Now the Xcode project window appears, as shown in Figure 2-3.

    Figure 2-3  The MyRecorder Xcode project window

    The MyRecorder Xcode project window
  4. Next, you need to add the QuickTime Kit framework to your MyRecorder project. Although obvious, this step is sometimes easy to forget. Note that you don’t need to add the QuickTime framework to your project, just the QuickTime Kit framework. Choose Project > Add to Project.

  5. The QuickTime Kit framework resides in the System/Library/Frameworks directory. Select QTKit.framework, and click Add when the Add To Targets window appears to add it to your project.

    Important: This completes the first sequence of steps in your project. In the next sequence, you’ll move ahead to define actions and outlets in Xcode before working with Interface Builder. This may involve something of a paradigm shift in how you may be used to building and constructing an application with versions of Interface Builder prior to Interface Builder 3. Because you’ve already prototyped your QTKit capture application, at least in rough form with a clearly defined data model, you can now determine which actions and outlets need to be implemented. In this case, you have a QTCaptureView object, which is a subclass of NSView, and two simple buttons to start and stop the recording of your captured media content.

Name the Project Files and Import the QTKit Headers

  1. Choose File > New File. In the panel, scroll down and select Cocoa > Objective-C class, which includes the Cocoa.Cocoa.h. files.

  2. Name your implementation file MyRecorderController.m. You’ll also check the item to name your declaration file MyRecorderController.h.

  3. In your MyRecorderController.h file, add #import <QTKit/QTkit.h>.

Determine the Actions and Outlets You Want

  1. Now you can begin adding outlets and actions. In your MyRecorderController.h file, add the instance variable mCaptureView in the following line of code:

    IBOutlet QTCaptureView *mCaptureView;
  2. You also want to add these two actions:

    - (IBAction)startRecording:(id)sender;
    - (IBAction)stopRecording:(id)sender;
  3. Now open your MyRecorderController.m file and add the following actions, along with the requisite braces:

    - (IBAction)startRecording:(id)sender
    {
    }
    - (IBAction)stopRecording:(id)sender
    {
    }

At this point the code in your MyRecorderController.h file should look like this.

#import <Cocoa/Cocoa.h>
#import <QTKit/QTKit.h>
 
@interface MyRecorderController : NSObject {
    IBOutlet QTCaptureView *mCaptureView;
}
- (IBAction)startRecording:(id)sender;
- (IBAction)stopRecording:(id)sender;
 
@end

This completes the second stage of your project. Now you’ll need to shift gears and work with Interface Builder 3 to construct the user interface for your project.

Create the User Interface Using Interface Builder 3

In the next phase of your project you’ll see how seamlessly Interface Builder and Xcode work together, enabling you to construct and implement the various elements in your project more efficiently and with less overhead.

  1. Open Interface Builder 3 (Figure 2-4) and drag the MainMenu.nib file in your Xcode project window on the Interface Builder 3 icon. Because of the new integration between Xcode 3 and Interface Builder 3, you’ll find the actions and outlets you’ve declared in your MyRecorderController.h file are also synchronously updated in Interface Builder 3. This will become apparent once you open your nib file and begin to work with the library of controls available in Interface Builder 3.

    Figure 2-4  The new Interface Builder 3 icon

    The new Interface Builder 3 icon
  2. In Interface Builder 3, you’ll find a new library of controls. Scroll down until you find the QuickTime Capture View control, as shown in Figure 2-5.

    Figure 2-5  QuickTime Capture View control in the library

    QuickTime Capture View control in the library

    The QTCaptureView object provides you with an instance of a view subclass to display a preview of the video output that is captured by a capture session.

  3. Drag the QTCaptureView object into your window and resize the object to fit the window, allowing room for the two Start and Stop buttons in your QTKit capture player.

  4. Choose Tools > Inspector. In the Identity Inspector, select the information (“i”) icon. Click in the field Class and your QTCaptureView object appears, as shown in Figure 2-6.

    Figure 2-6  The resized QTCaptureView object and its class Identity defined in the Identity Inspector

    The resized QTCaptureView object and its class Identity defined in the Identity inspector
  5. Set the autosizing for the object in the Capture View Size Inspector, as shown in Figure 2-7.

    Figure 2-7  Setting the autosizing for your QTCaptureView object

    Setting the autosizing for your QTCaptureView object
  6. Define the attributes of your MyRecorder Window, as shown in Figure 2-8.

    Figure 2-8  Window attributes defined in the Inspector

    Window attributes defined in the Inspector
  7. In the Library, select the Push Button control and drag it to the Window, as shown in Figure 2-9. Enter the text Start and duplicate the button to create another button as Stop. In autosizing, set the struts for both buttons at the bottom and right outside corners, leaving the inside struts untouched.

    Figure 2-9  Specifying Start and Stop push buttons

    Specifying Start and Stop push buttons
  8. Set up the autosizing for your buttons by selecting the button and clicking the Button Size Inspector shown in Figure 2-10.

    Figure 2-10  Setting up the autosizing for the Start and Stop buttons

    Setting up the autosizing for the Start and Stop buttons
  9. In the Library, scroll down and select the blue cube control shown in Figure 2-11, which is an object (NSObject) you can instantiate as your controller.

    Figure 2-11  The blue cube object for your controller

    The blue cube object for your controller
  10. Drag the object into your MainMenu.nib window, as shown in Figure 2-12.

    Figure 2-12  The object from the instantiated as a controller

    The object from the instantiated as a controller
  11. Select the object and enter its name as My Recorder Controller. Then click the information icon in the Inspector. When you click the Class Identity field, the MyRecorderController object appears. Interface Builder has automatically updated the MyRecorderController class specified in your Xcode implementation file. You don’t need to enter the name of this class in the Class Identity field. Note that to verify and reconfirm that an update has occurred, press Return. If the identify field is not automatically updated, you may need to specify manually that it is a MyRecorderController object.

Set Up a Preview of the Captured Video Output

In the next phase of your project, you’ll see how seamlessly Interface Builder and Xcode work together, enabling you to construct and implement the various elements in your project more efficiently and with less overhead.

  1. In Interface Builder, hook up the MyRecorderController object to the QTCaptureView object. Control-drag from the MyRecorderController object in your nib file to the QTCaptureView object. A transparent panel will appear, as shown in Figure 2-13, displaying the IBOutlet instance variable, mCaptureView, that you’ve specified in your declaration file.

    Figure 2-13  The mCaptureView instance variable wired as an outlet for the MyRecorderController object

    The mCaptureView instance variable wired as an outlet for the MyRecorderController object
  2. Click the Interface Builder outlet mCaptureView to wire up the two objects.

Wire the Start and Stop Buttons

Now you’re ready to add your Start and Stop push buttons and wire them up in your MainMenu.nib window.

  1. Control-drag each of the Start and Stop buttons from the window to the MyRecorderController object, as shown in Figure 2-14. Click the startRecording: method in the transparent Received Actions panel to connect the Start button, and likewise, the stopRecording: method in the Received Actions panel to connect the Stop button.

    Figure 2-14  Wiring the recording buttons to the MyRecorderController object

    Wiring the recording buttons to the MyRecorderController object
  2. Now you’ll need to hook up the window and the MyRecorderController object as a delegate, shown in Figure 2-15. Control-drag a connection from the window to the MyRecorderController object and click the outlet, connecting the two objects.

    Figure 2-15  Connecting the window to the MyRecorderController object as the delegate outlet

    Connecting the window to the MyRecorderController object as the delegate outlet
  3. To verify that you’ve correctly wired up your window object to your delegate object, select the Window and click the Window Connections Inspector icon, shown in Figure 2-16.

    Figure 2-16  The window object wired up correctly to the delegate object

    The window object wired up correctly to the delegate object
  4. Verify that you’ve correctly wired up your outlets and received actions. Select the My Recorder Controller object and click the My Recorder Controller Connections Inspector icon, shown in Figure 2-17.

    Figure 2-17  My Recorder controller object and buttons wired up correctly

    My Recorder controller object and buttons wired up correctly
  5. Check the My Recorder Controller Identity Inspector panel to confirm the class actions and class outlets, shown in Figure 2-18.

    Figure 2-18  Class actions and outlets specified in the identity inspector

    Class actions and outlets specified in the identity inspector
  6. Click the MainMenu.nib file to verify that Interface Builder and Xcode have worked together to synchronize the actions and outlets you’ve specified. A small green light appears at the left bottom corner of the MainMenu.nib file next to MyRecorder.xcodeproj to confirm this synchronization, as shown in Figure 2-19.

    Figure 2-19  The green light indicating synchronization between Xcode and Interface Builder

    The green light indicating synchronization between Xcode and Interface Builder
  7. Save your nib file.

  8. Verify that your QTKit MyRecorder capture application appears as shown in Figure 2-20.

    Figure 2-20  The completed user interface for the MyRecorder application

    The completed user interface for the MyRecorder application

You’ve now completed your work in Interface Builder 3. In this next sequence of steps, you’ll return to your Xcode project, adding a few lines of code in both your declaration and implemention files to build and compile the QTKit capture player application.

Complete the Project Nib File in Xcode

To complete the project nib file, you’ll need to declare the outlet you set up and connected in Interface Builder, and define the instance variables that point to the capture session, as well as to the input and output objects.

  1. In your Xcode project, you need to add the instance variables to the interface declaration. Add these lines of code in your MyRecorderController.h declaration file:

    @interface MyRecorderController : NSObject {
    QTCaptureSession           *mCaptureSession;
    QTCaptureMovieFileOutput   *mCaptureMovieFileOutput;
    QTCaptureDeviceInput       *mCaptureDeviceInput;

    The mCaptureSession instance variable points to the QTCaptureSession object, and the mCaptureMovieFileOutput instance variable points to the QTCaptureMovieFileOutput object. The last line declares that the mCaptureDeviceInput instance variable points to the QTCaptureDeviceInput object.

    The complete code for your declaration file should look like this:

    //  MyRecorderController.h
    #import <Cocoa/Cocoa.h>
    #import <QTKit/QTkit.h>
     
    @interface MyRecorderController : NSObject {
     
        IBOutlet QTCaptureView *mCaptureView;
     
        QTCaptureSession            *mCaptureSession;
        QTCaptureMovieFileOutput    *mCaptureMovieFileOutput;
        QTCaptureDeviceInput        *mCaptureDeviceInput;
     
     
    }
    - (IBAction)startRecording:(id)sender;
    - (IBAction)stopRecording:(id)sender;
     
    @end
  2. In your MyRecorderController.m implementation file, add these lines of code, following your @implementation MyRecordController directive:

    Important: There is a specific, though not rigid, order of steps you want to follow in constructing your code. These are the steps you need to follow:

    1. Create the capture session.

    2. Find the device and create the device input. Then add it to the session.

    3. Create the movie file output and add it to the session.

    4. Associate the capture view in the user interface with the session.

    - (void)awakeFromNib
    {
    //Create the capture session
        mCaptureSession = [[QTCaptureSession alloc] init];
     
    //Connect inputs and outputs to the session
        BOOL success = NO;
        NSError *error;
     
    // Find a video device
    QTCaptureDevice *device = [QTCaptureDevice defaultInputDeviceWithMediaType:QTMediaTypeVideo];
        if (device) {
            success = [device open:&error];
            if (!success) {
                // Handle error
            }
    // Add the video device to the session as device input
            mCaptureDeviceInput = [[QTCaptureDeviceInput alloc] initWithDevice:device];
            success = [mCaptureSession addInput:mCaptureDeviceInput error:&error];
            if (!success) {
                // Handle error
            }
    // Create the movie file output and add it to the session
        mCaptureMovieFileOutput = [[QTCaptureMovieFileOutput alloc] init];
        success = [mCaptureSession addOutput:mCaptureMovieFileOutput error:&error];
        if (!success) {
            // Handle error
        }
    // Set the controller be the movie file output delegate.
        [mCaptureMovieFileOutput setDelegate:self];
     
    // Associate the capture view in the UI with the session
     
        [mCaptureView setCaptureSession:mCaptureSession];
        }
    // Start the capture session running
            [mCaptureSession startRunning];
     
    }
  3. Add these lines to handle window closing notifications for your device input and stop the capture session.

     
    - (void)windowWillClose:(NSNotification *)notification
    {
        [mCaptureSession stopRunning];
        [[mCaptureDeviceInput device] close];
     
    }
  4. Insert the following block of code to handle deallocation of memory for your capture objects.

     
     - (void)dealloc
    {
        [mCaptureSession release];
        [mCaptureDeviceInput release];
        [mCaptureMovieFileOutput release];
     
        [super dealloc];
    }
  5. Implement these start and stop actions, then add the following lines of code to specify the output destination for your recorded media, in this case a QuickTime movie (.mov) in your /Users/Shared folder.

     
    - (IBAction)startRecording:(id)sender
    {
        [mCaptureMovieFileOutput recordToOutputFileURL:[NSURL fileURLWithPath:@"/Users/Shared/My Recorded Movie.mov"]];
    }
     
    - (IBAction)stopRecording:(id)sender
    {
        [mCaptureMovieFileOutput recordToOutputFileURL:nil];
    }
  6. Add these lines of code to finish recording and then launch your recording as a QuickTime movie on your Desktop.

     
    - (void)captureOutput:(QTCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL forConnections:(NSArray *)connections dueToError:(NSError *)error
    {
        [[NSWorkspace sharedWorkspace] openURL:outputFileURL];
        // Do something with the movie at /Users/Shared/My Recorded Movie.mov
    }

Implement and Build Your Capture Application

After you’ve saved your project, click Build and Go. After compiling, click the Start button to record, and the Stop button to stop recording. The output of your captured session is saved as a QuickTime movie in the path you’ve specified in this code sample.

Now you can begin capturing and recording with your QTKit capture player application, as shown in Figure 2-21. Using a simple iSight camera, you can capture and record media, and then output your recording to a QuickTime movie. .


Figure 2-21  MyRecorder with Start and Stop buttons

MyRecorder with Start and Stop buttons

In the next chapter you’ll see how easy it is to add audio input capability to your QTKit capture player application. Only a half dozen lines of code are required. You can build on what you’ve already written for your MyRecorder project, and add audio, along with support for DV cameras other than your iSight camera, with a minimum of programming effort.



< Previous PageNext Page > Hide TOC


© 2007 Apple Inc. All Rights Reserved. (Last updated: 2007-10-31)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.