Getting format capabilities through IWMDMDevice3

IWMDMDevice3::GetFormatCapability is the preferred method for asking a device what formats it supports. The following steps show how to use this method to query a device for its format capabilities:

  1. The application must determine which formats a device supports and which are of interest. To do this, the application can request a list of formats supported by the device by calling IWMDMDevice3::GetProperty.
  2. The application loops through all of the supported formats and requests a device's format capabilities for a specific format (such as WMA or WMV) by calling IWMDMDevice3::GetFormatCapability and specifying a format using the WMDM_FORMATCODE enumeration. This method retrieves a WMDM_FORMAT_CAPABILITY structure.
  3. Loop through all the WMDM_PROP_CONFIG structures in the retrieved WMDM_FORMAT_CAPABILITY structure. Each WMDM_PROP_CONFIG structure holds a group of properties with supported values, representing one configuration for that format. Each configuration has a preference number, where a lower number indicates a greater preference by the device.
  4. Loop through all the WMDM_PROP_DESC structures in the retrieved WMDM_PROP_CONFIG. Each WMDM_PROP_DESC contains a list of supported property/value pairs.
  5. Retrieve the property names and values from the WMDM_PROP_DESC structure. Properties include bit rate, codec, and frame size. Property names are defined in the mswmdm.h header file; a list of most of these constants is given in Metadata Constants. Three types of property values are possible:
    • A single value, WMDM_ENUM_PROP_VALID_VALUES_ANY, indicating support for any values for this property.
    • A range of values, defined by a maximum value, minimum value, and interval.
    • A list of discrete values.
  6. Clear the stored values. Memory for these values is allocated by Windows Media Device Manager; your device is responsible for freeing the memory. How to do this is described at the end of this topic.

When responding to GetFormatCapability, a device can report WMDM_ENUM_PROP_VALID_VALUES_ANY for WMDM_FORMAT_CAPABILITY.WMDM_PROP_CONFIG.WMDM_PROP_DESC.ValidValuesForm to claim support for any values for bit rate, channels, and so on. However, you should treat this claim with caution, because devices can sometimes report support for any values when in fact they do not support all bit rates or image sizes. You might consider having your application transcode extremely large or high-bit-rate files to smaller versions or less memory-intensive and CPU-intensive versions when sending them to devices that are intended to play these files.

The following C++ function shows how to request device format support for a specific format.

HRESULT GetFormatCaps(WMDM_FORMATCODE formatCode, IWMDMDevice3* pDevice)
{
    HRESULT hr = S_OK;

    // Get a list of supported configurations for the format.
    WMDM_FORMAT_CAPABILITY formatCapList;
    hr = pDevice->GetFormatCapability(formatCode, &formatCapList);
    if (FAILED(hr)) return E_FAIL;

    // TODO: Display the format name.
    // Loop through the configurations and examine each one.
    for (UINT iConfig = 0; iConfig < formatCapList.nPropConfig; iConfig++)
    {
        WMDM_PROP_CONFIG formatConfig = formatCapList.pConfigs[iConfig];

        // Preference level for this configuration (lower number means more preferred).
        // TODO: Display the preference level for this format configuration.

        // Loop through all properties for this configuration and get supported
        // values for the property. Values can be a single value, a range, 
        // or a list of enumerated values.
        for (UINT iDesc = 0; iDesc < formatConfig.nPropDesc; iDesc++)
        {
            WMDM_PROP_DESC propDesc = formatConfig.pPropDesc[iDesc];
            // TODO: Display the property name.

            // Three ways a value can be represented: any, a range, or a list.
            switch (propDesc.ValidValuesForm)
            {
                case WMDM_ENUM_PROP_VALID_VALUES_ANY:
                    // TODO: Display a message indicating that all values are valid.
                    break;
                case WMDM_ENUM_PROP_VALID_VALUES_RANGE:
                    {
                        // List these in the docs as the propvariants set.
                        WMDM_PROP_VALUES_RANGE rng = 
                            propDesc.ValidValues.ValidValuesRange;
                        // TODO: Display the min, max, and step values.
                    }
                    break;
                case WMDM_ENUM_PROP_VALID_VALUES_ENUM:
                    {
                        // TODO: Display a banner for the list of valid values.
                        WMDM_PROP_VALUES_ENUM list = propDesc.ValidValues.EnumeratedValidValues;
                        PROPVARIANT pVal;
                        for (UINT iValue = 0; iValue < list.cEnumValues; iValue++)
                        {
                            pVal = list.pValues[iValue];
                            // TODO: Display each valid value.
                            PropVariantClear(&pVal);
                            PropVariantInit(&pVal);
                        }
                    }

                    break;
                default:
                    return E_FAIL,
                    break;
            }
        }
    }
    // Now clear the memory used by WMDM_FORMAT_CAPABILITY.
    FreeFormatCapability(formatCapList);
    return hr;
}

Clearing allocated memory

After retrieving format capabilities from a device, the application must free the memory allocated to hold the description. GetFormatSupport and GetFormatSupport2 have arrays of simple structures that can be cleared by simply calling CoTaskMemFree with the array. However, GetFormatCapability has a more complex data structure with dynamically allocated memory that must be cleared by looping through all the elements and freeing them individually.

The following C++ code shows how an application can free the memory allocated for a WMDM_FORMAT_CAPABILITY structure.

void CWMDMController::FreeFormatCapability(WMDM_FORMAT_CAPABILITY formatCap)
{
    // Loop through all configurations.
    for (UINT i = 0; i < formatCap.nPropConfig; i++) 
    {
        // Loop through all descriptions of a configuration and delete
        // the values particular to that description type.
        for (UINT j=0; j < formatCap.pConfigs[i].nPropDesc; j++) 
        {
            switch (formatCap.pConfigs[i].pPropDesc[j].ValidValuesForm)
            {
                case WMDM_ENUM_PROP_VALID_VALUES_ENUM:
                    for (UINT k=0; k < formatCap.pConfigs[i].pPropDesc[j].ValidValues.EnumeratedValidValues.cEnumValues; k++)
                    {
                        PropVariantClear (&(formatCap.pConfigs[i].pPropDesc[j].ValidValues.EnumeratedValidValues.pValues[k]));
                    }
                    CoTaskMemFree(formatCap.pConfigs[i].pPropDesc[j].ValidValues.EnumeratedValidValues.pValues);
                    break;
                case WMDM_ENUM_PROP_VALID_VALUES_RANGE:
                    PropVariantClear (&(formatCap.pConfigs[i].pPropDesc[j].ValidValues.ValidValuesRange.rangeMin));
                    PropVariantClear (&(formatCap.pConfigs[i].pPropDesc[j].ValidValues.ValidValuesRange.rangeMax));
                    PropVariantClear (&(formatCap.pConfigs[i].pPropDesc[j].ValidValues.ValidValuesRange.rangeStep));
                    break;
                case WMDM_ENUM_PROP_VALID_VALUES_ANY:
                    // No dynamically allocated memory for this value.
                default:
                    break;
            }

            // Free the memory for the description name.
            CoTaskMemFree(formatCap.pConfigs[i].pPropDesc[j].pwszPropName);
        }
        // Free the memory holding the array of description items for this configuration.
        CoTaskMemFree(formatCap.pConfigs[i].pPropDesc);
    }

    // Free the memory pointing to the array of configurations.
    CoTaskMemFree(formatCap.pConfigs);
    formatCap.nPropConfig = 0;
}

Querying for all supported formats

Typically, an application queries a device for a specific format, because it is interested in sending a specific file to the device. However, if you want to query an application for all its supported formats, you can call IWMDMDevice3::GetProperty and pass in g_wszWMDMFormatsSupported to retrieve a full list.

If a device only returns one element, WMDM_FORMATCODE_UNDEFINED, this usually means that the device does not support format codes. Calling GetFormatCapability with WMDM_FORMATCODE_UNDEFINED might retrieve capabilities, but these properties might be fairly generic (such as name, file size, last modified date, and so on).

The following steps show how to query for a list of all supported formats:

  1. Request a list of all format codes supported by calling IWMDMDevice3::GetProperty and passing in g_wszWMDMFormatsSupported. This returns a PROPVARIANT containing a SAFEARRAY of supported formats.
  2. Loop through the elements by calling SafeArrayGetElement. Each element is a WMDM_FORMATCODE enumeration.
  3. Request the capabilities for each format, freeing the memory for each WMDM_FORMAT_CAPABILITY element once done with it.
  4. Clear the PROPVARIANT retrieved in step 1 by calling PropVariantClear.

The following C++ example code retrieves a list of supported formats for a device.

// Query a device for supported configurations for each media or format type. 
HRESULT CWMDMController::GetCaps(IWMDMDevice3* pDevice)
{
    HRESULT hr = S_OK;

    // Request the "formats supported" property to get a list of supported formats.
    PROPVARIANT pvFormatsSupported;
    PropVariantInit(&pvFormatsSupported);
    hr = pDevice->GetProperty(g_wszWMDMFormatsSupported, &pvFormatsSupported);
    HANDLE_HR(hr, "Got a property list in GetCaps", "Couldn't get a property list in GetCaps.");

    // Loop through the retrieved format list.
    // For each format, get a list of format configurations.
    SAFEARRAY* formatList = pvFormatsSupported.parray;
    WMDM_FORMATCODE formatCode = WMDM_FORMATCODE_NOTUSED;
    for (LONG iCap = 0; iCap < formatList->rgsabound[0].cElements; iCap++)
    { 
        // Get a format from the SAFEARRAY of retrieved formats.
        SafeArrayGetElement(formatList, &iCap, &formatCode);

        // Call a custom function to request the format capabilities.
        if (formatCode != WMDM_FORMATCODE_NOTUSED)
            myGetFormatCaps(formatCode, pDevice);
    }

e_Exit:
    // Clear out the memory we used.
    PropVariantClear(&pvFormatsSupported);
    return hr;
}

Discovering Device Format Capabilities