## Exposing Capture and Compression Formats

This article describes how to return capture and compression formats by using the IAMStreamConfig::GetStreamCaps method. This method can get more information about accepted media types than the traditional way of enumerating a pin's media types, so it should typically be used instead. See Establishing Media Type Connections for information about traditional media type enumeration. IAMStreamConfig::GetStreamCaps can return information about the kinds of formats allowed for audio or video. Additionally, this article provides some sample code that demonstrates how to reconnect the input pin of a transform filter to ensure your filter can produce a particular output.

The IAMStreamConfig::GetStreamCaps method returns an array of pairs of media type and capabilities structures. The media type is an AM_MEDIA_TYPE structure and the capabilities are represented either by an AUDIO_STREAM_CONFIG_CAPS structure or a VIDEO_STREAM_CONFIG_CAPS structure. The first section in this article presents a video example and the second presents an audio example.

Contents of this article:

- Video Capabilities
- Audio Capabilities
- Reconnecting Your Input to Ensure Specific Output Types

Video Capabilities

The IAMStreamConfig::GetStreamCaps method presents video capabilities in an array of pairs of AM_MEDIA_TYPE and VIDEO_STREAM_CONFIG_CAPS structures. You can use this to expose all the formats and resolutions supported on a pin as discussed below.

See Audio Capabilities for audio-related examples of IAMStreamConfig::GetStreamCaps.

Suppose your capture card supports JPEG format at all resolutions between 160 × 120 pixels and 320 × 240 pixels, inclusive. The difference between supported resolutions is one in this case because you add or subtract one pixel from each supported resolution to get the next supported resolution. This difference in supported resolutions is called granularity.

Suppose you card also supports the size 640 × 480. The following illustrates this single resolution and the above range of resolutions (all sizes between 160 × 120 pixels and 320 × 240 pixels).

Also, suppose it supports 24-bit color RGB format at resolutions between 160 × 120 and 320 × 240, but with a granularity of 8. The following illustration shows some of the valid sizes in this case.

To put it another way, and listing more resolutions, the following are all among the list of valid resolutions.

- 160 × 120
- 168 × 120
- 168 × 128
- 176 × 128
- 176 × 136
- ... additional resolutions ...
- 312 × 232
- 320 × 240

Use GetStreamCaps to expose these color format and dimension capabilities by offering a media type of 320 × 240 JPEG (if that is your default or preferred size) coupled with minimum capabilities of 160 × 120, maximum capabilities of 320 × 240, and a granularity of 1. The next pair you expose by using GetStreamCaps is a media type of 640 × 480 JPEG coupled with a minimum of 640 × 480 and a maximum of 640 × 480 and a granularity of 0. The third pair includes a media type of 320 × 240, 24-bit RGB with minimum capabilities of 160 × 120, maximum capabilities of 320 × 240, and a granularity of 8. In this way you can publish almost every format and capability your card might support. An application that must know what compression formats you provide can get all the pairs and make a list of all the unique subtypes of the media types.

A filter obtains its media type source and target rectangles from the VIDEOINFOHEADER structure's rcSource and rcTarget members, respectively. Filters do not have to support source and target rectangles.

The cropping rectangle described throughout the IAMStreamConfig documentation is the same as the VIDEOINFOHEADER structure's rcSource rectangle for the output pin.

The output rectangle described throughout the IAMStreamConfig documentation is the same as the biWidth and biHeight members of the output pin's BITMAPINFOHEADER structure.

If a filter's output pin is connected to a media type with nonempty source and target rectangles, then your filter is required to stretch the input format's source subrectangle into the output format's target subrectangle. The source subrectangle is stored in the VIDEO_STREAM_CONFIG_CAPS structure's InputSize member.

For example, consider the following video compressor scenario: The input image is in RGB format and has a size of 160 × 120 pixels. The source rectangle's upper-left corner is at coordinate (20,20), and its lower-right corner is at (30,30). The output image is in MPEG format with a size of 320 × 240. The target rectangle's upper-left corner is at (0,0) and its lower-right corner is at (100,100). In this case, the filter should take a 10 × 10 piece of the 160 × 120 RGB source bitmap, and make it fill the top 100 × 100 area of a 320 × 240 bitmap, leaving the rest of the 320 × 240 bitmap untouched. The following illustration shows this scenario.

A filter might not support this and can fail to connect with a media type where rcSource and rcTarget are not empty.

The VIDEOINFOHEADER structure exposes information about a filter's data rate capabilities. For example, suppose you connected your output pin to the next filter with a certain media type (either directly or by using the media type passed by the CMediaType::SetFormat function). Look at the dwBitRate member of that media type's VIDEOINFOHEADER format structure to see what data rate you should compress the video to. If you multiply the number of units of time per frame in the VIDEOINFOHEADER structure's AvgTimePerFrame member by the data rate in the dwBitRate member and divide by 10,000,000 (the number of units per second), you can figure out how many bytes each frame should be. You can produce a smaller sized frame, but never a larger one. To determine the frame rate for a video compressor or for a capture filter, use AvgTimePerFrame from your output pin's media type.

Audio Capabilities

For audio capabilities, IAMStreamConfig::GetStreamCaps returns an array of pairs of AM_MEDIA_TYPE and AUDIO_STREAM_CONFIG_CAPS structures. As with video, you can use this to expose all kinds of audio capabilities on the pin, such as data rate and whether it supports mono or stereo.

See Video Capabilities for video-related examples relating to IAMStreamConfig::GetStreamCaps.

Suppose you support pulse code modulation (PCM) wave format (as represented by the Microsoft® Win32® PCMWAVEFORMAT structure) at sampling rates of 11,025, 22,050, and 44,100 samples per second, all at 8- or 16-bit mono or stereo. In this case, you would offer two pairs of structures. The first pair would have an AUDIO_STREAM_CONFIG_CAPS capability structure saying you support a minimum of 11,025 to a maximum of 22,050 samples per second with a granularity of 11,025 samples per second (granularity is the difference between supported values); an 8-bit minimum to a 16-bit maximum bits per sample with a granularity of 8 bits per sample; and one-channel minimum and two-channel maximum. The first pair's media type would be your default PCM format in that range, perhaps 22 kilohertz (kHz), 16-bit stereo. Your second pair would be a capability showing 44,100 for both minimum and maximum samples per second; 8-bit (minimum) and 16-bit (maximum) bits per sample, with a granularity of 8 bits per sample; and one-channel minimum and two-channel maximum. The media type would be your default 44 kHz format, perhaps 44 kHz 16-bit stereo.

If you support non-PCM wave formats, the media type returned by this method can show which non-PCM formats you support (with a default sample rate, bit rate, and channels) and the capabilities structure accompanying that media type can describe which other sample rates, bit rates, and channels you support.

Reconnecting Your Input to Ensure Specific Output Types

Filters implement the IAMStreamConfig::SetFormat method to set the audio or video stream's format before pins are connected. Additionally, if your output pin is already connected and you can provide a new type, then reconnect your pin. If the other pin your filter is connected to can't accept the media type, fail this call and leave your connection alone.

Transform filters that do not know what output types their pins can provide should refuse any calls to IAMStreamConfig::SetFormat and IAMStreamConfig::GetStreamCaps with the error code VFW_E_NOT_CONNECTED until their input pin is connected.

If your pin knows what types it can provide even when your input is not connected, it is okay to offer and accept them as usual. See Connecting Transform Filters for more information.

In certain cases it is useful to reconnect pins when you are offering a format on an established connection. For example, if you can compress video into format X but only if you get 24-bit RGB input, and you can turn 8-bit RGB input into compressed format Y, you can either:

1. Offer and accept both X and Y in IAMStreamConfig::GetStreamCaps and IAMStreamConfig::SetFormat all the time, or,
2. Only offer format X if your input is connected as 24, and only offer Y if your input is connected as 8. Fail both IAMStreamConfig::GetStreamCaps and IAMStreamConfig::SetFormat if your input is not connected.

No matter which one you choose, you will need some reconnecting code that looks like this:

    // Overridden to do fancy reconnecting footwork.
    //
    HRESULT MyOutputPin::CheckMediaType(const CMediaType *pmtOut)
    {
        HRESULT hr;
        CMediaType *pmtEnum;
        BOOL fFound = FALSE;
        IEnumMediaTypes *pEnum;

        if (!m_pFilter->m_pInput->IsConnected()) {
           	return VFW_E_NOT_CONNECTED;
        }

        // Quickly verify that the media type is not bogus here
        //

        // If somebody has previously called SetFormat, fail this call if the media type
        // isn't an exact match.

           // Accept this output type like normal; nothing fancy required.
        hr = m_pFilter->CheckTransform(&m_pFilter->m_pInput->CurrentMediaType(),
    				    pmtOut);
        if (hr == NOERROR)
    	return hr;

        DbgLog((LOG_TRACE,3,TEXT("Can't accept this output media type")));
        DbgLog((LOG_TRACE,3,TEXT(" But how about reconnecting our input...")));

        // Attempt to find an acceptable type by reconnecting our input pin.
        // The pin our input pin connects to might be able to provide a type
        // that our pin can convert into the necessary type.
        hr = m_pFilter->m_pInput->GetConnected()->EnumMediaTypes(&pEnum);
        if (hr != NOERROR)
    	return E_FAIL;
        while (1) {
    	hr = pEnum->Next(1, (AM_MEDIA_TYPE **)&pmtEnum, &j);

    	// All out of enumerated types.
    	if (hr == S_FALSE || j == 0) {
    	    break;
    	}

    	// Can our pin convert between these?
    	hr = m_pFilter->CheckTransform(pmtEnum, pmtOut);

    	if (hr != NOERROR) {
    	    DeleteMediaType(pmtEnum);
    	    continue;
    	}

    	// OK, it offers an acceptable type, but will it accept it now?
    	hr = m_pFilter->m_pInput->GetConnected()->QueryAccept(pmtEnum);
    	// Nope.
    	if (hr != NOERROR) {
    	    DeleteMediaType(pmtEnum);
    	    continue;
    	}
    	// OK, I'm satisfied.
    	fFound = TRUE;
    	DbgLog((LOG_TRACE,2,TEXT("This output type is only acceptable after reconnecting our input.")));

    	// All done with this.
    	DeleteMediaType(pmtEnum);
    	break;
        }
        pEnum->Release();

        if (!fFound)
            DbgLog((LOG_TRACE,3,TEXT("*NO! Reconnecting our input won't help")));

        return fFound ? NOERROR : VFW_E_INVALIDMEDIATYPE;
    }

    HRESULT MyOutputPin::SetFormat(AM_MEDIA_TYPE *pmt)
    {
        HRESULT hr;
        LPWAVEFORMATEX lpwfx;
        DWORD dwSize;

        if (pmt == NULL)
    	return E_POINTER;

        // To make sure streaming isn't in the middle of starting/stopping:
        CAutoLock cObjectLock(&m_pFilter->m_csFilter);

        if (m_pFilter->m_State != State_Stopped)
    	return VFW_E_NOT_STOPPED;

        // Possible output formats depend on the input format.
        if (!m_pFilter->m_pInput->IsConnected())
    	return VFW_E_NOT_CONNECTED;

        // Already using this format.
        if (IsConnected() && CurrentMediaType() == *pmt)
    	return NOERROR;

        // See if this type is acceptable.
        if ((hr = CheckMediaType((CMediaType *)pmt)) != NOERROR) {
    	DbgLog((LOG_TRACE,2,TEXT("IAMStreamConfig::SetFormat rejected")));
    	return hr;
        }

        // If connecting to another filter, make sure they like it.
        if (IsConnected()) {
    	hr = GetConnected()->QueryAccept(pmt);
    	if (hr != NOERROR)
    	    return VFW_E_INVALIDMEDIATYPE;
        }

        // Now make a note that from now on, this is the only format allowed,
        // and refuse anything but this in the CheckMediaType code above.

        // Changing the format means reconnecting if necessary.
        if (IsConnected())
            m_pFilter->m_pGraph->Reconnect(this);

        return NOERROR;
    }

    // Overridden to complete our fancy reconnection footwork:
    //
    HRESULT MyWrapper::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt)
    {
        HRESULT hr;

        // Set the OUTPUT type.
        if (direction == PINDIR_OUTPUT) {

    	// Uh oh.  As part of our fancy reconnection, our input pin might be asked to
    	// provide a media type it cannot provide without reconnection
    	// to a different type.
    	if (m_pInput && m_pInput->IsConnected()) {

    	    // If our pin can actually provide this type now, don't worry.
        	    hr = CheckTransform(&m_pInput->CurrentMediaType(),
    				    &m_pOutput->CurrentMediaType());
    	    if (hr == NOERROR)
    		return hr;

                	DbgLog((LOG_TRACE,2,TEXT("*Set OUTPUT requires RECONNECT of INPUT!")));

    	    // Reconnect our input pin.
    	    return m_pGraph->Reconnect(m_pInput);

    	}

    	return NOERROR;
        }

        return NOERROR;
    }

© 1997 Microsoft Corporation. All rights reserved. Terms of Use.