Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2002-605
$eventId
ID of event: wwdc2002
$eventContentId
ID of session without event part: 605
$eventShortId
Shortened ID of event: wwdc02
$year
Year of session: 2002
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC02 • Session 605

Developing QuickTime Components

QuickTime • 1:05:29

This is a technical discussion of the foundations of writing QuickTime components. This session focuses on development tasks common to all components. Developers gain a basic knowledge of how to write and debug QuickTime components for multiple operating systems, and how to develop their own component interfaces. Topics include components versus other shared code mechanisms, anatomy of a basic component, the component dispatch helper, cross-platform considerations, developing your own component API, debugging techniques, and common pitfalls.

Speaker: Gary Woodcock

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.

Okay, good morning. I can see everybody can hear me. Thanks for coming to this session. This is on developing QuickTime components. I'm Gary Woodcock from Discrete. And what we're going to cover today are concepts that are common to writing all types of QuickTime components. So we're not actually going to focus on the details of any particular component, like an image compressor or a movie exporter, but we'll go over the basics of things that you'll have to do for any type of component that you write.

And hopefully what you'll take away from this session is how to leverage QuickTime to deploy your own technologies. For example, let's say you have come up with the world's best compression algorithm. We'd all love to see that in QuickTime, and there's mechanisms where you can make that technology available, and those mechanisms are components. QuickTime is the most flexible and open media architecture that's out there on any platform. We as developers have a really unprecedented ability to customize QuickTime to do new and different things.

Okay, you can also use components as an extensibility mechanism for your software. This would be a type of thing where if you didn't want to-- well, if you wanted to encapsulate functionality in your application in a modular way, components are a way that you can do that. So you could actually have-- use components as plug-ins, for example.

So you might have a spelling checker component or a grammar component for a word processing application. You could actually bundle them up this way. Okay, you should be familiar with C, the memory manager, the resource manager, and of course, the component manager. We'll go through a brief review of the component manager a little later today.

Okay, so we're going to cover what is a component, why should I write one, how do I write and debug a component, How do I create my own kind of component? And then how do I deliver it? How do I get it to a customer or an end user? Basically, a component is just another type of code library. We've probably all written static libraries, shared libraries of various types. A component is really, at its heart, just another type of code library. It can be used by multiple clients, so it has the concept of reusability.

It can be private or public, so there's a notion of scoping. And it can be versioned. and you might say to yourself, "Well, gee, that's not really all that unique," but components have a very interesting feature in that they can be searched by capability as opposed to a name or a symbol.

Basically, you can say, "I need this type of functionality," and there's various levels of granularity that you can request this, but you might say, "I need to get access to a JPEG codec or a decompressor." You can actually ask for that type of functionality without really caring who implements it, what its name is, what the symbol tables look like, and all the usual things that you have to deal with with typical DLL or shared library mechanisms.

You can also use components to directly extend the capabilities of QuickTime. In other words, we as developers, we can make QuickTime do things that it didn't do out of the box. And then, of course, you can deliver your components on Windows. You can deliver it on Mac OS 9, Mac OS X. And it's largely the same code in all those places.

Okay, so what else can they do? Components have a notion of being able to inherit functionality. Basically, you can capture other kinds of components. You can delegate functions to base components to help you do your work when you're writing a component. An example of this would be the base image decompressor, where Apple's made it very easy to write your own decompressor by handing off a lot of the maintenance and housekeeping functions to a base component that will do that for you. So you can concentrate on just writing the differentiating code for your image decompressor.

Components can also expose user interface. Probably some of the most popular or most prominent examples of that would be the movie controller is a component that exposes interface. The standard compression dialog is another component that has user interface. Okay, you can use -- your component can use other components to create a fairly complex chain of functionality. Think of something like the sequence grabber, which makes it fairly easy for an application to put video acquisition into their application.

It's a component that actually uses, you know, Half a dozen or more different components to get that functionality into your app. And you don't actually know about it, because you have a very simple API that you're using at the top level. But these things can be chained together to do some really amazing things.

Okay, so real quickly, let's compare components to other types of libraries. So this is just a quick table. You can see search by name. Generally speaking, everybody can do that. Components are unique in that they can search by capability, and we'll talk about this in a little bit more detail in a moment. Dynamically loaded? Sort of. Windows and OS X have this ability. OS 9 components do not.

You can use a variety of languages to write components. I actually haven't tried to write a component in Objective-C. If any of you ever try that, I'd love to hear the results of that. But pretty much any type of language you can use to write a component. And then, of course, as we mentioned earlier, they're supported on a wide variety of operating systems. Basically, anywhere QuickTime runs, you could potentially deliver your component.

Okay, some popular components. These are just things that developers often write. Image codecs, movie importers and exporters, graphics importers, exporters, video digitizers, media handlers. These are all examples of components that third-party developers have written to extend QuickTime. And there are many examples of this in the marketplace today.

At its heart, a component contains a dispatcher, which is basically just a mechanism to route client requests to a routine in your component. There are four required component routines. We'll go through each of these in detail. Every component should implement these. There are some optional component routines that are defined, your choice as to whether you want to implement them. And then each component type has a set of interfaces that are associated with the type. So, for example, a video digitizer component has probably over 100 different routines that are defined for that component type.

And then, whoops, I'm sorry, support resources. These are the component resource, might be the component resource map if your component needs to export public resources. Again, we'll talk about these in more detail a little later. But basically these things, pretty much any component will have this as a minimum.

Okay, real quick component manager review. So the component manager basically manages the components and the connections from clients to those components. and it's actually part of Mac OS and on Windows it's delivered with QuickTime. Components are always identified by a type, a subtype, and a manufacturer. These are 4CC codes.

Not too much you have to worry about here. There's a lot that are predefined by Apple. Lowercase types are, as usual, reserved for Apple, so as a third-party developer, you need to use either a mix of uppercase and lowercase or something along those lines, but stay away from the all lowercase things. Those are reserved.

Okay, you have to register a component before you can use it. There's a variety of mechanisms that you can do this, depending on whether you want to make your component widely available to all kinds of applications or whether you want to use it privately, and we'll talk about that in a little bit. We already mentioned that. Each component has its own unique API.

This is an example of how you would find a component. So in this case, we have a math component type. This is actually the sample component that will be in the sample code. There will be a URL later for where you can go download that. But what we're saying here is basically we want to get a math component. We don't really care what subtype or what manufacturer it is. Just go find the first one that you can locate and give me its ID. We could refine this search.

If, for example, there were, you know, the Super Food Company had a particular type of math component that had extremely good precision or something, we could actually substitute the 4cc for that manufacturer in the component manufacturer field to say, I really want to find that component. In other words, don't just give me any one. Give me that particular one.

Opening a component is really easy. You take the component ID and you call open a component, you get a component instance, which is your connection as a client. The only thing to point out here, there's an older call called open component that does not return a result code, so I would encourage everyone to use this form of the call because it will actually give you the result code.

You can query a component to see if a particular selector in its component type interface is actually supported before you use it or before you try to call it. The call component can do, and it takes your component instance along with the selector of interest, and then it returns basically zero if it's not supported, and then if it returns anything else, it's supported. So you can call this to find out before you try to call a routine whether it'll actually work or not.

version, basically every component has a version. It returns a long and the high word of the long is actually the interface revision. So if you are writing your own component API, you'll define normally a 1 for your component interface version. The lower two bytes are actually the implementation version. So as you do bug fixes or you bump the revision of the implementation, you would actually bump that number.

Okay, you can... Components, this is what I was referring to a little earlier about being able to do some limited form of inheritance. You can target components, and what this call is basically saying is when you would normally call your component internally to do a particular function, if you make this call to a component, this says, use the target instead. Don't call yourself. Call the target. This allows another component to be able to override another component's behavior by forcing that component to call out to it.

It's actually a really simple thing to support, as we'll see later. Okay, then closing a component is very simple. You just close the component instance, and that's it. There are some platform differences I'd like to point out. On the Mac OS 9 and earlier, there's a global registration list for components.

Basically, there's one big list of components that all processes and address spaces can see, and so you can actually see component instance counts for things that are not actually in your process. In OS X and Windows, this isn't true. You get a local list for each process. There are some implications to that we'll talk about in a moment.

Component reference constants. These are a 32-bit value that can be stored with the component ID. The idea here is that that value can be retrieved by any instance of that component. Okay, and we'll talk about how that's used. Modification seeds and instance counting, again, these on OS X and Windows only work on a per-process basis. They don't work across all processes.

So I might count two instances of a JPEG decompressor in one process on OS X, and in a different process there may be one instance. But I don't actually, they don't have any relationship to one another. other than their JPEG components. Okay, this sometimes trips people up. Carbon components don't actually run on OS 9. See, this one crop up every now and then. If you want to build a component for OS 9, you actually still need to link against interface slide.

And on OS X and Windows, the register and unregister routines are actually invoked each time a new QuickTime process starts. So if you have a component that implements these things, it will actually get called each time on OS X and Windows. On OS 9 earlier, it was really just when the component was first registered for the entire system. So there are, again, implications to that, and let's talk about those.

Okay, so you actually want to avoid using the component refcon to store common instance data across processes or address spaces. This doesn't work on OS X and Windows. What used to happen in OS X is people would say, I want to do some, like a lookup table setup, and I'd store it in a handle or a pointer off the refcon with the idea that any time that component was opened in any process, I could actually get that value. Okay, that doesn't work on OS X in Windows.

Okay, again, one of the older usages of register and unregister was sort of a one-time setup and teardown type of routine. As we noted earlier, on OS X and Windows, these two routines actually get called each time that your QuickTime process begins. So if you really have a one-time thing you need to do, this doesn't work. Also on OS X, you may actually incur additional application launch overhead or boot overhead. For the process, because you actually are running these things multiple times instead of just once. So you want to avoid doing that sort of thing in register and unregister on OS X.

Okay, and then as we noted earlier, you really, if you want to limit connections to your component, counting instances is probably not the best way to do that. If you had some hardware resource, for example, that you really could only have one connection to that, and you were using a component to wrap the interface, because you get a different copy of that component, or a different registration of it per process, you can't actually limit the connections to your hardware resource this way.

Okay, development tools. In the sample code that's posted, what I did is I actually used Project Builder for OS X, Code Warrior for the OS 9 and earlier, and Visual Studio.net for the Windows code. There are other environments you can use. For example, you can use Code Warrior to build Windows components. These are just sort of the prominent environments for the various operating systems.

Okay, so you're probably saying, okay, can we really start now? What are we doing? Okay, what we're going to do is we're going to try to define a component that can actually add two numbers and give you the result. Something that would take you one line of C code, we're going to stretch out into a bunch of lines of C code.

But it'll be illustrative of the process of what you have to do to write an interface and write a component. We'll support all the required routines and most of the optional routines, and then we're going to see how calls in the client program translate to routines in the component.

Okay, so here's our API. And it's... It looks a little strange, but there's some similarities to what you would do if you were defining an API for any other kind of code library. In the highlighted portion, we have basically a return code, which is the component result. We have the name of the function, which is math add. And then we have an argument list. And the first argument in any component call is going to be the component instance.

This is your connection to the component. And then the rest of the arguments are the interesting arguments for the call. In this case, we have, you know, the first number, the second number, and then we return a result of the addition. The rest of what's there is... You'll see that the first line, the kmath add select, is actually a selector. That's a component selector. Component selectors for things that... in interfaces you write or interfaces that you use.

Most cases will be positive. The base selectors, which are the open, close version, and can do, and some of the optional ones, these are all negative selectors. So negative selectors are reserved for use by Apple. So none of us actually will get to define any of those. You'll also see if you go look in any of the, like quicktimecomponents.h, for example, you'll see selector numbers for the various component types that are in there. Thank you.

The rest of this is basically some macros. Xtern API is basically a helpful macro that keeps you from having to write things like Xtern Pascal underscore underscore, DeclSpec, DLL import, whatever it might be. If you use this, it will actually, when you compile, insert the right stuff for you.

So it's pretty handy. The component call now is a macro that really is associating the amount of space needed by your argument list for the component manager. So that's why we see the size of calls here. Basically what we're saying is we need eight bytes of space for our argument list.

Okay, there's also some constants that we need. So we mentioned our type, subtype, and manufacturer earlier. So for the sake of this demonstration, we're going to use math and WWDC and Apple. We need a resource ID for our component, and then we have our interface version and our implementation version. So they're both one because we're creating something new.

Okay, every component needs instance storage, or every component instance needs some storage. What's listed here is basically the minimal stuff that almost every component will need. The self is the reference that the component manager assigned when open component was called. So we actually get that in our component, and we can actually use that to call other routines in our interface from within our component.

Target, if you recall earlier our discussion of that, this is when our component is targeted externally, this is the instance that is passed in and that we store in our component. This will allow us to where we would call self or use self to call one of our internal routines or call one of our routines internally, excuse me, we would instead use target if we had been targeted.

Okay, the dispatcher. And this causes a lot of issues. It basically is just responsible for mapping our selectors to the routines that we've implemented. You can write it yourself, and you can go through the pain of dealing with mixed-mode logic and routine descriptors and all sorts of other gnarly stuff, or you can use the component dispatch helper.c file, which I strongly recommend everyone use, and it will handle all of that stuff for you.

It's not very hard to use. We'll go through it in detail here. Again, do use that. If you really want to see what it does, you can preprocess the file, your component file, and dump out the C code and look at what the dispatcher does for you. And then maybe that might convince you that you don't actually want to hand code all that stuff.

Okay, so the dispatch helper is part of the QuickTime SDK. And what we're going to do is we're going to write a dispatch header file and go through all the defines that you actually need to create to make it generate the right thing for you. Okay, so we have a dispatch header file, and the first part of it looks like this. We'll go through each line here. So the first line, we have a component selector offset.

This is basically the number of base selectors that we're specifying in our component. This number I put 10 here, that's actually the total number of base selectors that are defined by the component manager today. It's a contiguous number of base selectors. It's not the total possible number. If you put 10, in your dispatch file, you'll need to have 10 entries.

We'll go through that in a moment. Then we have the number of selector ranges that we're going to define. A selector range is really, every component has a range of selectors for the component manager calls, open, close, can-do, version. And then presumably for it to be useful, there's at least one range for the component type. So we have one range. We're going to use our math component interface that we're defining.

Again, it's a contiguous range of selectors, and there are two other defines that help the component dispatch helper understand how to interpret some of the range information. The first is a range shift. A lot of folks get hung up on this one. It's basically the amount that a selector value is shifted left and then incremented to produce its range number.

I have an example listed here. If we had a selector value, if our math add select value was, say, 0101, if we shifted left by 8, we'd get 1, and then we would actually say we'd add 1 to that, and it would actually be in range 2. In our particular case, when we define our selector as 0, we shift that left, we get 0, we add 1, it's in component range 1.

So that's all it is. It's just a shift in the increment to get the range that your selectors are in. Okay, so that tells us how to find the range. We also need to be able to find what entry we're talking about within a range, and the range mask helps us do that.

So we can actually use this value to mask against the selector to get the particular entry in a given component range. So if we use the 01-01 example again, that would actually map to routine entry 1. Okay, so we'd mask off that first 01. We'd be left with 01. That's our entry.

Okay, so that's really all there is to that. We have a storage type that we need to define. If you recall back, we talked about each component instance needs storage. That can be a pointer. It can be a handle. This is just declaring what kind of a thing that it is. Generally speaking, unless you've just got a really huge set of component information, you need to keep track of it on an instance basis. Most of the time I just use pointer for this.

Okay, so here's our, this was our range zero, if you will. These are our base selectors that the component manager defines. So what we see here is we're going to support basically the first six selectors, open, close, can do, version, register, target, did I say six? It's actually seven, and unregister. For those components or those selectors, we're not going to write a routine for, we'll define component error for. It just tells the dispatcher that return an error code for that if anybody tries to call it because we don't actually implement it.

Okay, and again, these selectors, the order is actually significant here. So this actually, get public resource has a value of minus 10 for its selector value. Open has a minus 1, so it's significant, the order of declaration here. And again, negative selectors are reserved by Apple. Okay, so here's our range. We have one selector, and it's math add. And it's just that simple. We have a component call that says, again, it's just another macro. And then we have a component call that says, again, it's just another macro.

There are some other ones you can use here. We aren't going to have it in our example, but other things that you can have are component delegate. If you have a component that your component opens that you want to pass calls off to, you can actually tell the dispatcher to call that component instead of calling your component for a particular selector.

Okay, there's a bunch of defines that the component dispatch helper actually needs to do its job. And we'll go through these. So this define is really just the prefix of our component-- the names of our component routines. In this case, I've used __math. So if you looked in-- when you look in the sample code, you'll actually see a routine in the body of the component that's called __mathopen.

So all that's happening here is the dispatch helper's using this to catenate to the base selector names and our API selector names. This is just telling the dispatcher what kind of storage we have. Our storage type is actually MathGlobal's pointer. We define this value so that the dispatcher knows that's the kind of data type we use for our storage.

And then we have a prefix for any of the universal proc pointer information that we need, which in this case is UPP math. If you look in some, again, in some of the QuickTime component headers, you'll see the same type of stuff for all the components basically have a UPP, VD, you know, get max source rack, for example, for a video digitizer. So this is just the prefix that's used.

And then we tell it where to actually find the dispatch file. This is basically the stuff that says, "Here's the component ranges. Here are the entries that we're actually supporting in our component." And then we have an API-unique set of defines for our math interface, the base name and the globals. In most cases, it's safe to just use the same stuff that you defined for the component base and the component globals. There's really not much need to make those different.

Okay, so finally we get to the point where we can start to include all of this into our .c file, and this is the order in which we would actually do the includes, and it too is significant. You want to pull things in. The base components select this k.h file, our math selectors, k.h, and then the dispatch helper. So all the stuff that the dispatch helper actually needs to generate the dispatcher has to be included first. You're probably asking, what is this k.h file? file.

And these are some of the utility macros that are actually needed by the helper. And rather than go through this in exhaustive detail, and I have a hard time remembering this myself from instance to instance of work that I do with components, you could actually just treat this more or less as a template.

And if you need to write your own interface, you can take this and basically change math to whatever your needs are. And it's basically stuff that here's how you catenate things, here are the storage types, the prefixes, things of that nature. It just helps the component manager understand how it can put together all these defines and build out the dispatcher names.

Okay, and then we also have another expression of our prototype. It looks similar to what we defined in the selectors.h, but it's expressed with some of the defines that we use, like the math globals, add math base name, which is that underscore, underscore math thing we talked about. So it's really just the same expression of the API in a slightly different way.

Okay, and then we have this UPP proc info constant. And actually, if you look at this in the QuickTime headers, you'll actually see these usually expressed as some hex number. And basically what you see here is a bunch of macros and defines that will actually generate a similar hex number if you actually preprocess this. You'd get some 32-bit long hex number. Again, it's about telling the component manager how much space that you need for your return values and for your arguments in your routine. And that's really what this is about.

Okay, let's look at how each of the routines are actually implemented. So this is the open routine. Again, these are intended to show the minimum things that a component would need to do. You open a component, and the first thing that you actually need to do is allocate some storage for your component instance state. That's the new pointer call there. Then we actually tell the component manager to associate that storage with our instance. That's what the set component instance storage is about.

And then you see a line there that is global self equals global target. This is just so that anywhere where we might call one of our API routines internally, we'll just always use target. So we don't have to actually keep track of whether it's self or target. We'll just always use that, and we'll initialize it to be self, so that we're always calling internally to start with.

Okay, and the close routine is pretty simple. The biggest thing to remember here is just be sure to clean up everything that you're using. So for this particular example, it's very simple. We just need to dispose of our instance storage. But if you had other types of data that you had in handles or pointers, you need to clean that up here. This might be a good place to close down connections to hardware if you needed to do that. Basically, put the system back to where it was, where you found it when you actually opened, as much as possible.

Okay, the version routine, again, very simple. It's just return your version information. So those are those constants that we defined as our implementation is one and our interface version is one. So that's a very simple routine to implement. Okay, then we have the can-do routine. I actually put this slide together before I knew there was actually Jamba Juice here. But in any event, you don't actually have to write this if you use the component dispatch helper. It will write this routine for you. So you get it for free if you use the component dispatch helper.

Okay, register routine. Now, this routine is optional. You don't have to implement it. It can be used to check to see what kind of environment you're dealing in. In this example, what I did was say, all right, let's go make sure that a particular version of QuickTime is actually present before I allow this component to be registered for use. So what happens here is if QuickTime 4.1.2 isn't, or later, isn't installed, we actually won't register this component, and it won't be available for use.

So things you might do here are if you required a particular operating system service or a particular feature of QuickTime, that sort of thing, you might check for that here, and then not allow the component to be used. If you do use this routine, you do need to set the component wants registered message flag in your component resource, and we'll look at that a little later.

Okay, the target routine. Again, this is fairly simple. If you recall earlier in the open, we set the target field to be pointing to self. And here what happens is we simply update that value with what gets passed into this routine. So we update target to be target, and then we continue to use it the way we were using it all along. And then if it gets set to null, we'll just reset back to target. himself.

Okay, unregister. Again, this is another optional routine. You don't have to support it. I actually couldn't think of a really good example here. It's not used all that often. But if there was some particular cleanup that you needed to do in general for your component that you couldn't do in Close, this is kind of your last opportunity to do that cleanup. And if you want to use this routine, again, there's another flag. There's a flag in the resource, in your component resource called component wants unregister.

And it's actually in the component register flags field. And you have to set that flag or this routine won't be The component manager has this notion of public resources. So components can have public and private resources. And private resources are really those things that you want to use in your component, but you don't really want anybody else to use. Public resources are those things that your component may use internally, but you would actually want, or you would want to provide the ability for other clients of your component to use.

You do this by means of a THNR resource, a component resource map, and it associates your private component type and ID with a public type and ID. Places where you see this in QuickTime today are, for example, for MIME types. You might see this kind of a mapping. A client gets access to these resources by calling getComponentPublicResource.

And you don't actually have to implement get public resource in your component for this to work. You only really need to have the component map resource. If you have resources that are actually dynamically created on the fly, like you can't actually compile them into a .r file, you may actually need to implement this routine in your component. But if it's just sort of a straight mapping from static resources, you don't actually have to implement this routine.

Okay, finally, after all that, we're to the point where we can add two numbers. So, this is our math add routine. Pretty simple. We just check to make sure we got good values, we add the numbers, we return it back out. We have a component now, and you think, can I actually start to use it from a client? Well, not quite yet.

We actually need to write some glue to allow clients to call your component. The glue basically facilitates the handoff from the client to the component manager and then to the component. It is possible to hard link this into an application. This usually ends up as a shared library, for example, on Mac OS 9 or a framework on OS X. You can, again, make this private if you want to so that only your client can see it.

We're going to create a stub library that our component and any of our clients can actually link against. And there's gonna be some glue helper routines that are defined here. And I'm gonna go through this kind of quickly, 'cause it's fairly, uh, gnarly. But again, you can use a lot of this.

A lot of this is kind of boilerplate, and a lot of it is really-- the stuff that isn't boilerplate is really wrapped around, again, communicating how much space your component needs for its result codes and argument list. So you can think of this as kind of another way to express that. So this is the helper for a call mac component, and you'll notice there's, again, the information here about the result size and the stack routine information.

on Windows it's a little messier. You have to actually manually fill out a component parameter structure, and that requires you to do a little finagling with how many parameters you actually have so that you can fill out that structure correctly. In this case, for our Math.Add routine we actually have three parameters. So you can see that we have--there's a params array at the bottom if you can see that. So we have three of those. If this only had one parameter, we'd actually have a structure that only had the param zero.

Fortunately, most of the time you're not writing your own API, so you don't have to go through all this. For any of the existing component types, this is all handled for you already. Okay, and then there's yet another expression of our API and how to, what to do when this API is encountered. So this is, again, calling through our helper routines. And again, it's very gnarly, but the thing to bear in mind is we're basically building up this component parameter structure so the component manager gets the data in the form it needs before it passes the data line.

And that's what all that is about. All of that stuff is in the sample code, so there is a working example for Windows, Mac OS X, Mac OS 9, it's all there, so you can see frameworks and shared libraries, figure out how to do this glue. It is all there for you.

On Windows, there's another thing that you need to do before you can get rolling with this. A component on Windows is basically a DLL, so you need a main entry point. This doesn't actually do anything. It needs to be there to define how we get into the component, but as you can see, it just returns true. It doesn't actually do anything on the switch statement. You also need to export the name of your component dispatcher. That's usually done with a module definition file. Again, this is Windows-only stuff.

Okay, let's take a quick look at some of the component resource stuff. People get kind of hung up on this one, too, and usually it is either an issue with flags or in the way that you're defining your code type or your platform type. So the things I actually have highlighted here, you'll see why in just a moment. We have our component type, component subtype, component manufacturer. We have some flags information.

The STRN that you see there is just a component name resource. We'll show that in a minute. Then there's an info resource. We don't have an icon. That's why after the STRI stuff, there's two zeros that would normally be for icons. We have a variety of flags here. We actually support unregister, so you'll notice that's actually set. The component has multiple platforms flag is a little bit misleading because in this particular example with PowerPC, we only actually have one platform we're building, but you still need that flag set.

It basically lets you see the component extension and define that. So in most cases, you'll have that set. We have the component wants register message set because we actually implemented that. Underscore PPC is just our code resource type on PowerPC, and then we indicate our platform there as well. well. You'll notice at the very bottom we say we have a component resource map. That's the THNR. So what's different between this and a Carbon CFM? Very little. We now have a code fragment resource that is referring to our code, and we changed the platform type.

Okay, how about MachO? Again, very little different. We have a DLLE entry for our code resource, and we still have a PowerPC native entry point. Lastly, Windows. Very similar. We have the DLLE, but we have a platform Win32. So all the rest of that stuff stayed the same for the component resource. But those are the things that make it, that people get a little hung up on, so I wanted to point out that, you know, largely these things can stay very similar across platforms.

Okay, the code fragment resource, if you're building that type of a component, a couple things I wanted to point out here. It's a standard code fragment resource, but you'll notice in the highlight portion we have the CPNT, which indicates it's a component, and then there's this weird hex value there. That actually turns out to simply be our component resource ID, so it's 128, just expressed as a hex number.

Okay, for our Windows and our Mac OS stuff, we have a DLL e-resource, and this is where we define the dispatcher name. Pay careful attention to this, because this is often something that trips people up when they try to register their components for the first time and use them. If there are differences, for example, on the Windows side between the name that you exported and what you have here, it won't work. You need to make sure that those names are actually the same, that they line up.

Okay, the string name and string info resources are pretty self-explanatory. So when somebody calls get component info on your component, this is the name and the info handles that will be returned back to the client. You can pretty much put whatever you want here. Try to put something explanatory of what your component does.

Okay, here's our component resource map. And all we really did here, just again for the purposes of an example, is we have our string name resource, and I just mapped it out to be a public resource of type STR with ID 1. So if you actually called get public resource with type STR and ID 1, you would actually get our component string name resource. One thing to notice that I forgot to mention earlier about public resources, they're called resources, they're not actually resource manager handles. They're handles. So you don't want to call release resource on them, you want to call dispose handle when you get rid of them.

Okay, on Windows we have these interesting tools called res and reswac that you need to run to get your resource information attached to your QuickTime component. And res is, if any of you are familiar with the old MPW tools for doing resource compilation, this is very similar, except it's a DOS console app that compiles QuickTime .r files into .qtr files. Okay, so it's just sort of a Windows-compatible form of the resource for it.

And then there's another utility called ResWack that you use to actually combine the QTR file into your Windows component DLL. Okay, and ordinarily these things you can set up as just a post-build batch execution. Once you're done compiling on Windows, you can just run a batch file that takes care of this for you. An example of this is in the sample code.

Okay, registering components. There's three ways we can do this. You can do auto-registration, which basically means I'm just going to let the system take care of this for me, register my component for me. So you leave it up to basically the component manager to decide when to register your component. Typically that will be when the QuickTime process on OS X starts up.

Your component will get picked up by the component manager, and then you can register your component. You can also get a component picked up in the local registration for that process. There's also an application called Reinstaller 3 that's useful if any of you are doing Mac OS 9 or earlier type of testing or development. This actually lets you register a component without actually having to reboot, which is the normal way that you would get a component in on OS 9. And the last way is a programmatic registration.

So the auto registration basically you just have to put the components in the right place. On OS 9, that's in the QuickTime extensions folder, in extensions, inside system folder, and then you reboot. On OS X, you put this into library QuickTime or user's username library QuickTime. You don't have to reboot.

On Windows, you put this in your System32 QuickTime directory. On older versions of Windows, prior to, say, Windows 2K, it might actually be System QuickTime, for example, on Windows 98 or something like that. And we did, we just talked about Reinstaller 3. This is the URL where you can actually get this utility if you want to download it. You can also get to this just off the main QuickTime developer pages.

Okay, programmatic registration. So register component basically, basically If you have the description of the component and its main entry point, you can use this routine to register the component local to your application. The other way you can do it is if you've got the component resource, you can call register component resource, and this will do a similar thing. So you're probably wondering, though, why this would be interesting.

So the first thing is you actually don't want other clients to see your component or use it. Why would you ever want to do that? Well, if you were in the Wednesday sessions, Tom Dowdy actually mentioned something about how you could use, if you had a proprietary file format, for example, in your application, and maybe you used a lot of the other file formats that QuickTime already supports, you could actually have a more consistent user experience if you implemented your file format as an exporter, and then you could leverage all the QuickTime UI, make it a very seamless experience, no matter whether people were exporting to your file format or to other formats that QuickTime supported.

It would all look very much the same. So you get some leverage out of the UI by doing that. You may also want to just... It's an interesting way to do some source-level debugging. You can just link it in directly with your app and debug through it that way.

Okay, debugging components. Source level debugging is available in all major development environments. This didn't actually used to be the case some years ago. It was kind of a pain to actually debug components. But in Project Builder, in Visual Studio.net, in Code Warrior, all these environments, you can actually do source level debugging.

Okay, if you're still doing some 9x work, you have the thing, dcommand, for a max bug, and this will actually show you a full printout of all the components that are in your system, how many instances are open, what their refcon values are, all sorts of interesting information. So it's pretty helpful on that platform.

And there's some stuff you can do on Windows. There's, you know, first of all, one of the things you may want to do is make sure that your QuickTime resources actually got into your component DLL, and you can use the Visual Studio editor if you want to just open up and look and see if it got attached, or you can use a tool called ResDet to see whether the component was attached properly.

Okay, as usual, debug string, output debug string are your friends. You want to use them liberally. and you can find out whether your routines are actually being called, in what order, that sort of thing. So they all work great. One quick thing I want to talk about is, okay, now I've got a component, how do I get it to an end user? You can ship it yourself.

So if you have, as we were talking earlier, you've got an application, and in the case certainly where if you don't want other people to use it, you just ship it with your app, or even if you do want other people to use it, you can ship it that way.

Apple actually provides another mechanism, which is the QuickTime Component Download Program. This is very interesting if you're doing an exporter, an importer, a codec. There are a number of third parties today that have codecs that you can get in exporters through this program. So if you're doing that type of component, I'd encourage you to contact Apple about that.

Okay. The rest of the time here I really want to spend going through common problems that people have developing components. Okay, so this is common. My component compiles and links, never shows up, it's never registered, my clients can't see it. Okay, so the first obvious thing to do here is you want to make sure that your register routine isn't failing. In other words, that it's not returning that it can't register. That's an obvious thing to check. If you're auto-registering, make sure you put the component in the right directory.

This is something we talked about a little earlier. You want to make sure that your DLLE resource on Mac OS X and Windows is defined, and that it's actually lining up, that you have the same names in both places. And then for Carbon, you want to make sure that you have a code fragment resource.

So if these are messed up at all, chances are really good your component is not going to do anything for you. Okay, and then this was the earlier statement about making sure you don't have a mismatch in spelling or whatever between your dispatcher name and the name you're actually exporting.

okay, my custom components routine has wacky values in its arguments and it sometimes crashes, locks up, does other nonsocial or unsociable behavior. If you're doing your own API, you want to make sure that you've actually declared all that stuff we talked about a little earlier about making sure we had the return values and the stack arguments -- I mean, the routine arguments all defined correctly in terms of sizes. If you do that wrong, this can be the kind of behavior that you see. So it might be the kind of a thing where you said you needed two bytes for an argument and you actually need four. So you can get a stack misalignment.

Okay, my component's register routine never gets called. So the component looks like it's coming up, and I can see it, but the register routine never gets called. You want to make sure that that component wants register message flag is set in the component entry, platform entry of your component resource.

And there's actually another place where this can be set, and it's in the 68K flags field at the beginning of the component resource. I Don't think there's too many people doing 68k component development. So for all intents and purposes, that's an obsolete field. So you need to make sure that you not only define it, you need to put it in the right place.

Okay, and then if you're actually doing the thing we described earlier where you're calling register component or register component resource, you'll actually need to manually call your register routine. So if you're doing this from within your application code to register your component, it won't actually call register for you. You need to do that yourself. There is a call in the component manager in components.h, call component register, that you can use to do this.

Okay, my components routine never gets called. Not just register, but nothing gets called. This is usually, you want to make sure that you've accounted for and lined up your selectors in your dispatch file. So, This can be an error in how you've defined your range shift mask or your range mask.

It could be an error in how you've defined the values for your selectors. So you need to just basically go through that and make sure what they should be. Go through that with a fine-tooth comb, and that usually solves a lot of these problems. Topics include components versus other shared code mechanisms, anatomy of a basic component, the component dispatch helper, cross-platform considerations, debugging techniques, and common pitfalls. Topics include components versus other shared code mechanisms, anatomy of a basic component, the component dispatch helper, cross-platform considerations, debugging techniques, and common pitfalls.

okay, my component opens another one of my components and needs to share internal state with it. Okay, if you recall earlier, we were talking about how you can't actually use component ref cons anymore to do this kind of thing reliably, which is a way that some components in the past on OS 9 used to do this. What you can do is, with the material that we've gone over today, we showed you how to write a new component type. That same mechanism can be used to extend an existing component interface.

Okay, so let's say I was writing an image decompressor, and it was actually a hardware resource that was running on a board that maybe also does video acquisition. I'm probably creating an unusual example here. So you have two components that are trying to share the same hardware resource.

You'd actually like them to be able to find out whether one or the other of them has control of the hardware. You could potentially do that by extending either the image decompressor API with a private selector or the VDig API with a private selector that can allow those two components to communicate with each other. And you would use the same techniques that you use for defining a new component type API.

Okay, so speaking of hardware resources, this one comes up a lot too. I need to connect to a hardware resource for my component, and the hardware only supports a single connection to the physical device. And to make matters worse, I could maybe have multiple physical devices present. How should my component handle this? So I want a component to be the wrapper around this hardware resource.

That's a good one. This is a difficult problem to solve. And some approaches, you could register your component once for each physical device that's in the system. And you could say, okay, for each of those components, I'm only going to allow one instance. Okay, there's some drawbacks to that. First of all, it assumes that you can actually uniquely and persistently identify these devices that are in the system. Maybe that's not always the case. And that it's possible that you can track device connection status across address spaces.

Okay, another approach you might use is, I register my component once, allow multiple instances of the component up to the number of devices that I actually have installed in the system. There are some of the same qualifications here, and it makes it a little more difficult to count the devices that are in the system, because from the component manager level, what I would have to do is actually keep opening the component to figure out how many of these devices were there.

I could register the component once, and then I just don't have any limitations at all. In other words, I implement some type of elaborate mechanism to share a single hardware connection amongst all these instances. The obvious thing that happens here is the more instances they get open, my performance very likely goes down because I'm having to manage between these things.

There's not really a good answer here, and a lot of this you actually want to push down to the driver level for your hardware device so it can actually tell your component information. The general thing here is you probably want to not try to do all of this sort of stuff in your component because it's very difficult. You actually want to try to push it to a lower level.

Now that said, if any of you were in the video-intensive application session, Sean Williams, he showed some stuff with the IIDC cameras. Basically what was cool there is there's a class of devices for which Apple's providing support where you actually can have these devices with unique IDs on your FireWire bus, and it can persist state between hot plug and unplug. There's a lot of progress being made in this area to help people with this problem with components.

So I don't know if there's any other questions. I would encourage you to, if you didn't get to see that session, then at least when the DVDs of the conference come out, you may want to go look at that session. There was some very interesting stuff talked about there.

Okay, so as we noted, there's not really a magic bullet on this one. You'll have to experiment to see what actually works best for you, but again, you probably want to push a lot of this connection management stuff out of the component down to a lower level in your software.

Okay, just some closing advice to summarize some of the things from the session. You don't want to allow multiple instances of your component if you can't actually support it. You need to make sure you clean up properly when your component's closed. I encourage you to use Gestalt to find out if the services you need are actually there before your component is registered, before you allow clients to use it.

Use the component dispatch helper. Having done this for many years for both ways, this is hugely useful. And like I say, if you need to convince yourself, take the sample code that's available and preprocess the C file and look at what the dispatcher does for you. It does a lot of work for you.

Don't forget that the component manager has platform differences if you're actually trying to develop for systems beyond Mac OS X. Okay, before you actually go through the exercise of writing your own API, we saw that it's fairly involved to do, you should probably go through the QuickTime APIs first. It has a lot of different APIs, and perhaps you can get by with, if you can get by with using them, the ones that are there, that's great, or maybe you can actually use one that's close and extend it to get what you need.

Okay, there's a QuickTime API mailing list. I encourage everyone to subscribe to that if you're doing component work. There's a lot of developers there that have a lot of helpful hints and tips. And this is the roadmap for today. And one thing I want to get to is, let's see, so we have MPEG-4 today.

The sample code for this session is at that URL. There's actually both Mac and Windows files there for you to download. It illustrates everything we talked about in the session today. So you can go get that and try it out. Please feel free to contact me at garywoodcock.com. After this session, we can talk outside, or you can also email me. That's our session for today, and I'd like to thank everybody very much for coming.