Graphics and Media • 1:11:27
QuickTime 7 in Tiger introduces QTKit, a new robust Cocoa framework for accessing QuickTime. The QTKit framework provides a set of Objective-C APIs that make it simple to add movie playback, editing, and importing and exporting, and more. We'll go hands on and show you how to use QTKit to unleash the power of QuickTime in your Cocoa application.
Speakers: Tim Monroe, Gary Flint, Tom Maremaa, Michael Johnson
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Good afternoon and welcome to session 211. This is Application Programming with QTKit. My name is Tim Monroe and I've had the pleasure of serving as the tech lead for QTKit for about the past year and a half. I'm sure you're all aware that QTKit is a Cocoa framework for QuickTime.
The Cocoa side of the ledger is fairly straightforward. To say QTKit is a Cocoa framework means that it is a set of Objective-C classes. The interfaces to the methods in these classes use standard Cocoa data types like NSArrays, NSDictionary's, file names are passed as NSStrings, URLs as NSURLs.
Changes in objects in the kit can be announced to other objects in your application by means of notifications, the standard Cocoa mechanism. And some of the objects in the QTKit have delegate methods so that other objects in your application can actually take part and modify the behavior of QTKit objects.
So the QuickTime part is a little tougher. QuickTime is a huge technology, and it's simply not true to say that QTKit wraps the entire QuickTime technology. Probably the fairest thing to say is that QTKit is a Cocoa framework that allows you to open, display, and manipulate QuickTime movies.
So there's huge chunks of functionality that we don't yet handle in QTKit that we ultimately would like to, such as audio and video capture, broadcasting, working with specific media types. Those are things that we'd like your input on, where you'd like to see the QTKit go, so that we can grow the QTKit to meet your needs.
Now, Kit already has some very important clients, and I've listed just three of them here. It turns out that all through Tiger, people are turning to QTKit in order to display QuickTime movies. These three get special mention because they are what I call key clients. They have not only used the Kit, but they've also given us important feedback, and more importantly, made demands on the Kit. So there are methods in the Kit that can be traced to each one of these guys.
Spotlight, for instance, uses QTKit to display the thumbnail images of any movies that might turn up in a search that you have executed. QuickTime Player, as I'm sure you're aware, is built heavily on QTKit. Our goal was to put as much of the functionality for opening and playing QuickTime movies into the framework so that QuickTime Player could be a fairly thin layer on top of that.
The benefit to you as developers, of course, is that you get all that functionality basically for free. And also, you may have seen the earlier keynote this week. Pixar has based a number of their in-house movie production tools on QTKit, and we'll see a little bit of that later.
So what are we going to do today? I'm going to give a brief overview of QTKit. It's not going to be in tremendous depth because you've got some really good documentation to work from. Then we will jump right into hands on coding. I'll start off by building a couple of command line tools to give you an idea of just how easy it is to get up and running with QTKit.
Then we will build from scratch a graphical application, which is what most of you are likely to want to do. That is to display QuickTime movies in an application on the screen. And finally, we'll end with some fairly advanced considerations, in particular how to use QTKit in a multi-threaded environment. Now I'm skeptical that we're actually going to have time for summary or Q&A, but that's not all bad.
Because later this evening, we have a... a three hour session over in the Tiger Lab, which will be devoted entirely to QTKit. So come on in, bring your questions. We can look at your code. If you have existing code that uses the Cocoa QuickTime classes, NSMovie or NSMovieView, maybe we can help you port them over. And then if that's not enough, we'll be back here tomorrow morning in the Graphics and Imaging Lab to continue that discussion.
So let me give a quick overview of what's in QTKit. QTKit currently consists of five public classes that are listed there in white, and two data structures that you'll be working with, which are listed in blue. QTMovieView is just what you'd expect. It's a view object where you will display QuickTime content and allow the user to interact with it. The next four public classes are object-oriented wrappers around QuickTime data types, in particular movies, tracks, media, and data references. And finally, we have the two structures, which are our ways of indicating times or time ranges in a movie.
So let's look at QTTime. As you can see, there are three fields here. The first one is a duration or time value for a movie. Now that value is always interpreted relative to the time scale field, which is the number of units per second in the time value.
Finally, we have a flags field, which allows us to indicate various things about the QT time. For instance, if you were to pass into us a QT time that had a time scale of zero, which is invalid, then we would pass you back a QT time that had the QT time is indefinite bit set, essentially indicating to you that you should ignore the other fields.
There are a large number of utility functions that you can use to fill out a QT time structure or to operate on QT time structures. So you can see the QT make time, you give us the time value and the time scale, and we'll pass you back an appropriately filled out QT time structure. Or if you have two QT time structures and you'd like to add them to one another, you can use the QT time increment function.
Finally, as an example, if you have a QT time structure and you'd like it represented in another time scale for some reason, you can use the QT make time scaled function. QTTimeRange is again a very simple structure. It indicates a range within a movie and it is just what you'd expect. It's a QTTime structure that indicates the start time and another one that indicates the duration. And once again there's a large number of utility functions you can use to fill out QTTimeRange structures or to perform various operations on them.
QTMovie is the first class I'll talk about. It is by far the richest class in QTKit. There's a large number of functions or methods that you'll want to use here. There's a fairly broad number, spectrum of methods that you can use to create a QTMovie object. And I've listed just some of them here.
For instance, if you give us a file name, we will read the movie from that file and give you a QTMovie object. You can also pass us a URL, a data reference. You may have your movie data in an NSData block. Or finally, you could put a movie into the Nib file and read it out of the Nib file with the movie named method.
You'll notice that most of these methods have an error parameter. So that if you, if we encounter an error, when trying to open the movie that you've indicated to us, we can pass you back an NSError object, which will indicate to you what error occurred, and in fact will give you a localized string that you could display to the user.
QTMovie, as I said, is a very rich class, and this is just a small summary of some of the methods that are in it. There are methods for going to the beginning and the end of the movie. There's a method for getting the current time, and notice that it gives you back a QT time structure.
If you want, you can get a dictionary that contains key value pairs for all the publicly defined attributes in a movie. There are a number of editing methods, for instance, delete segment, which we'll work with in a minute. If you want to get an array of all the tracks in a movie, we have a tracks method. And the last two methods here are particularly important.
I mentioned that QuickTime is a very broad technology, and that QTKit currently wraps only a part of it. So it's fairly likely that if you're doing intricate stuff, you'll want to use those Carbon APIs. To do that, you're going to need either the movie or the movie controller identifier, and you can get those with the QuickTime movie or the QuickTime movie controller method.
QTTrack, as you know, a QuickTime movie consists of a number of tracks. So we have the QTTrack object to correspond to a QuickTime track. Currently there's only one way to create a QT track object, which is to already have a QuickTime track and then to use the initWithQuickTimeTrack method. Once again, looking at the final method there, if you need to use Carbon APIs that operate on tracks, you can get the track identifier associated with the QT track object.
Now a class that you're likely to use quite extensively is QTMovieView. This is a subclass of NSView that is associated with the QTMovie object. You make that association with the SetMovie method, which is the first one I've listed there. There are a number of attributes that you can get and set on the MovieView. For instance, you can determine whether the controller bar that's drawn at the bottom of a movie is visible or invisible.
You can set whether resizing the view preserves the aspect ratio of the movie or not. If you have that attribute set, then parts of the MovieView that are not drawn with the movie data will be painted with a fill color, which is specifiable by you. And also you can set the editable state of the movie in the MovieView. So much for theory. Let's actually do some work with QTKit here. And let's move over to demo one, please.
[Transcript missing]
Okay, so we've opened the movie, we've made it editable, now let's edit it. We do that with the delete segment method in the next line of code, and that chops out the specified segment. Then we can call the updateMovieFile method, and that will update the movie file associated with this QTMovie object. So let's actually run this. First thing I want to do is I've got a movie called abc.move in the current directory. Let's copy that into the specified location in /tmp. Now let's build our tool.
You can see I'm using C compiler, telling it what my output file is, what my input file is. And notice that I'm linking against the QTKit framework and the foundation framework. So let's build that tool. That took no time at all. And now let's run it. Okay, so let's open the original file and we'll see that it's a six second movie that has three frames, each frame is two seconds long. Nothing exciting there. Now let's open the edited movie.
And it should be just the same, except that it should have the final two seconds cut out. And indeed when we play it we see that it is now four seconds long, missing the last frame. So there was a very simple command line tool that did actual interesting work. So let's quit that and let's do one more example.
I'm moving to another directory where I have this move to 3GP.m file, and let's take a look at that. This is actually simpler than the one we just considered. What this does is it will open that same movie file and then it will export it into a 3GPP file. That is to say a file that is suitable for streaming to a 3GPP handset such as a cell phone.
So again we include the QTKit headers, we create our auto-release pool, later release it, we open the movie with exactly the same line of code, and what we want to use now is the write-to-file method. Write-to-file is a general method for taking an existing movie and putting it into a different file in some different form. That could mean flattening the movie, it could mean exporting the movie, in this case we want to export the movie.
How do we do that? Well, the key is this "with attributes" dictionary. And if you look here, when we create the dictionary, we add in a key value pair that says "I want you to export the movie" and I want the type of the exported file to be a 3GPP file. Notice that we did not, in this case, need to make the movie editable, because we're not editing the movie. We're opening it and then writing it out as some other form. So now let's run this guy. First we better build it.
Copy that command line there. Build the tool. Run it. And now let's open that file at /temp/sample.3gp. And notice that this is smaller than the original movie. That's because the 3GPP spec says these are smaller movies. This is 176 by 144 pixels. Now, if the original movie had had a soundtrack, this export operation would also have transcoded the soundtrack into a 3GPP compatible form.
So that's all I want to do for you here. What I'd like to do now is bring up the tech lead for the QuickTime player. Could I go back to slides please? Thank you. For the QuickTime player application to give us a few thoughts on QuickTime player's use of QTKit. Gary Flint. Thanks. Thank you.
Are we on? OK. One of the challenges for the QTKit team, of course, was to build a framework that would not only encompass some subset of QuickTime, but also create an interface that looked familiar to Cocoa programmers. Because those of you familiar with the QuickTime APIs know they're very different indeed.
So I'd like to show some examples of integrating QTKit with an AppKit-based application, of which the QuickTime player in QuickTime 7.0 is. The first feature I'm going to show is the code it required for us to implement a multiple undo. The old QuickTime APIs supported single-level undo. And when we were working on the Cocoa-based player, we certainly wanted to support multiple undo, because most Cocoa applications do.
The decision was, is that a play-based? Is it a clear feature or a kit feature? Fortunately for you, and for us, the feature was pushed down into the kit. So the kit gives us multiple undo support. And here's what it takes to access that functionality in your own application.
This may look a little oversimplified, but this is actually it. As Tim mentioned earlier, you have to turn on editability of a movie. Once you've built your user interface using Interface Builder and connected up your edit menu items appropriately, it all just works if you're using a Qt movie view.
And this Qt kit, besides providing support for multiple undo, of course, is implementing most of the high-level editing features of the edit menu, cut, copy, paste, et cetera. The next example demonstrates the code we had to write in the Cocoa player to support drag and drop. It may look like we didn't finish the slide here, but this is actually it. The Qt kit completely implements drag and drop support.
If you turn on editability, it's just there. If you're familiar with the old Carbon APIs, it did take one line of code in the old Carbon APIs to enable the movie controller to support drag and drop. But we're actually not even using that functionality in Qt kit. Drag and drop has been re-implemented so that it supports all the drag and drop features you would expect in a Cocoa application. I think the Qt kit team had a motto, which should be less code, more features.
The next example here is saving a document. Tim showed earlier how to update a movie file as well as how to write to a new movie file. In QTKit, basically we have to make one choice when the user requests to save a document, and that's can this movie be saved on top of itself? Some movie files, some formats cannot, such as .mp4. They have to be saved into a separate file.
So this is the extent of the logic in QuickTime Player to make that determination. If it can't be saved on itself, we save it as a separate file. Otherwise, we save the file in place. And here's the implementation of the method to actually update the movie file in the player. If it's a save operation, we simply call updateMovieFile. So even in the player, saving an edited movie is as simple as the sample that Tim gave earlier.
The next example QTKit is listening for notifications. If you use the Carbon APIs and you've had to deal with an action filter, you know that there are a lot of different actions. You have to decide what you're going to listen to, what you're not interested in listening to, and they have different parameters, different, and sometimes slightly different behaviors.
QTKit has basically encapsulated these mechanisms as well as a few additional mechanisms behind Cocoa notifications. So you can basically register to listen to notifications that your application is interested in. In this case, this shows listening to a movie edited notification. The next slide shows a few additional notifications that the kit supports, listening to movie size change notifications, did end, etc.
Why are there of wired movie support for enter and exit full screen. One that I'd like to point out here is the load state change notification, because that was not an action in the Carbon API. It was actually a toolbox call. The old player, the Carbon player, actually had probably over 150 lines of code dedicated to monitoring the movie load state and knowing when the load state had changed so it could do the right thing. But all that functionality is now implemented in the kit.
So all you have to do is listen for a load state notification, and your application will receive it when QtKit sends the notification. So if you're wondering how much of QtKit we've actually used in QuickTime Player, we're using the vast majority of the calls and features of QtKit.
Since QtKit covers the basic functionality for playback and file handling functionality that you'd want. And in QuickTime application, it allows us to focus on the unique features of QuickTime Player. And I'd say that's the same reason for anyone to adopt QtKit. It handles all the basic functionality for you, and you can focus on whatever makes your application unique. Thanks.
Thank you, Gary. So I don't think you came here to build command line tools, so I think you want to build some GUI tools. And to lead us through that process, I'd like to bring up the person who is in charge of the documentation for QTKit, and that is Tom Maremaa.
Thanks, Tim. QTKit, awesome or what? Good afternoon. Quickly, I'd like to get a show of hands. How many of you guys out there are Cocoa developers? Do I see any? Wow, awesome. How many of you guys are QuickTime developers? Not bad. How many of you guys like to cook? Let's see some show of hands. All right. Good things happen in the kitchen.
Good recipes come out of the kitchen. The recipe for the QTKit player app came out of this eco-friendly brown paper napkin from the Cafe Mac cafeteria on the Apple campus. So from this, rather than pass this around to you guys, I thought, Hey, why don't we blow it up full screen? I'd like to do three things here with my QTKit Player app. Number one, I want to import, and I want to import everything, all the media types, variety of media types that QuickTime supports, and that's 200 and counting.
Number two, I'd like to do three things here with my QtKit player app. Number one, I want to import, and I want to import everything, all the media types, a variety of media types that QuickTime supports. And that's 200 and counting. Number two, I can really do take advantage of the editing capabilities. So with that, let's move forward to demo one, and let's get cooking.
Welcome to my kitchen. As you can see, I've got an appetizer, which I'm going to build from scratch. I've got something in the oven. If you guys downloaded the sample code, you've got it as well. I also have the cookbooks, the QTKit reference, programming guide, and the update to QuickTime 7. And I have something in the freezer. And finally, I have a lot of media goodies.
And I want my QTKit player to, as I say, do things with my media goodies. So with that in mind, let's get started. With Xcode, let's launch Xcode and let's build it from scratch. Maybe you guys want to follow this along with me, at least take it up to a certain point.
Obviously, start my new project, and what am I looking at? I'm looking at a Cocoa document-based project. What do we want to call it? QTKit, how about just Simple Guy Player? Let's be imaginative, right? There's my Simple Guy player. And if I open it up, I can see what I have. First thing that I need to do, I need to add to my project the QTKit framework. Well, where does that live? That lives in the system folder.
Library, where else would it be? Frameworks, and with a little bit of scrolling if I can get there. Hello? Down. There it is. That's what I need to add. Add it to my target, and I'm cooking, okay? Next thing that I want to do is I'd like to take a look at my
[Transcript missing]
Maybe Tim can help me out on this one, huh? My file extensions for QuickTime are mov, right? And my OS types are the QuickTime file types, which are mov. So I'm going to save that in my info.p list. Next, Let's go and launch Interface Builder. Hey, where is our QTKit palette? Let's find it. The QTKit palette resides Of all places, let's open up our QTKit. Don't ask me to explain why, but it does live in the extras.
is that not able to open it up? Just add. There we go. You won't let me open up extras? There it is. There's some palettes, and there's my QTKit palette. Okay. Why don't I close my preferences very quickly, and let's just do some You won't let me open up extras? There it is. There's some palettes, and there's my QTKit palette. Okay. Why don't I close my preferences very quickly, and let's just do some You won't let me open up extras? There it is.
There's some palettes, and there's my QTKit palette. Okay. Why don't I close my preferences very quickly, and let's just do some Close enough. Next thing that I want to do is I want to go into my My Document class and simply add an outlet to my document. Let's call it MmovieView, we've added that.
Little mouse movement here, hello. Got to wire it up. Press the control key, and there we are. We've wired it up, and let's connect it. Connection made. All right. We're cooking. Now, if I do an inspection, I can take a look and see what attributes are available to me. I can set the fill color. I have a checkbox for show controller, etc.
All right. One more thing to do, and of course, size does matter. I've got to set some springs. And I am done here. I can close down my nib file, save the changes that I've made. and go back into my QTKit simple guide. I need to add a few lines of code to make this thing all happen. Number one, let's go into my declaration file.
This is what I have to do. Two things have to happen here. I can just whack this. Who is this? Let's take this. Two things, my import statement and my variable that points to the QT movie view. I'm done there. Next, let's go into the implementation. Couple of lines of code that we want to add here that are important that we've already talked about. In my window control did load nib, hey, I need to add some code here that executes when my window controller loads a movie, a documents window. And I'll explain those once I can move over here.
As you can see, here's what I have, my initWithFile method. This initializes my QTMovie object. With the data from a file, with the data in the file specified by the file, by the name file name, as you can see. Second line of code, and this is what you've seen already, is the set attribute, my set attribute for key method, which I invoke. Now I have a whole list of QTMovie attributes, and this is the one that I need that will enable me to handle the editing menu items that I need and to support movie editing in my QTKit player.
Last line of code, one of the most commonly used, is the setMovie method. And in this case, what I'm doing is I'm setting my QTMovie object in a QTMovie view to movie. OK, we're almost there. Final lines. Hey, we want to be able to edit our saved movie. And in order to do that, we need an override. And I think I can get it from here. and I are going to show you how to use the QTKit to launch a new project.
Let's go ahead and There is my simple guy player. Wow, not really, hello. And guess what I can do? I can go into My Media Goodies, and as you can see, I will be able to open up lots of media goodies. A QuickTime VR, for example. This is the Apple campus. You guys have been there. And I'll just take you real quick-- whoops, hello? VR.
This is where the cafe, this is the beginning of the napkin that I was showing you at the Cafe Max, so. There is historical continuity here. And in fact I can open up QuickTime content, play it. In addition, I have a contextual menu that lets me play my movie, stop it, cut, copy, paste, etc. Alright, so, Let's close this out. As you can see with just a few lines of code, I have a working QTKit media player app.
Aha, as they say on the cooking shows, let's turn it up another notch. Let's go back into the kitchen. Why don't we close our simple guy player down. Go back into our kitchen and let's see what was in the oven. Okay. Hey, it's the QTKit player import. Why don't we launch the player import.
Let me just close this down. And this is where it is. All right. Let's check what we've done and evaluate. Open up. As you can see, I've added an app delegate class. And there's a reason for that. I won't get into it. It has to do with displaying the default movie and also opening up other QuickTime movies.
Now, a couple of things have happened here. In my Info.plist, if I look closely here, I have added another document type, movie document data. And guess what I'm doing? I'm saying, "Hey, open up the floodgates. Let me open and display the broad range of QuickTime content, still image formats, JPEGs, GIFs, etc., VR, skin movies, wired movies, anything that QuickTime can open and display and that I can play with." Now, interestingly enough, as you've seen, I've renamed. And I think good things happen when things are baked.
As we have merged, I don't know how to describe it exactly from a writer's perspective, a document-based architecture with QuickTime movies. So we end up with movie documents. And that's what I've named everything. If we look at our nib file here, our movie document nib file, no changes. We've built it using the QTKit palette and it's unchanged. We don't have to do any modifications to that. However, our main nib, that's where, let me clear up a little bit of real estate here.
As you can see here, this is where we have added Additional capabilities and done a lot of different connections. If you can see, open document, movie obviously, add scale. Replace, Trim, etc. And movie playback. All the way start, stop, go to frame, poster frame, which is really the first frame. Set fill color, etc. So we've done some work there, not too complicated, and it's done.
Let's take a look briefly, we won't have time to get into this at great length, but I want to show you One of the things that I've done here as far as an NS document override, the read from file. This is important and I'll show it to you later when we get to the implementation of it. This is our declarations are really quite straightforward. These are the actions that I've added as you can see.
Now, if we go quickly into our implementation file, you'll see that we have specified a constant default width for non-visual movies. Remember in our original drawing, we wanted to open up JPEGs. We wanted to open up VR. But we also want to open up audio files, MP3 files, AIFF files, etc. And this enables us to do that.
It's a pound defined. We've specified the width that it opens. And that's that. So then let's go quickly down to Read from File. And this is where you can see what we have done. Looks kind of familiar, doesn't it? The heavy lifting is done with the movie with--whoa, sorry.
with MovieWithData. And this returns NSData and it opens up the floodgates, as you can see. In addition here, the CanInitWithFile, this is something that Tim will talk about. and explain a little bit more detailed. Also in my panel, you'll see that I've used the can init with file as well. And this takes care of a lot of goodies for us. OK, so I'm going to close this off, build, compile, run. Hello, hello, hello, hello, hello.
There we are. Guess what? Let's import. And as you can see, when we look in our Media Goodies and Contents folder, we can open up a lot of stuff. So let's open up very quickly an audio file. I like that. Remember those days?
[Transcript missing]
Now we have beauty, gone to the end of it, gone to the beginning. We could even clone it, and it is big. and we have editing. So there you are. Happy cooking.
So what I want to do now is sort of pick up where Tom left off on a few issues and clean up a few loose ends. So let's go back to demo one. And I want to open up a project which is essentially identical to the first project that Tom built.
And that is here. In the downloadable code, it's called QT Advanced Document. I don't know who gave it that name. I called it QT Player Extras. And that's what I'll play with here. So again, this is essentially the same as the first project that Tom built. files will be useful. Let me just run this out of the box.
Build it, run it, and now we will open a file. And I want to go back to my own. Here's our set of media files and here's our good old abc.move and it's just that six second movie that we know and love. Now there's something wrong with this. What I've got is a QuickTime movie that completely fills the content region of the NS window.
Well, the movie display is fine. The start and stop button works fine. The thumb works fine. The frame backward button works just fine. But the frame forward button does not work. I can click it all day and it's not going to work. What we have here is two objects that are claiming that little square of real estate.
The QT movie thinks it owns this little square of real estate down here, and it wants to put its frame forward button there. But also the NSWindow thinks it owns that piece of real estate. And guess who wins? Well, the NSWindow wins. Its resize box is what's operative there. So if you want to have an application like this where you fill the window, it would be nice to work around that particular issue. And it's actually quite easy to do that. Let's go into our windowDidLoad nib and... Oh no, this again.
Don't you love formatting? The workaround for this is to tell the NSWindow object not to draw its resize indicator, and we can do that by uncommenting this line, which says, Dear NSWindow that's attached to the movie view, please don't show your resize indicator. And there is a similar method in QTMovieView, which says, why don't you show your resize indicator.
Now when we build with these two lines uncommented, we should see a little bit better behavior. And now we see that the movie controller has drawn its standard resize button here, and we can now use the frame forward button just the way we thought we could, and the movie continues to resize just the way we wanted it to. So there's a nice little refinement to get things to work really nice.
One other thing I want to touch on is Gary showed us how fundamentally the QTPlayer is built on QTKit. So let's just have a pop quiz. Who thinks they know what method QTPlayer uses to open a file, to open a movie file? How many votes do I get for init with file? No? Movie with file? Oh, you guys are hard.
We got one vote here. Okay, in fact it does not call initWithFile. It uses a different initializer that I haven't mentioned, and this is where this formatting is going to kill us. QuickTime Player uses a method called initWithAttributes. Now the reason we have init with attributes is to override some of the default behaviors of QTMovie. Now we haven't talked a whole lot about what those default behaviors are, but one of them worth noting is that when we open a movie, we open it asynchronously. That means when you call initWithURL, we will immediately return to you a QTMovie object.
Now it's possible that you can't do much with that object, because the data might still be out there on the internet ready to come over the wire. We may have none of that data available, but we'll still give you back a QTMovie object. Well, for some purposes that might not be ideal for you. Okay? So you might want to tell QTMovie, "No, I don't want you to open movies asynchronously. I don't want you to give me a QTMovie object until you've got the data." Well, there's no way to do that with the initWithURL function.
Another reason that you might want to use initWithAttributes is to set a delegate on the movie object. It turns out that you can call initWithURL or initWithFile. And then call the setDelegate method. But in some instances it might be too late. Stuff might have happened that calls a delegate method before we return. So in those two cases, to override default behaviors and to set a delegate, you'll want to use initWithAttributes. initWithAttributes is very simple. It takes a dictionary of attributes. And I apologize for this formatting here.
But you can see that I'm building a dictionary. Here I'm saying one of the attributes I want you to work with is the movie file name attribute. And I'm passing in the attribute attached to the -- or the file name attached to the document. Boy, this is really ugly.
Another thing I can do is in this dictionary set the movie editable attribute to yes so that I don't need to make that separate call after I've opened the movie to make it editable. Another thing I might want to do is open the movie muted so I could set the volume attribute to zero. Or if I want I can set the movie open async OK attribute to no. It's not OK to open it asynchronously, I want you to open it synchronously.
So let's enable that code by turning this one to a zero. And in fact we'll see no differences at all in the operation of the application because nothing important will have changed. So for those two instances you want to override some default behaviors of QTMovie or you need to assign a delegate early on in the process, you'll want to look at this initWithAttributes call. So I will leave it at that and can we go back to slides please? Back to slides, please.
I know I had a clicker around here somewhere. In the graphics and imaging keynote, you saw Michael Johnson from Pixar show some of the work that he's done with QTKit. And we are very fortunate to have him here in this session to talk to us a little bit more about his use of QTKit. So please help me welcome him to the stage.
Hi everybody. Can I get demo three, I guess? Goodness, I did nothing. Okay. So, let's see how much time we got. Okay, we got a fair amount of time. So, I have a bunch of different things that I thought would be interesting to people and I'm sort of up here, I guess, as the token outside person who's done something real with the kit so they're not lying person.
So, I have a couple of things that I wanted to talk about that I think will be very useful for people. One of them is threads. Threads are tricky in QuickTime, especially if you're a Cocoa person because it's usually very clear Cocoa-wise what's thread safe, what's not, what should be done in a main thread, what you can do in a background thread.
And in QuickTime, thread is very complicated because depending on what you're doing, some things are thread safe or not. And a lot of times you don't know what you're doing until you're in the middle of doing it. And what I mean by that is you could be opening up some arbitrary movie that has some codec that was written in 1992 by Avid, let's say, and you might find out that in the middle of opening up that movie that it's not actually thread safe.
So, you need to kind of think about what you're doing with that stuff and you need to put stuff in place for exception handling. And we've gone through this over the last year and a half with the QT folks and they've done a really good job of sort of making this stuff possible and I'm going to show a little example of that.
Which because I think one of the obvious things that Cocoa people will want to do when they walk out of here or they've already tried to do it and it didn't work so well or you're smarter than me and you already got it to work. Is take a bunch of still images, read them into a movie in a background thread with a progress bar with a little image that's thumbnail that's showing sort of the images as you're loading them up.
Because that's sort of an obvious thing if you saw the little thing I showed in Tim Schaaf's talk, you know, that's kind of obvious for me. I've got a bunch of storyboards, I need to bring them in, I need to assign some timing to them and I don't want to do that in my main thread because I might have, I don't know, I might have a bunch of 3,000 images. And I also want to have those in a sort of a tight auto-release pool because the images that I'm loading are undoubtedly film res and they're really big and if you're in a tight loop on that, it's a bad thing.
So let me show that example. And I'm not quite sure what the right way to get this code to people is but I think what I'll probably do is I'll leave it in the lab over there so if people want it they can get it there. But so--.
Let's see, do I have this running actually? Yeah, there we go. So this is a very simple app. I'll show you it working first to prove I haven't broken anything and then we'll go and look at what I'm doing. So I'm just going to swipe out a whole bunch of images here and the stuff that's on the bottom here is just a--it's just an accessory panel that I put on there so that I could change what the frame rate was of the movie and how long to hold them.
So I'm essentially holding everything for half a second here and I also put this handy thing in there to tell me how many things I selected. So we'll say open and we'll put up a progress bar and since I'm running just on my little power book here, it kind of took a to start but now it's going off and it's doing that and it's building them there in the background thread and I'm just sort of monitoring to tell you sort of how long I think it's going to be and then here we have the--we have the movie and it's in there, it's all fine.
Now one thing that sort of again in kind of the context of I get to be the full disclosure guy is one sort of thread through I think a bunch of the information that was being presented to you that may or may not be completely obvious is that the QT kit is as Tim, you know, said, this is sort of, you know, a really great--or at least I think it's really great, 1.0.
It's, you know, this is not covering the whole of QuickTime. This is a very targeted subset. So I'm going to go ahead and show you a little bit of this. So, you know, the QT kit is a very targeted subset to sort of get this thing out the door.
I think get it in people's hands, get feedback and, you know, get some momentum on this. So one thing that the QT kit is not sort of front and center right now is making new movies. And so what I did right there was actually made a new movie.
And so--because all the other things that they're talking about are like you're reading--you know, you're getting a movie here, you're cutting the movie, you're taking parts of a movie, you're taking three movies and making a movie out of those three movies. You're translating--you're taking--you're transcoding them. But you're not sort of saying, "Tabula rasa, I got nothing.
I want a movie and I want to start adding it to it." Because the obvious thing that you would think, which is that you would say QT movie, movie, and then you would start doing add image for time is not going to work. And the reason is very defensible.
It's not completely clear when you think about it, but it is very defensible, which is where is that data going, okay? As you're adding those images, where are they going? And you could say, "Oh, well, they could be in memory." And that's sort of not what you want to have happen, you know, right? You actually want to sort of say, "Oh, those should be on disk somewhere." And so what I did for my little app here was I put a category on QT movie.
And so this is something that's actually I think very necessary right now, but I want to sort of show you a particular way of doing it that I think is actually important. So I had a QT kit, let's see, additions down here. So I have, I added a little method called init to file.
So not init with file, but init to file. And then I implemented this with some, you know, carbony stuff to like make a movie and then hand that QuickTime movie into the init with QuickTime movie thing. But the important thing here, and this is really, really important because as opposed to say the app kit, which is, you know, the one I started using in '89, it's probably much older than that.
So it's a very old API, okay? This is a pretty new one. It's going to start growing. And so if you start adding, you start going category crazy, which is what I refer to certain people who go, "Categories, you're cool. I don't have to subclass at all. This is awesome." You are dangerous, my friend, because I was you.
And because what you'll do is you'll say, "Hey, boy, you know, these guys, they showed this, you know, set movie attribute colon, you know, and then you're going, you know, on the editable thing. You know, I'm just going to like make a shortcut. I'm going to put a category in it which is like set editable with a Boolean, okay?" Yeah, but what if like in the 1.1 release, they like make that be a method? You'll never know because you're always overriding it, right? How many people understood that? Oh, excellent. See, and I bet you were going to do exactly that.
Not wrong, am I? So what I tend to do is I tend to name things in obnoxious ways. I actually have a different naming convention than the self-centered one for this demo, but I would highly recommend that you do that because what you're going to do as soon as you start trying to do something with the QTKit is you're going to see all the things that aren't there.
Okay, and then you'll say, oh, actually I see they are there, but they're actually an attribute, and so I'm going to put this shortcut on there. Or, oh, you know, let me go, you know, and, you know, and we all know how to, do we all know how to like actually go and find all the methods that are in a framework? You have so much to learn, young ones.
And so, I mean, you know, that's a good example of something to go do and then realize why you shouldn't go and just name them exactly the same way that they would be named. So, because they might already be there. And so, that's a very important thing. So let me just talk a little bit about what I'm doing here in this and see how we're doing on time.
Okay. So I'm not going to spend too much more time on this because, again, like I said, I'll put the code out there. But some of the important things about this are, you know, sort of the basic way this works is that in the nib, I actually go and instantiate a controller which is an import images controller.
And then that's wired up to the document as an import images controller. And this is really just sort of a nicety that I tend to do in things of where I've got a bunch of different controllers to do it. I instantiate them inside my document. Nib because it lets me actually wire them back up to the document so that the--so that they know about the document and they know about the document window because the document window is the thing that they're going to hang the sheet off. I could actually just go ask the document and get the window from that but I'm sort of trying to be clear on what I'm doing here.
The--so the other sort of subtly about this for those of you who are somewhat newish to Cocoa and certainly the document architecture, is that this object which is instantiated in a nib and is wired up to a couple things has itself a nib that it then opens up once it starts up. And that's actually what has that accessory view for the open panel and what also has the progress bar that has the little image view and stuff like that on there.
And so this is--that object, that import controller is the owner of this nib file. So--and then he's all wired up. Now notice the--the--the one thing here that isn't--that isn't wired up in this is the document window for example. And also notice that since interface builder parses headers and not actually reading the file that actually that under our document isn't in there.
So, that's about all I want to talk about on this thing here. It's basically, well, one more thing on this. So, once we actually start up, when we start up the, so we do import images here, oh, this sucks. Yeah, you should just get the code. I can't make this big enough for this to be useful right now.
But basically what I'm doing is I'm just getting that information, getting the URLs of the movies you selected, starting up a background thread. And then the key is when you need to do things like updating that UI, you loft that into the main thread by basically saying perform selector on main thread. And you tell yourself to, hey, with this image, go update that little thumbnail view in there.
And so there's a whole category of things that, again, as a Cocoa programmer, you know that you don't touch the GUI from anything other than the main thread. But you can be being told to do that synchronously or asynchronously from a worker thread which has just gotten that image and is just adding it to the main thread. But adding it to the movie.
So that one I'll sort of make that code available. But, so I want to get through and get some other things here. So two other things which are, you know, were not true in the kit when this first started and are now very true, which is movie editing is faster than you think. And moving editing is slicker than you think. And so for that, I want to sort of go into IB here and show a little example. So this is just using our Film Online kit that has a couple of objects in it here.
And so So I just have a simple movie that's in here. It's just the movie that I was using to show off to in Tim's talk there. So this is, You know, this is the last 22 minutes of The Incredibles or so. But so, so here I am, and now I'm actually going to go and start chopping that movie up.
Okay, and this is just completely random, I'm just going in here and doing this. And so, if you think about what's going on there, that's, you know, where I can actually go and, you know, pick one of these guys up and, let's see if we go and put him in there, whatever.
That's actually like really expensive. I've got like, you know, a lot of movie there now. And, and I've cut those things together and I'm, and I can be jumping around in there. I can go full screen in our way of doing full screen. So there's all those things on there.
And it's just, you know, it just works, which is mind boggling to me. But you can go pick these things up, move them around, and you can do them, which is the amazing part to me, you know, while it's playing.
[Transcript missing]
The right way, at least the way I think of this, is I think of it as if I was opening up each one of these things in QTPlayer.
And if you've ever kind of opened up a folder of things and sort of swiped it out and opened up, you know, 20 movies or something in QTPlayer, it's fine. But then if you actually swiped out something that had 100 movies, think about what it has to do.
It's got to show at least the first frame, so it's got to actually get a full res image of that movie in memory somewhere. Then if it's smart, and this stuff tends to be pretty smart, it's probably going to kind of pre-roll that, so it might actually have like a couple of images of that movie around, because it assumes you're going to do a bunch of stuff with this.
So the tens one will work fine. The hundreds of ones, it could work, but you're going to have a lot more, you're going to have a much bigger memory footprint than you might think. And so the secret is actually very easy, and it's something that I stumbled on when I was actually working on, a thing for the Incredibles where we were trying to get out all the nominations for all the animators for the Annie Awards.
And I had been doing some stuff with this, some of the in-house stuff there, and I'd been opening up tens of shots and maybe hundreds of shots and these things there, but suddenly I had to open up the whole movie, which is 2,249 shots, and there was no way I could do it.
So, but then I realized if I opened up the movie once, and then each one of my elements was just indexing into that movie in a different place, using a different time range to snip the media that it wanted out of it, and actually then insert it into this other thing, it would work just fine.
And so when I do things like the demo I showed, and I'll talk some more about this on Friday, but when I would do these things where I basically have one QuickTime movie, and then I have this cut list of it, and the reason I can open up that so fast is because I'm opening up one QuickTime movie, and then basically just using time ranges to kind of cut in there and take those segments out of the movie and cut them into this other movie that I made, and all that stuff happens really, really fast. And if you're on a dual G5, that stuff tends to happen in under a second.
And, you know, for hundreds or even, in some cases, thousands of elements cutting those things in. So the big take home from me, I guess, on this is when you're starting off with this stuff, as soon as you start to scale up a bit, use Shark and use the memory allocation stuff in Sampler to be kind of watching yourself, if you're going to do stuff in background threads, if you can control the kind of codec chain, so in that case there, as those images come in, I use, like in Tiger, you know, Pixlet is thread safe, or JPEG, which has sort of been thread safe, I think, since it started.
You know, if you know what the codec is, and you know that it's thread safe, you can do it. Otherwise, make sure that you're very carefully wrapping exception handlers around things, and going back to the main thread to do the stuff in there if you can't do it otherwise.
That's probably all I have. Just to do the plug for, I'm going to give what I hope will be a fun talk on Friday about some of the in-house tools that we've done and some sort of coding practices that have helped us be able to make some interesting apps at Pixar, a lot of them using this stuff.
So I hope you guys use it, and I hope you understand that it's a, you know, it's a really good 1.0, and also, as you add categories, because you will, make sure you name them funny, so that when these fine folks put out new code, you actually get the new code, and you don't just sort of re-tromp over the stuff with your old code that was probably fine, but theirs is probably better. So, enjoy the show.
Okay, I think we're pretty much done. Let me remind you of the two labs that we have. One tonight, we'll be here until 9 or later if you want to be that late. And then we'll be back here in the morning at 9 in the morning, I guess, to spend more time with you all. So thanks for coming and enjoy QTKit.