ADC Home > Reference Library > Technical Q&As > QuickTime > QuickTime Component Creation >

Video Output Components - Implementing QTVideoOutputGetIndSoundOutput on Mac OS X


Q: How do I implement QTVideoOutputGetIndSoundOutput on Mac OS X? I have a Core Audio driver not a Sound Output Component. Where do I get the Sound Output Component from?

A: The Carbon Sound Manager will register Sound Output Components with the subType kHALCustomComponentSubType for each Core Audio device driver on Mac OS X. To find the Sound Output Component corresponding to your Core Audio device follow these steps:

  1. Find your driver's Unique ID (UID) using Core Audio.
  2. Initialize the Sound Manager - this can be done using an inexpensive call such as SndManagerStatus.
  3. Iterate over the Sound Output Components using FindNextComponent, and for each Sound Output Component found call SoundComponentGetInfo using the siHALAudioDeviceUniqueID selector. This will also return a UID.
  4. Match your driver's UID returned from Core Audio with the UID returned from SoundComponentGetInfo.
  5. If the UIDs match you've found the Sound Output Component. Return it and you're done.

Listing 1 demonstrates one possible implementation supporting a single Sound Output Component.


#define kMaxStringSize 1024

// CopyMyAudioDriverUID
//      Find your drivers UID and return it in
// a CFString you are responsible for releasing.
OSStatus CopyMyAudioDriverUID(CFStringRef *outUID)
{
    UInt32          theSize;
    char            theString[kMaxStringSize];
    UInt32          theNumberDevices;
    AudioDeviceID   *theDeviceList = NULL;
    UInt32          theDeviceIndex;
    CFStringRef     theCFString = NULL;
    OSStatus        theStatus = noErr;

    // this is our driver
    const char      *nameString = "BrownSound audio controller";
    const char      *manufacturerString = "Eddie";

    *outUID = NULL;

    // device list size
    theSize = 0;
    theStatus = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &theSize, NULL);
    if (theStatus) goto done;

    theNumberDevices = theSize / sizeof(AudioDeviceID);

    // allocate the device list
    theDeviceList = (AudioDeviceID*)malloc(theNumberDevices * sizeof(AudioDeviceID));

    // get the device list
    theSize = theNumberDevices * sizeof(AudioDeviceID);
    theStatus = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &theSize, theDeviceList);

    // iterate through the device list, find our device and return the UID
    for(theDeviceIndex = 0; theDeviceIndex < theNumberDevices; ++theDeviceIndex)
    {
        // get name
        theSize = kMaxStringSize;
        theStatus = AudioDeviceGetProperty(theDeviceList[theDeviceIndex],
                                      0, 0, kAudioDevicePropertyDeviceName, &theSize, theString);
        if (theStatus) goto done;

        // is it me?
        if (strncmp(theString, nameString, strlen(nameString)) == 0) {

            // get manufacturer
            theSize = kMaxStringSize;
            theStatus = AudioDeviceGetProperty(theDeviceList[theDeviceIndex], 0, 0,
                                     kAudioDevicePropertyDeviceManufacturer, &theSize, theString);
            if (theStatus) goto done;

            // is it really me?
            if (strncmp(theString, manufacturerString, strlen(manufacturerString)) == 0) {
                // get device UID
                theSize = sizeof(CFStringRef);
                theStatus = AudioDeviceGetProperty(theDeviceList[theDeviceIndex],
                                 0, 0, kAudioDevicePropertyDeviceUID, &theSize, &theCFString);
                if (theStatus) goto done;
                *outUID = theCFString;
                break;
            }
        }
    }

    // we didn't find ourselves, that's bad!
    if (NULL == *outUID) theStatus = badComponentType;

done:
    // free the device list
    if (theDeviceList) free(theDeviceList);

    return theStatus;
}

// QTVideoOutputGetIndSoundOutput
//      Return the sound output component associated with the video output component
// specified by the index parameter. The index of the first component should be 1.
pascal ComponentResult MyVOut_GetIndSoundOutput(MyVOutGlobalsPtr storage,
                                                 long index, Component *outputComponent)
{
    Component            c = 0;
    ComponentDescription cd = { kSoundOutputDeviceType, kHALCustomComponentSubType, 0, 0, 0 };
    CFStringRef          myUID = NULL;
    CFStringRef          halUID = NULL;
    SMStatus             ignore;
    ComponentResult      err;

    *outputComponent = NULL;

    // we only have one Sound Output Component so,
    // if index is anything other than 1 it's an error
    if (index != 1) return paramErr;

    // already have it, we're done
    if (storage->soundOutput) {
        *outputComponent = storage->soundOutput;
        return noErr;
    }

    // find our audio drivers UID using core audio
    err = CopyMyAudioDriverUID(&myUID);
    if (err) goto bail;

    // cheaply init the Sound Manager - must be done so the Sound Manager will
    // synthesize Sound Output Components for each Core Audio Driver on the system
    SndManagerStatus(sizeof(SMStatus), &ignore);

    // find our synthesized Sound Output Component
    while (c = FindNextComponent(c, &cd)) {
        // don't release the returned CFString
        err = SoundComponentGetInfo(c, 0, siHALAudioDeviceUniqueID, &halUID);
        if (err) goto bail;

        // if it's us, we're done
        if (CFEqual(myUID, halUID)) {
            *outputComponent = c;
            storage->soundOutput = c;
            break;
        }
    }

    // we didn't find the corresponding sDev, that's bad!
    if (NULL == *outputComponent) err = badComponentType;

bail:
    if (myUID) CFRelease(myUID);

    return err;
}                

Listing 1.


Note: You must first initialize the Carbon Sound Manager so it can register Sound Output Components for each Core Audio driver on the system. An inexpensive way to perform this initialization is to call SndManagerStatus before any calls to SoundComponentGetInfo.


[Oct 29 2002]