c++ - 在 C++ 中使用 directshow 过滤器从视频中捕获帧

标签 c++ directx directshow video-capture samplegrabber

我从网上获取了一段代码,用于从视频文件中捕获一个帧,并进行了修改以捕获所有帧并将其存储为 bmp 图像。

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;
}

输入视频文件有 132 帧。 但是只生成了 68 张图像。 还会为最后 38 张图像捕获视频的最后一帧。

我认为 directshow 图在连续运行,WriteBitmap() 丢失帧。

如何让directX中的控件捕获一帧并写入bmp文件并捕获下一帧从而将所有帧捕获为bmp图像。

谢谢 阿伦

最佳答案

你的方法是错误的。目前,您将样本采集器设置为一次拍摄,然后等待图形完成。这样它只适用于捕获单个帧。您需要在 pGrabber 的 ISampleGrabberCB 回调中捕获帧。您需要实现 ISampleGrabberCB 接口(interface)并在您的 pGrabber 过滤器上使用 ISampleGrabber::SetCallback 将其指向您的实现。之后,您可以在 SampleCB 或 BufferCB 方法中捕获帧。 http://www.infognition.com/blog/2013/accessing_raw_video_in_directshow.html

关于c++ - 在 C++ 中使用 directshow 过滤器从视频中捕获帧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40589405/

相关文章:

algorithm - 三角扇的纹理坐标

c# - DirectShow/WPF 线程问题

c++ - 为什么 std::sin() 在 CUDA 内核中工作?

c++ - dynamic_cast 真的适用于多重继承吗?

c# - 如何用C#单独控制立体画幅? (NVIDIA 3D 快门眼镜)

DirectShow Source 过滤器使用 Dekstop 窗口作为源

delphi - 如何使用 DSPACK 将自定义音频缓冲区注入(inject) DirectX 过滤器图表?

c++ - 来自重载 << 运算符的奇怪链接器错误 - C++

c++ - 在 OpenGL/GLFW 3.2 中在窗口和全屏之间切换

c# - 视频编辑 API 建议?