1

I have taken a code from the net to capture a frame from a video file and modified to capture all frames and store it as bmp images.

HRESULT GrabVideoBitmap(PCWSTR pszVideoFile)
{
    IGraphBuilder *pGraph = NULL;
    IMediaControl *pControl = NULL;
    IMediaEventEx *pEvent = NULL;
    IBaseFilter *pGrabberF = NULL;
    ISampleGrabber *pGrabber = NULL;
    IBaseFilter *pSourceF = NULL;
    IEnumPins *pEnum = NULL;
    IPin *pPin = NULL;
    IBaseFilter *pNullF = NULL;

    long evCode;

    wchar_t temp[10];
    wchar_t framename[50] = IMAGE_FILE_PATH;    // L"D:\\sampleframe";

    BYTE *pBuffer = NULL;
    HRESULT hr = CoInitialize(NULL);
    if (FAILED(hr))
        return 0;

    hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, 
                          IID_PPV_ARGS(&pGraph));

    hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));

    hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));

    // Create the Sample Grabber filter.
    hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, 
                          IID_PPV_ARGS(&pGrabberF));

    hr = pGraph->AddFilter(pGrabberF, L"Sample Grabber");

    hr = pGrabberF->QueryInterface(IID_PPV_ARGS(&pGrabber));


    // Displays the metadata of the file
    DisplayFileInfo((wchar_t*)pszVideoFile); // to display video information

    AM_MEDIA_TYPE mt;
    ZeroMemory(&mt, sizeof(mt));
    mt.majortype = MEDIATYPE_Video;
    mt.subtype = MEDIASUBTYPE_RGB24;

    hr = pGrabber->SetMediaType(&mt);

    hr = pGraph->AddSourceFilter(pszVideoFile, L"Source", &pSourceF);

    hr = pSourceF->EnumPins(&pEnum);

    while (S_OK == pEnum->Next(1, &pPin, NULL))
    {
        hr = ConnectFilters(pGraph, pPin, pGrabberF);
        SafeRelease(&pPin);
        if (SUCCEEDED(hr))
        {
            break;
        }
    }

    hr = CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC_SERVER,  
                          IID_PPV_ARGS(&pNullF));

    hr = pGraph->AddFilter(pNullF, L"Null Filter");

    hr = ConnectFilters(pGraph, pGrabberF, pNullF);

    hr = pGrabber->SetOneShot(TRUE);

    hr = pGrabber->SetBufferSamples(TRUE);

    hr = pControl->Run();


    hr = pEvent->WaitForCompletion(INFINITE, &evCode);

    for (int i = 0; i < 10; i++)
    {
        // Find the required buffer size.
        long cbBuffer;
        hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);

        pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);

        hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);

        hr = pGrabber->GetConnectedMediaType(&mt);

        // Examine the format block.
        if ((mt.formattype == FORMAT_VideoInfo) &&
            (mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&
            (mt.pbFormat != NULL))
        {

            swprintf(temp, 5, L"%d", i);
            wcscat_s(framename, temp);
            wcscat_s(framename, L".bmp");

            VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;
            hr = WriteBitmap((PCWSTR)framename, &pVih->bmiHeader,  
                  mt.cbFormat - SIZE_PREHEADER, pBuffer, cbBuffer);
            wcscpy_s(framename, IMAGE_FILE_PATH);
        }
        else
        {
            // Invalid format.
            hr = VFW_E_INVALIDMEDIATYPE;
        }

        FreeMediaType(mt);
    }
done:
    CoTaskMemFree(pBuffer);
    SafeRelease(&pPin);
    SafeRelease(&pEnum);
    SafeRelease(&pNullF);
    SafeRelease(&pSourceF);
    SafeRelease(&pGrabber);
    SafeRelease(&pGrabberF);
    SafeRelease(&pControl);
    SafeRelease(&pEvent);
    SafeRelease(&pGraph);
    return hr;
}

The input video file has 132 frames. But only 68 images are generated. Also last frame of the video is captured for the last 38 images.

I think the directshow graph is running continuously and WriteBitmap() is missing frames.

How to get the control in directX to capture one frame and write it to bmp file and capture the next frame and thus capture all the frames as bmp images.

Thanks Arun

Roman R.
  • 64,446
  • 5
  • 83
  • 139
Arun AC
  • 379
  • 5
  • 14

1 Answers1

2

Your approach is wrong. Currently, you set the sample grabber to one shot and after that you wait for the graph completion. This way it only works for capturing a single frame. You need to capture the frames inside the ISampleGrabberCB callback of your pGrabber. You need to implement ISampleGrabberCB interface and use ISampleGrabber::SetCallback on your pGrabber filter to point it to your implementation. After that you can capture the frames inside either SampleCB or BufferCB methods. http://www.infognition.com/blog/2013/accessing_raw_video_in_directshow.html

VuVirt
  • 1,778
  • 9
  • 12