Exposing Capture and Compression Formats through IAMStreamConfig


This article describes how to return capture and compression formats with 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. It 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 certain output.

The IAMStreamConfig::GetStreamCaps method returns a whole array of MediaType and Capabilities pairs where MediaType is an AM_MEDIA_TYPE structure and Capabilities is either an AUDIO_STREAM_CONFIG_CAPS structure or a VIDEO_STREAM_CONFIG_CAPS structure. The first topic in this article presents a video example and the second presents an audio example.

Contents of this article:

Video Capabilities

IAMStreamConfig::GetStreamCaps 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 kinds of capabilities on the pin.

Suppose your capture card supports JPEG format at resolutions anywhere between 160 × 120 pixels and 320 × 240 pixels, and it also supports the size 640 × 480 as shown in the following illustration.

Illustration of JPEG support at sizes between 160 × 120 pixels and 320 × 240 pixels

Illustration of JPEG support at size 640 × 480

Also, suppose it supports 24-bit color RGB format at resolutions between 160 × 120 and 320 × 240, but only in multiples of 8 as shown in the following illustration.

Illustration of RGB support at sizes between 160 × 120 and 320 × 240 in multiples of 8

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. 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 structure 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 width and height 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 diagram illustrates this scenario.

Illustration of video compressor stretching subrectangle of image between source and target rectangles

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.

Suppose you support pulse code modulation (PCM) wave format (as represented by the 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 you are connected to can't accept the media type, fail this call and leave your connection alone.

In the case of transform filters, your pin should refuse any calls to IAMStreamConfig::SetFormat and IAMStreamConfig::GetStreamCaps with VFW_E_NOT_CONNECTED until your input is connected if it does not know what output types can be provided. If your pin does know what types it can provide even when your input is not connected, it is still ok to go ahead and offer and accept them as usual.

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 we're
    // checking isn't an exact match.
   
       // We can 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("*We can't accept this output media type")));
    DbgLog((LOG_TRACE,3,TEXT(" But how about if we reconnected our input...")));
    
    // Now let's get fancy.  We could accept this type if we reconnected our
    // input pin... in other words if the guy our input pin is connected to
    // could provide a type that we could 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 we convert between these?
	hr = m_pFilter->CheckTransform(pmtEnum, pmtOut);

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

	// OK, it offers the type, and we like it, 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("*We can only accept this output type if we reconnect 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 we're not in the middle of start/stop streaming
    CAutoLock cObjectLock(&m_pFilter->m_csFilter);

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

    // our possible output formats depend on our input format
    if (!m_pFilter->m_pInput->IsConnected())
	return VFW_E_NOT_CONNECTED;

    // We're already using this format
    if (IsConnected() && CurrentMediaType() == *pmt)
	return NOERROR;

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

    // If we are connected to somebody, 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 footwork we may be being asked to
	// provide a media type we cannot provide unless we reconnect our
	// input pin to provide a different type
	if (m_pInput && m_pInput->IsConnected()) {

	    // If we 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!")));

	    // Oh boy. Reconnect our input pin.  Cross your fingers.
	    return m_pGraph->Reconnect(m_pInput);

	}

	return NOERROR;
    }

    return NOERROR;
}

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