Some notes on the QuickTime part

There are some design details that might seem strange at the first glance. But many of them are intended - they are based on some funny and some not-so-funny experiences working with OSX and QuickTime. So here are some explanations:

Version 0.3 had support for one cam at a time and one component: a vdig (Video Digitizer) - automatically registered. Communication to the driver went through a bridge object (of class "MyBridge"). The world was fine. Just a bit strange if there was no camera.

Version 0.4 introduced a second component: a sgpn (Sequence Grabber Panel), subtype vide (Video channel) to enable specification of non-standard parameters. And some multicam support was added: The vdig had multiple inputs. This was also kind of fine but simultaneous use of multiple cams was not possible (since it is expected that a vdig can only use one input at a time). When there was no camera, a dummy camera supplied an error message. Still, all the stuff went through the bridge. Historically understandable, the vdig was the "real owner" of the bridge. The sgpn just borrowed it.

Version 0.5 differs quite a bit. I wanted to have one vdig per camera (as recommended by the QuickTime docs) - this would solve two things: First, the stupid "no camera" message (we let the application decide what to do when there's no input instead). And second, we allow to have multiple cameras used at the same time since the vdigs are independent. So we cannot have static, automatic registration any more. The usual way to do this in a dynamic manner is to set "cmpWantsRegisterMessage" in the component's 'thng' resource. We'll then get a register message where we may decide if we want to be registered or not. First thing to mention here is that originally, this was done on system startup and all apps accessed the same component. Since we have protected memory, sharing components between application does not work this simple any more. And so the components are now registered on every application launch. Even better for us since the number of attached cameras is detected at that time. But here is where the odds start: We cannot say "no" in the registration function! Question: Why? Answer: Because we use Cocoa!

A separated note on this because it's really weird: Our component is in fact a bundle (just with a different extension). Bundles' executable code can be loaded and unloaded. The sequence is as follows: load bundle executable code, (open component - unimportant here), ask component about registration, (close component - unimportant here). If the component agreed, register and fine. If not, unload the code. This is the problem! There are (at least) two bundle types: CFBundle and NSBundle. They are almost identical (and can usually be typecast thanks to Toll-Free Bridging). But they are not exactly identical: Bundles that use Java or Objective-C have to load their executable code with NSBundle's [load], CFBundles' equivalent does not work (see the note deeeeeep inside CFBundle.h). If we ignore this fact, the code cannot be unloaded, causing the current application to immediately exit with a Console log. This is quite ineffective because it was just starting up. And even worse, it seems that all apps will do this. So will the Finder. If you have such a component installed and reboot,  the machine won't start up any more because it ends up in a Finder start-register-fail-exit-restart loop. Funny but annoying. So don't try this at home (unless you have a startup CD handy).

So we need a different approach. One that's dynamic and doesn't refuse registration. The strategy is as follows: The sgpn can safely be registered. So we mark sgpn with "cmpWantsRegisterMessage". The sgpn's registration implementation will instantiate the central, start it up, count the cameras and register one vdig for each (via RegisterComponent). The components will receive their needed camera info in their refCon. The sgpn always agrees to register. This has a nice side effect: Since both components are inside the same bundle and bundles have one executable code block at max, we can safely assume that the vdig code will be loaded all the time because the sgpn is also loaded and registered. And we have the code only loaded once - it is shared among the sgpn and all vdig components. This means that we have two levels of global variables: "static" vars are global among all components. The component refCon and the instanceStorage could be used to distinguish between component-global and component-instance-specific, but we don't distinguish that since we only allow one connection per component. As said before, The refCon is used for camera info passing and instanceStorage is used for instance storage (what else?).

This also implies a change of the bridge's role: Before 0.5, it was meant as a Cocoa client of the driver that could be easily accessed from the QuickTime API. It was already needed to catch the camera driver notifications. This is still the case. But before 0.5, the bridge was a singleton. It owned the camera central and the current camera driver. This is different now. Each registered component has an own bridge. It has the duty to provide the camera central and driver to the component functions. The bridge objects will be created by the sgpn register function - just with central and cid, no camera driver yet - and passed to the component in its refCon. The cid's camera driver will be allocated when the vdig component is opened. This has two reasons: First, we want to keep the sgpn registration function fast (because it will delay almost every program's startup). Second, the usb device should be only locked by an exclusive access if we really use the matching video digitizer (to be multi-app friendly).

$Id: QT_architecture.txt,v 1.3 2007/02/18 06:06:43 hxr Exp $
