Search code examples
winapivisual-c++videodirectshow

My DirectShow failed on dynamic format changes


Recently I'm working with VC and DirectShow, the problem is that the dynamic format changes did not work out to me.

I customized a CBaseFilter as my SouceFilter. It get, fill and deliver a MediaSample(Sample for short) to downstream filter which is a VMR precisely.The SourceFilter get the Sample through CBaseOutputPin::GetDeliveryBuffer, and deliver the Sample through CBaseOutputPin::Deliver.

For reasons, the resolution of frames may change any time. So I attach a new MediaType with width and height changed in its pbFormat bmiHeader every time the resolution changed.

According ot MSDN Dynamic Format Changes, I wrote codes as below:

// YUV420 frame got
// now try to get Sample buffer
if(pvi.bmiHeader.biWidth != width || pvi.bmiHeader.biHeight != height){ 
    // if resolution changed
    // generate new MediaType with new resolution
    SetMediaType(width, height);
    if(S_OK == pRenderInPin->QueryAccept(&mediaType)){
        // if the input Pin of render accept the new MediaType
        if(pSourceFilter){
            /*
             * In function GetSampleBufferOfMediaType, 
             * I ask for a Sample, attach a MediaType to it,
             * and get its buffer for future use
             */
            while(S_OK != pSourceFilter->GetSampleBufferOfMediaType(&mediaType, &pSampleBuffer)
                    && isActive){
                Sleep(8);
            }
        }
    }else{
        DEBUG_INFO("ERROR - failed on QueryAccept\n");
    }
}else{
    // in function GetSampleBuffer, just ask for a Sample buffer
    while (S_OK != pSourceFilter->GetSampleBuffer(&pSampleBuffer)
            && isActive){
        Sleep(8);
    }
}
// fill buffer and deliver it

I considered that once RenderFilter get the Sample with new MediaType attached, it will call IMediaSample::GetMediaType, and adopt the new MediaType. But I found onece resolution changed, the GetSampleBufferOfMediaType will execute, unfortunately the pictures did not properly show on the screen.

I wonder why it does not work as I expected, and how should I modify my program.

Any hint will be highly appreciate.

-----[Extra Information]-----

In condition which has no format changes, my MediaType works with 720P and 1080P well, but it is not lucky with CIF(352*288). Maybe here's something wrong with my function SetMediaType()?

void CReadBufController::SetMediaType(short _width, short _height){
    mMediaType.subtype = MEDIASUBTYPE_YV12;
    mMediaType.majortype = MEDIATYPE_Video;
    mMediaType.formattype = FORMAT_VideoInfo;
    mMediaType.bFixedSizeSamples = FALSE;
    mMediaType.bTemporalCompression = TRUE;
    mMediaType.lSampleSize = 0;
    mMediaType.pUnk = 0;
    mMediaType.cbFormat = sizeof(VIDEOINFOHEADER);

    pvi.bmiHeader.biBitCount = 12;
    pvi.bmiHeader.biWidth = _width;
    pvi.bmiHeader.biHeight  = _height;
    pvi.bmiHeader.biSizeImage = GetBitmapSize(&pvi.bmiHeader);
    pvi.bmiHeader.biPlanes = 1;
    pvi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pvi.bmiHeader.biCompression = MAKEFOURCC('Y', 'V', '1', '2');

    mMediaType.pbFormat = (BYTE *)(&pvi);
}

-----[UPDATE]-----

According to comment of @Roman_R, here's my code of GetSampleBufferOfMediaType:

// here CReadBufferFilter is the SourceFilter I mentioned
HRESULT CReadBufferFilter::GetSampleBufferOfMediaType(AM_MEDIA_TYPE *_pMediaType, PBYTE *_pBuffer){
    SAFE_RELEASE(mSample);
    // get a Sample from output Pin
    mOutputPin->GetDeliveryBuffer(&mSample, NULL, NULL, 0);
    // attach the new MediaType to the Sample
    if(mSample && (S_OK == mSample->SetMediaType(_pMediaType) )){
        mSample->GetPointer(_pBuffer); // get the Sample buffer
        return S_OK;
    }
    return E_FAIL;
}

'the pictures did not properly show on the screen' means its gray and overlapped with its multiple image copy, just act like when you assigned improper width and height to the MediaType it will.


Solution

  • Your MSDN section is QueryAccept (Downstream). This type of format change only works when you are switching from higher resolution to lower, that is when the connection does not need to re-agree memory allocator and the buffers which are already allocated are suitable for new updated resolution.

    You should see an error or rejection code returned by video renderer in one of the calls: IPin::QueryAccept, IMediaSample::SetMediaType or IMemInputPin::Receive.

    Then to add to this, even if you change resolution, video renderer might want to additionally change stride to fit hardware requirements. This is specifically the case of smaller resolutions because video renderer/hardware wants the stride to be aligned to 16 bytes or more. Your request to set 352x288 resolution is likely to immediately cause Handling Format Changes from the Video Renderer change to agree the same resolution but with increased stride. Not handling this scenario is likely to result in image visually seen as picture with shifted lines.